292 lines
8.2 KiB
Perl
292 lines
8.2 KiB
Perl
|
|
package Time::Zone;
|
|
|
|
=head1 NAME
|
|
|
|
Time::Zone -- miscellaneous timezone manipulations routines
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Time::Zone;
|
|
print tz2zone();
|
|
print tz2zone($ENV{'TZ'});
|
|
print tz2zone($ENV{'TZ'}, time());
|
|
print tz2zone($ENV{'TZ'}, undef, $isdst);
|
|
$offset = tz_local_offset();
|
|
$offset = tz_offset($TZ);
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This is a collection of miscellaneous timezone manipulation routines.
|
|
|
|
C<tz2zone()> parses the TZ environment variable and returns a timezone
|
|
string suitable for inclusion in L<date(1)>-like output. It opionally takes
|
|
a timezone string, a time, and a is-dst flag.
|
|
|
|
C<tz_local_offset()> determins the offset from GMT time in seconds. It
|
|
only does the calculation once.
|
|
|
|
C<tz_offset()> determines the offset from GMT in seconds of a specified
|
|
timezone.
|
|
|
|
C<tz_name()> determines the name of the timezone based on its offset
|
|
|
|
=head1 AUTHORS
|
|
|
|
Graham Barr <gbarr@pobox.com>
|
|
David Muir Sharnoff <muir@idiom.com>
|
|
Paul Foley <paul@ascent.com>
|
|
|
|
=cut
|
|
|
|
require 5.002;
|
|
|
|
require Exporter;
|
|
use Carp;
|
|
use strict;
|
|
use vars qw(@ISA @EXPORT $VERSION @tz_local);
|
|
|
|
@ISA = qw(Exporter);
|
|
@EXPORT = qw(tz2zone tz_local_offset tz_offset tz_name);
|
|
$VERSION = "2.24";
|
|
|
|
# Parts stolen from code by Paul Foley <paul@ascent.com>
|
|
|
|
sub tz2zone (;$$$)
|
|
{
|
|
my($TZ, $time, $isdst) = @_;
|
|
|
|
use vars qw(%tzn_cache);
|
|
|
|
$TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : ''
|
|
unless $TZ;
|
|
|
|
# Hack to deal with 'PST8PDT' format of TZ
|
|
# Note that this can't deal with all the esoteric forms, but it
|
|
# does recognize the most common: [:]STDoff[DST[off][,rule]]
|
|
|
|
if (! defined $isdst) {
|
|
my $j;
|
|
$time = time() unless $time;
|
|
($j, $j, $j, $j, $j, $j, $j, $j, $isdst) = localtime($time);
|
|
}
|
|
|
|
if (defined $tzn_cache{$TZ}->[$isdst]) {
|
|
return $tzn_cache{$TZ}->[$isdst];
|
|
}
|
|
|
|
if ($TZ =~ /^
|
|
( [^:\d+\-,] {3,} )
|
|
( [+-] ?
|
|
\d {1,2}
|
|
( : \d {1,2} ) {0,2}
|
|
)
|
|
( [^\d+\-,] {3,} )?
|
|
/x
|
|
) {
|
|
my $dsttz = defined($4) ? $4 : $1;
|
|
$TZ = $isdst ? $dsttz : $1;
|
|
$tzn_cache{$TZ} = [ $1, $dsttz ];
|
|
} else {
|
|
$tzn_cache{$TZ} = [ $TZ, $TZ ];
|
|
}
|
|
return $TZ;
|
|
}
|
|
|
|
sub tz_local_offset (;$)
|
|
{
|
|
my ($time) = @_;
|
|
|
|
$time = time() unless $time;
|
|
my (@l) = localtime($time);
|
|
my $isdst = $l[8];
|
|
|
|
if (defined($tz_local[$isdst])) {
|
|
return $tz_local[$isdst];
|
|
}
|
|
|
|
$tz_local[$isdst] = &calc_off($time);
|
|
|
|
return $tz_local[$isdst];
|
|
}
|
|
|
|
sub calc_off
|
|
{
|
|
my ($time) = @_;
|
|
|
|
my (@l) = localtime($time);
|
|
my (@g) = gmtime($time);
|
|
|
|
my $off;
|
|
|
|
$off = $l[0] - $g[0]
|
|
+ ($l[1] - $g[1]) * 60
|
|
+ ($l[2] - $g[2]) * 3600;
|
|
|
|
# subscript 7 is yday.
|
|
|
|
if ($l[7] == $g[7]) {
|
|
# done
|
|
} elsif ($l[7] == $g[7] + 1) {
|
|
$off += 86400;
|
|
} elsif ($l[7] == $g[7] - 1) {
|
|
$off -= 86400;
|
|
} elsif ($l[7] < $g[7]) {
|
|
# crossed over a year boundry!
|
|
# localtime is beginning of year, gmt is end
|
|
# therefore local is ahead
|
|
$off += 86400;
|
|
} else {
|
|
$off -= 86400;
|
|
}
|
|
|
|
return $off;
|
|
}
|
|
|
|
# constants
|
|
|
|
CONFIG: {
|
|
use vars qw(%dstZone %zoneOff %dstZoneOff %Zone);
|
|
|
|
my @dstZone = (
|
|
# "ndt" => -2*3600-1800, # Newfoundland Daylight
|
|
"brst" => -2*3600, # Brazil Summer Time (East Daylight)
|
|
"adt" => -3*3600, # Atlantic Daylight
|
|
"edt" => -4*3600, # Eastern Daylight
|
|
"cdt" => -5*3600, # Central Daylight
|
|
"mdt" => -6*3600, # Mountain Daylight
|
|
"pdt" => -7*3600, # Pacific Daylight
|
|
"akdt" => -8*3600, # Alaska Daylight
|
|
"ydt" => -8*3600, # Yukon Daylight
|
|
"hdt" => -9*3600, # Hawaii Daylight
|
|
"bst" => +1*3600, # British Summer
|
|
"mest" => +2*3600, # Middle European Summer
|
|
"metdst" => +2*3600, # Middle European DST
|
|
"sst" => +2*3600, # Swedish Summer
|
|
"fst" => +2*3600, # French Summer
|
|
"cest" => +2*3600, # Central European Daylight
|
|
"eest" => +3*3600, # Eastern European Summer
|
|
"msd" => +4*3600, # Moscow Daylight
|
|
"wadt" => +8*3600, # West Australian Daylight
|
|
"kdt" => +10*3600, # Korean Daylight
|
|
# "cadt" => +10*3600+1800, # Central Australian Daylight
|
|
"aedt" => +11*3600, # Eastern Australian Daylight
|
|
"eadt" => +11*3600, # Eastern Australian Daylight
|
|
"nzd" => +13*3600, # New Zealand Daylight
|
|
"nzdt" => +13*3600, # New Zealand Daylight
|
|
);
|
|
|
|
my @Zone = (
|
|
"gmt" => 0, # Greenwich Mean
|
|
"ut" => 0, # Universal (Coordinated)
|
|
"utc" => 0,
|
|
"wet" => 0, # Western European
|
|
"wat" => -1*3600, # West Africa
|
|
"at" => -2*3600, # Azores
|
|
"fnt" => -2*3600, # Brazil Time (Extreme East - Fernando Noronha)
|
|
"brt" => -3*3600, # Brazil Time (East Standard - Brasilia)
|
|
# For completeness. BST is also British Summer, and GST is also Guam Standard.
|
|
# "bst" => -3*3600, # Brazil Standard
|
|
# "gst" => -3*3600, # Greenland Standard
|
|
# "nft" => -3*3600-1800,# Newfoundland
|
|
# "nst" => -3*3600-1800,# Newfoundland Standard
|
|
"mnt" => -4*3600, # Brazil Time (West Standard - Manaus)
|
|
"ewt" => -4*3600, # U.S. Eastern War Time
|
|
"ast" => -4*3600, # Atlantic Standard
|
|
"est" => -5*3600, # Eastern Standard
|
|
"act" => -5*3600, # Brazil Time (Extreme West - Acre)
|
|
"cst" => -6*3600, # Central Standard
|
|
"mst" => -7*3600, # Mountain Standard
|
|
"pst" => -8*3600, # Pacific Standard
|
|
"akst" => -9*3600, # Alaska Standard
|
|
"yst" => -9*3600, # Yukon Standard
|
|
"hst" => -10*3600, # Hawaii Standard
|
|
"cat" => -10*3600, # Central Alaska
|
|
"ahst" => -10*3600, # Alaska-Hawaii Standard
|
|
"nt" => -11*3600, # Nome
|
|
"idlw" => -12*3600, # International Date Line West
|
|
"cet" => +1*3600, # Central European
|
|
"mez" => +1*3600, # Central European (German)
|
|
"ect" => +1*3600, # Central European (French)
|
|
"met" => +1*3600, # Middle European
|
|
"mewt" => +1*3600, # Middle European Winter
|
|
"swt" => +1*3600, # Swedish Winter
|
|
"set" => +1*3600, # Seychelles
|
|
"fwt" => +1*3600, # French Winter
|
|
"eet" => +2*3600, # Eastern Europe, USSR Zone 1
|
|
"ukr" => +2*3600, # Ukraine
|
|
"bt" => +3*3600, # Baghdad, USSR Zone 2
|
|
"msk" => +3*3600, # Moscow
|
|
# "it" => +3*3600+1800,# Iran
|
|
"zp4" => +4*3600, # USSR Zone 3
|
|
"zp5" => +5*3600, # USSR Zone 4
|
|
# "ist" => +5*3600+1800,# Indian Standard
|
|
"zp6" => +6*3600, # USSR Zone 5
|
|
# For completeness. NST is also Newfoundland Stanard, and SST is also Swedish Summer.
|
|
# "nst" => +6*3600+1800,# North Sumatra
|
|
# "sst" => +7*3600, # South Sumatra, USSR Zone 6
|
|
# "jt" => +7*3600+1800,# Java (3pm in Cronusland!)
|
|
"wst" => +8*3600, # West Australian Standard
|
|
"hkt" => +8*3600, # Hong Kong
|
|
"cct" => +8*3600, # China Coast, USSR Zone 7
|
|
"jst" => +9*3600, # Japan Standard, USSR Zone 8
|
|
"kst" => +9*3600, # Korean Standard
|
|
# "cast" => +9*3600+1800,# Central Australian Standard
|
|
"aest" => +10*3600, # Eastern Australian Standard
|
|
"east" => +10*3600, # Eastern Australian Standard
|
|
"gst" => +10*3600, # Guam Standard, USSR Zone 9
|
|
"nzt" => +12*3600, # New Zealand
|
|
"nzst" => +12*3600, # New Zealand Standard
|
|
"idle" => +12*3600, # International Date Line East
|
|
);
|
|
|
|
%Zone = @Zone;
|
|
%dstZone = @dstZone;
|
|
%zoneOff = reverse(@Zone);
|
|
%dstZoneOff = reverse(@dstZone);
|
|
|
|
}
|
|
|
|
sub tz_offset (;$$)
|
|
{
|
|
my ($zone, $time) = @_;
|
|
|
|
return &tz_local_offset($time) unless($zone);
|
|
|
|
$time = time() unless $time;
|
|
my(@l) = localtime($time);
|
|
my $dst = $l[8];
|
|
|
|
$zone = lc $zone;
|
|
|
|
if($zone =~ /^(([\-\+])\d\d?)(\d\d)$/) {
|
|
my $v = $2 . $3;
|
|
return $1 * 3600 + $v * 60;
|
|
} elsif (exists $dstZone{$zone} && ($dst || !exists $Zone{$zone})) {
|
|
return $dstZone{$zone};
|
|
} elsif(exists $Zone{$zone}) {
|
|
return $Zone{$zone};
|
|
}
|
|
undef;
|
|
}
|
|
|
|
sub tz_name (;$$)
|
|
{
|
|
my ($off, $dst) = @_;
|
|
|
|
$off = tz_offset()
|
|
unless(defined $off);
|
|
|
|
$dst = (localtime(time))[8]
|
|
unless(defined $dst);
|
|
|
|
if (exists $dstZoneOff{$off} && ($dst || !exists $zoneOff{$off})) {
|
|
return $dstZoneOff{$off};
|
|
} elsif (exists $zoneOff{$off}) {
|
|
return $zoneOff{$off};
|
|
}
|
|
sprintf("%+05d", int($off / 60) * 100 + $off % 60);
|
|
}
|
|
|
|
1;
|