Timezone.pm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. package Time::Timezone;
  2. require 5.002;
  3. require Exporter;
  4. @ISA = qw(Exporter);
  5. @EXPORT = qw(tz2zone tz_local_offset tz_offset tz_name);
  6. @EXPORT_OK = qw();
  7. use Carp;
  8. use strict;
  9. # Parts stolen from code by Paul Foley <[email protected]>
  10. use vars qw($VERSION);
  11. $VERSION = 2006.0814;
  12. sub tz2zone
  13. {
  14. my($TZ, $time, $isdst) = @_;
  15. use vars qw(%tzn_cache);
  16. $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : ''
  17. unless $TZ;
  18. # Hack to deal with 'PST8PDT' format of TZ
  19. # Note that this can't deal with all the esoteric forms, but it
  20. # does recognize the most common: [:]STDoff[DST[off][,rule]]
  21. if (! defined $isdst) {
  22. my $j;
  23. $time = time() unless $time;
  24. ($j, $j, $j, $j, $j, $j, $j, $j, $isdst) = localtime($time);
  25. }
  26. if (defined $tzn_cache{$TZ}->[$isdst]) {
  27. return $tzn_cache{$TZ}->[$isdst];
  28. }
  29. if ($TZ =~ /^
  30. ( [^:\d+\-,] {3,} )
  31. ( [+-] ?
  32. \d {1,2}
  33. ( : \d {1,2} ) {0,2}
  34. )
  35. ( [^\d+\-,] {3,} )?
  36. /x
  37. ) {
  38. $TZ = $isdst ? $4 : $1;
  39. $tzn_cache{$TZ} = [ $1, $4 ];
  40. } else {
  41. $tzn_cache{$TZ} = [ $TZ, $TZ ];
  42. }
  43. return $TZ;
  44. }
  45. sub tz_local_offset
  46. {
  47. my ($time) = @_;
  48. $time = time() unless $time;
  49. return &calc_off($time);
  50. }
  51. sub calc_off
  52. {
  53. my ($time) = @_;
  54. my (@l) = localtime($time);
  55. my (@g) = gmtime($time);
  56. my $off;
  57. $off = $l[0] - $g[0]
  58. + ($l[1] - $g[1]) * 60
  59. + ($l[2] - $g[2]) * 3600;
  60. # subscript 7 is yday.
  61. if ($l[7] == $g[7]) {
  62. # done
  63. } elsif ($l[7] == $g[7] + 1) {
  64. $off += 86400;
  65. } elsif ($l[7] == $g[7] - 1) {
  66. $off -= 86400;
  67. } elsif ($l[7] < $g[7]) {
  68. # crossed over a year boundary!
  69. # localtime is beginning of year, gmt is end
  70. # therefore local is ahead
  71. $off += 86400;
  72. } else {
  73. $off -= 86400;
  74. }
  75. return $off;
  76. }
  77. # constants
  78. # The rest of the file originally comes from Graham Barr <[email protected]>
  79. #
  80. # Some references:
  81. # http://www.weltzeituhr.com/laender/zeitzonen_e.shtml
  82. # http://www.worldtimezone.com/wtz-names/timezonenames.html
  83. # http://www.timegenie.com/timezones.php
  84. CONFIG: {
  85. use vars qw(%dstZone %zoneOff %dstZoneOff %Zone);
  86. %dstZone = (
  87. "brst" => -2*3600, # Brazil Summer Time (East Daylight)
  88. "adt" => -3*3600, # Atlantic Daylight
  89. "edt" => -4*3600, # Eastern Daylight
  90. "cdt" => -5*3600, # Central Daylight
  91. "mdt" => -6*3600, # Mountain Daylight
  92. "pdt" => -7*3600, # Pacific Daylight
  93. "ydt" => -8*3600, # Yukon Daylight
  94. "hdt" => -9*3600, # Hawaii Daylight
  95. "bst" => +1*3600, # British Summer
  96. "mest" => +2*3600, # Middle European Summer
  97. "met dst" => +2*3600, # Middle European Summer
  98. "sst" => +2*3600, # Swedish Summer
  99. "fst" => +2*3600, # French Summer
  100. "eest" => +3*3600, # Eastern European Summer
  101. "cest" => +2*3600, # Central European Daylight
  102. "wadt" => +8*3600, # West Australian Daylight
  103. "kdt" => +10*3600, # Korean Daylight
  104. # "cadt" => +10*3600+1800, # Central Australian Daylight
  105. "eadt" => +11*3600, # Eastern Australian Daylight
  106. "nzdt" => +13*3600, # New Zealand Daylight
  107. );
  108. # not included due to ambiguity:
  109. # IST Indian Standard Time +5.5
  110. # Ireland Standard Time 0
  111. # Israel Standard Time +2
  112. # IDT Ireland Daylight Time +1
  113. # Israel Daylight Time +3
  114. # AMST Amazon Standard Time / -3
  115. # Armenia Standard Time +8
  116. # BST Brazil Standard -3
  117. %Zone = (
  118. "gmt" => 0, # Greenwich Mean
  119. "ut" => 0, # Universal (Coordinated)
  120. "utc" => 0,
  121. "wet" => 0, # Western European
  122. "wat" => -1*3600, # West Africa
  123. "azost" => -1*3600, # Azores Standard Time
  124. "cvt" => -1*3600, # Cape Verde Time
  125. "at" => -2*3600, # Azores
  126. "fnt" => -2*3600, # Brazil Time (Extreme East - Fernando Noronha)
  127. "ndt" => -2*3600-1800,# Newfoundland Daylight
  128. "art" => -3*3600, # Argentina Time
  129. # For completeness. BST is also British Summer, and GST is also Guam Standard.
  130. # "gst" => -3*3600, # Greenland Standard
  131. "nft" => -3*3600-1800,# Newfoundland
  132. # "nst" => -3*3600-1800,# Newfoundland Standard
  133. "mnt" => -4*3600, # Brazil Time (West Standard - Manaus)
  134. "ewt" => -4*3600, # U.S. Eastern War Time
  135. "ast" => -4*3600, # Atlantic Standard
  136. "bot" => -4*3600, # Bolivia Time
  137. "vet" => -4*3600, # Venezuela Time
  138. "est" => -5*3600, # Eastern Standard
  139. "cot" => -5*3600, # Colombia Time
  140. "act" => -5*3600, # Brazil Time (Extreme West - Acre)
  141. "pet" => -5*3600, # Peru Time
  142. "cst" => -6*3600, # Central Standard
  143. "cest" => +2*3600, # Central European Summer
  144. "mst" => -7*3600, # Mountain Standard
  145. "pst" => -8*3600, # Pacific Standard
  146. "yst" => -9*3600, # Yukon Standard
  147. "hst" => -10*3600, # Hawaii Standard
  148. "cat" => -10*3600, # Central Alaska
  149. "ahst" => -10*3600, # Alaska-Hawaii Standard
  150. "taht" => -10*3600, # Tahiti Time
  151. "nt" => -11*3600, # Nome
  152. "idlw" => -12*3600, # International Date Line West
  153. "cet" => +1*3600, # Central European
  154. "mez" => +1*3600, # Central European (German)
  155. "met" => +1*3600, # Middle European
  156. "mewt" => +1*3600, # Middle European Winter
  157. "swt" => +1*3600, # Swedish Winter
  158. "set" => +1*3600, # Seychelles
  159. "fwt" => +1*3600, # French Winter
  160. "west" => +1*3600, # Western Europe Summer Time
  161. "eet" => +2*3600, # Eastern Europe, USSR Zone 1
  162. "ukr" => +2*3600, # Ukraine
  163. "sast" => +2*3600, # South Africa Standard Time
  164. "bt" => +3*3600, # Baghdad, USSR Zone 2
  165. "eat" => +3*3600, # East Africa Time
  166. # "it" => +3*3600+1800,# Iran
  167. "irst" => +3*3600+1800,# Iran Standard Time
  168. "zp4" => +4*3600, # USSR Zone 3
  169. "msd" => +4*3600, # Moscow Daylight Time
  170. "sct" => +4*3600, # Seychelles Time
  171. "zp5" => +5*3600, # USSR Zone 4
  172. "azst" => +5*3600, # Azerbaijan Summer Time
  173. "mvt" => +5*3600, # Maldives Time
  174. "uzt" => +5*3600, # Uzbekistan Time
  175. "ist" => +5*3600+1800,# Indian Standard
  176. "zp6" => +6*3600, # USSR Zone 5
  177. "lkt" => +6*3600, # Sri Lanka Time
  178. "pkst" => +6*3600, # Pakistan Summer Time
  179. "yekst" => +6*3600, # Yekaterinburg Summer Time
  180. # For completeness. NST is also Newfoundland Stanard, and SST is also Swedish Summer.
  181. # "nst" => +6*3600+1800,# North Sumatra
  182. # "sst" => +7*3600, # South Sumatra, USSR Zone 6
  183. "wast" => +7*3600, # West Australian Standard
  184. "ict" => +7*3600, # Indochina Time
  185. "wit" => +7*3600, # Western Indonesia Time
  186. # "jt" => +7*3600+1800,# Java (3pm in Cronusland!)
  187. "cct" => +8*3600, # China Coast, USSR Zone 7
  188. "wst" => +8*3600, # West Australian Standard
  189. "hkt" => +8*3600, # Hong Kong
  190. "bnt" => +8*3600, # Brunei Darussalam Time
  191. "cit" => +8*3600, # Central Indonesia Time
  192. "myt" => +8*3600, # Malaysia Time
  193. "pht" => +8*3600, # Philippines Time
  194. "sgt" => +8*3600, # Singapore Time
  195. "jst" => +9*3600, # Japan Standard, USSR Zone 8
  196. "kst" => +9*3600, # Korean Standard
  197. # "cast" => +9*3600+1800,# Central Australian Standard
  198. "east" => +10*3600, # Eastern Australian Standard
  199. "gst" => +10*3600, # Guam Standard, USSR Zone 9
  200. "nct" => +11*3600, # New Caledonia Time
  201. "nzt" => +12*3600, # New Zealand
  202. "nzst" => +12*3600, # New Zealand Standard
  203. "fjt" => +12*3600, # Fiji Time
  204. "idle" => +12*3600, # International Date Line East
  205. );
  206. %zoneOff = reverse(%Zone);
  207. %dstZoneOff = reverse(%dstZone);
  208. # Preferences
  209. $zoneOff{0} = 'gmt';
  210. $dstZoneOff{3600} = 'bst';
  211. }
  212. sub tz_offset
  213. {
  214. my ($zone, $time) = @_;
  215. return &tz_local_offset() unless($zone);
  216. $time = time() unless $time;
  217. my(@l) = localtime($time);
  218. my $dst = $l[8];
  219. $zone = lc $zone;
  220. if ($zone =~ /^([\-\+]\d{3,4})$/) {
  221. my $sign = $1 < 0 ? -1 : 1 ;
  222. my $v = abs(0 + $1);
  223. return $sign * 60 * (int($v / 100) * 60 + ($v % 100));
  224. } elsif (exists $dstZone{$zone} && ($dst || !exists $Zone{$zone})) {
  225. return $dstZone{$zone};
  226. } elsif(exists $Zone{$zone}) {
  227. return $Zone{$zone};
  228. }
  229. undef;
  230. }
  231. sub tz_name
  232. {
  233. my ($off, $time) = @_;
  234. $time = time() unless $time;
  235. my(@l) = localtime($time);
  236. my $dst = $l[8];
  237. if (exists $dstZoneOff{$off} && ($dst || !exists $zoneOff{$off})) {
  238. return $dstZoneOff{$off};
  239. } elsif (exists $zoneOff{$off}) {
  240. return $zoneOff{$off};
  241. }
  242. sprintf("%+05d", int($off / 60) * 100 + $off % 60);
  243. }
  244. 1;
  245. __END__
  246. =head1 NAME
  247. Time::Timezone -- miscellaneous timezone manipulations routines
  248. =head1 SYNOPSIS
  249. use Time::Timezone;
  250. print tz2zone();
  251. print tz2zone($ENV{'TZ'});
  252. print tz2zone($ENV{'TZ'}, time());
  253. print tz2zone($ENV{'TZ'}, undef, $isdst);
  254. $offset = tz_local_offset();
  255. $offset = tz_offset($TZ);
  256. =head1 DESCRIPTION
  257. This is a collection of miscellaneous timezone manipulation routines.
  258. C<tz2zone()> parses the TZ environment variable and returns a timezone
  259. string suitable for inclusion in L<date>-like output. It optionally takes
  260. a timezone string, a time, and a is-dst flag.
  261. C<tz_local_offset()> determines the offset from GMT time in seconds. It
  262. only does the calculation once.
  263. C<tz_offset()> determines the offset from GMT in seconds of a specified
  264. timezone.
  265. C<tz_name()> determines the name of the timezone based on its offset
  266. =head1 AUTHORS
  267. Graham Barr <[email protected]>
  268. David Muir Sharnoff <[email protected]>
  269. Paul Foley <[email protected]>
  270. =head1 LICENSE
  271. David Muir Sharnoff disclaims any copyright and puts his contribution
  272. to this module in the public domain.