|
Packit |
95306a |
package Date::Manip::TZ;
|
|
Packit |
95306a |
# Copyright (c) 2008-2017 Sullivan Beck. All rights reserved.
|
|
Packit |
95306a |
# This program is free software; you can redistribute it and/or modify it
|
|
Packit |
95306a |
# under the same terms as Perl itself.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# Any routine that starts with an underscore (_) is NOT intended for
|
|
Packit |
95306a |
# public use. They are for internal use in the the Date::Manip
|
|
Packit |
95306a |
# modules and are subject to change without warning or notice.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# ABSOLUTELY NO USER SUPPORT IS OFFERED FOR THESE ROUTINES!
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
use Date::Manip::Obj;
|
|
Packit |
95306a |
use Date::Manip::TZ_Base;
|
|
Packit |
95306a |
@ISA = qw(Date::Manip::Obj Date::Manip::TZ_Base);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
require 5.010000;
|
|
Packit |
95306a |
use warnings;
|
|
Packit |
95306a |
use strict;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
use IO::File;
|
|
Packit |
95306a |
require Date::Manip::Zones;
|
|
Packit |
95306a |
use Date::Manip::Base;
|
|
Packit |
95306a |
use Data::Dumper;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
our $VERSION;
|
|
Packit |
95306a |
$VERSION='6.60';
|
|
Packit |
95306a |
END { undef $VERSION; }
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# To get rid of a 'used only once' warnings.
|
|
Packit |
95306a |
END {
|
|
Packit |
95306a |
my $tmp = \%Date::Manip::Zones::Module;
|
|
Packit |
95306a |
$tmp = \%Date::Manip::Zones::ZoneNames;
|
|
Packit |
95306a |
$tmp = \%Date::Manip::Zones::Alias;
|
|
Packit |
95306a |
$tmp = \%Date::Manip::Zones::Abbrev;
|
|
Packit |
95306a |
$tmp = \%Date::Manip::Zones::Offmod;
|
|
Packit |
95306a |
$tmp = $Date::Manip::Zones::FirstDate;
|
|
Packit |
95306a |
$tmp = $Date::Manip::Zones::LastDate;
|
|
Packit |
95306a |
$tmp = $Date::Manip::Zones::LastYear;
|
|
Packit |
95306a |
$tmp = $Date::Manip::Zones::TzcodeVersion;
|
|
Packit |
95306a |
$tmp = $Date::Manip::Zones::TzdataVersion;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# BASE METHODS
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _init {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'} =
|
|
Packit |
95306a |
{
|
|
Packit |
95306a |
# These are the variables defined in Date::Manip::Zones
|
|
Packit |
95306a |
'Module' => \%Date::Manip::Zones::Module,
|
|
Packit |
95306a |
'ZoneNames' => \%Date::Manip::Zones::ZoneNames,
|
|
Packit |
95306a |
'Alias' => \%Date::Manip::Zones::Alias,
|
|
Packit |
95306a |
'Abbrev' => \%Date::Manip::Zones::Abbrev,
|
|
Packit |
95306a |
'Offmod' => \%Date::Manip::Zones::Offmod,
|
|
Packit |
95306a |
'FirstDate' => $Date::Manip::Zones::FirstDate,
|
|
Packit |
95306a |
'LastDate' => $Date::Manip::Zones::LastDate,
|
|
Packit |
95306a |
'LastYear' => $Date::Manip::Zones::LastYear,
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# These override values from Date::Manip::Zones
|
|
Packit |
95306a |
'MyAlias' => {},
|
|
Packit |
95306a |
'MyAbbrev' => {},
|
|
Packit |
95306a |
'MyOffsets' => {},
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Each timezone/offset module that is loaded goes here
|
|
Packit |
95306a |
'Zones' => {},
|
|
Packit |
95306a |
'Offsets' => {},
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# methods a list of methods used for determining the
|
|
Packit |
95306a |
# current zone
|
|
Packit |
95306a |
# path the PATH to set for determining the current
|
|
Packit |
95306a |
# zone
|
|
Packit |
95306a |
# dates critical dates on a per/year (UT) basis
|
|
Packit |
95306a |
# zonerx the regular expression for matching timezone
|
|
Packit |
95306a |
# names/aliases
|
|
Packit |
95306a |
# abbrx the regular expression for matching timezone
|
|
Packit |
95306a |
# abbreviations
|
|
Packit |
95306a |
# offrx the regular expression for matching a valid
|
|
Packit |
95306a |
# timezone offset
|
|
Packit |
95306a |
# zrx the regular expression to match all timezone
|
|
Packit |
95306a |
# information
|
|
Packit |
95306a |
'methods' => [],
|
|
Packit |
95306a |
'path' => undef,
|
|
Packit |
95306a |
'zonerx' => undef,
|
|
Packit |
95306a |
'abbrx' => undef,
|
|
Packit |
95306a |
'offrx' => undef,
|
|
Packit |
95306a |
'zrx' => undef,
|
|
Packit |
95306a |
};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# OS specific stuff
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
my $os = $dmb->_os();
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($os eq 'Unix') {
|
|
Packit |
95306a |
$$self{'data'}{'path'} = '/bin:/usr/bin';
|
|
Packit |
95306a |
$$self{'data'}{'methods'} = [
|
|
Packit |
95306a |
qw(main TZ
|
|
Packit |
95306a |
env zone TZ
|
|
Packit |
95306a |
file /etc/TIMEZONE
|
|
Packit |
95306a |
file /etc/timezone
|
|
Packit |
95306a |
file /etc/sysconfig/clock
|
|
Packit |
95306a |
file /etc/default/init
|
|
Packit |
95306a |
tzdata /etc/localtime /usr/share/zoneinfo
|
|
Packit |
95306a |
),
|
|
Packit |
95306a |
'command', '/bin/date +%Z',
|
|
Packit |
95306a |
'command', '/usr/bin/date +%Z',
|
|
Packit |
95306a |
'command', '/usr/local/bin/date +%Z',
|
|
Packit |
95306a |
qw(cmdfield /bin/date -2
|
|
Packit |
95306a |
cmdfield /usr/bin/date -2
|
|
Packit |
95306a |
cmdfield /usr/local/bin/date -2
|
|
Packit |
95306a |
),
|
|
Packit |
95306a |
'command', '/bin/date +%z',
|
|
Packit |
95306a |
'command', '/usr/bin/date +%z',
|
|
Packit |
95306a |
'command', '/usr/local/bin/date +%z',
|
|
Packit |
95306a |
'gmtoff'
|
|
Packit |
95306a |
];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($os eq 'Windows') {
|
|
Packit |
95306a |
$$self{'data'}{'methods'} = [
|
|
Packit |
95306a |
qw(main TZ
|
|
Packit |
95306a |
env zone TZ
|
|
Packit |
95306a |
registry
|
|
Packit |
95306a |
gmtoff),
|
|
Packit |
95306a |
];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($os eq 'VMS') {
|
|
Packit |
95306a |
$$self{'data'}{'methods'} = [
|
|
Packit |
95306a |
qw(main TZ
|
|
Packit |
95306a |
env zone TZ
|
|
Packit |
95306a |
env zone SYS$TIMEZONE_NAME
|
|
Packit |
95306a |
env zone UCX$TZ
|
|
Packit |
95306a |
env zone TCPIP$TZ
|
|
Packit |
95306a |
env zone MULTINET_TIMEZONE
|
|
Packit |
95306a |
env offset SYS$TIMEZONE_DIFFERENTIAL
|
|
Packit |
95306a |
gmtoff
|
|
Packit |
95306a |
),
|
|
Packit |
95306a |
];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
$$self{'data'}{'methods'} = [
|
|
Packit |
95306a |
qw(main TZ
|
|
Packit |
95306a |
env zone TZ
|
|
Packit |
95306a |
gmtoff
|
|
Packit |
95306a |
),
|
|
Packit |
95306a |
];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _init_final {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$self->_set_curr_zone();
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
no strict 'refs';
|
|
Packit |
95306a |
# This loads data from an offset module
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _offmod {
|
|
Packit |
95306a |
my($self,$offset) = @_;
|
|
Packit |
95306a |
return if (exists $$self{'data'}{'Offsets'}{$offset});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $mod = $$self{'data'}{'Offmod'}{$offset};
|
|
Packit |
95306a |
eval "require Date::Manip::Offset::${mod}";
|
|
Packit |
95306a |
my %off = %{ "Date::Manip::Offset::${mod}::Offset" };
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'Offsets'}{$offset} = { %off };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This loads data from a zone module (takes a lowercase zone)
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _module {
|
|
Packit |
95306a |
my($self,$zone) = @_;
|
|
Packit |
95306a |
return if (exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $mod = $$self{'data'}{'Module'}{$zone};
|
|
Packit |
95306a |
eval "require Date::Manip::TZ::${mod}";
|
|
Packit |
95306a |
my %dates = %{ "Date::Manip::TZ::${mod}::Dates" };
|
|
Packit |
95306a |
my %last = %{ "Date::Manip::TZ::${mod}::LastRule" };
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone} =
|
|
Packit |
95306a |
{
|
|
Packit |
95306a |
'Dates' => { %dates },
|
|
Packit |
95306a |
'LastRule' => { %last },
|
|
Packit |
95306a |
'Loaded' => 1
|
|
Packit |
95306a |
};
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
use strict 'refs';
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# CHECKING/MODIFYING ZONEINFO DATA
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _zone {
|
|
Packit |
95306a |
my($self,$zone) = @_;
|
|
Packit |
95306a |
$zone = lc($zone);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (exists $$self{'data'}{'MyAlias'}{$zone}) {
|
|
Packit |
95306a |
return $$self{'data'}{'MyAlias'}{$zone};
|
|
Packit |
95306a |
} elsif (exists $$self{'data'}{'Alias'}{$zone}) {
|
|
Packit |
95306a |
return $$self{'data'}{'Alias'}{$zone};
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return '';
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub tzdata {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
return $Date::Manip::Zones::TzdataVersion;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub tzcode {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
return $Date::Manip::Zones::TzcodeVersion;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub define_alias {
|
|
Packit |
95306a |
my($self,$alias,$zone) = @_;
|
|
Packit |
95306a |
$alias = lc($alias);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($alias eq 'reset') {
|
|
Packit |
95306a |
$$self{'data'}{'MyAlias'} = {};
|
|
Packit |
95306a |
$$self{'data'}{'zonerx'} = undef;
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
if (lc($zone) eq 'reset') {
|
|
Packit |
95306a |
delete $$self{'data'}{'MyAlias'}{$alias};
|
|
Packit |
95306a |
$$self{'data'}{'zonerx'} = undef;
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$zone = $self->_zone($zone);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return 1 if (! $zone);
|
|
Packit |
95306a |
$$self{'data'}{'MyAlias'}{$alias} = $zone;
|
|
Packit |
95306a |
$$self{'data'}{'zonerx'} = undef;
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub define_abbrev {
|
|
Packit |
95306a |
my($self,$abbrev,@zone) = @_;
|
|
Packit |
95306a |
$abbrev = lc($abbrev);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($abbrev eq 'reset') {
|
|
Packit |
95306a |
$$self{'data'}{'MyAbbrev'} = {};
|
|
Packit |
95306a |
$$self{'data'}{'abbrx'} = undef;
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
if ($#zone == 0 && lc($zone[0]) eq 'reset') {
|
|
Packit |
95306a |
delete $$self{'data'}{'MyAbbrev'}{$abbrev};
|
|
Packit |
95306a |
$$self{'data'}{'abbrx'} = undef;
|
|
Packit |
95306a |
return (0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! exists $$self{'data'}{'Abbrev'}{$abbrev}) {
|
|
Packit |
95306a |
return (1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my (@z,%z);
|
|
Packit |
95306a |
my %zone = map { $_,1 } @{ $$self{'data'}{'Abbrev'}{$abbrev} };
|
|
Packit |
95306a |
foreach my $z (@zone) {
|
|
Packit |
95306a |
my $zone = $self->_zone($z);
|
|
Packit |
95306a |
return (2,$z) if (! $zone);
|
|
Packit |
95306a |
return (3,$z) if (! exists $zone{$zone});
|
|
Packit |
95306a |
next if (exists $z{$zone});
|
|
Packit |
95306a |
$z{$zone} = 1;
|
|
Packit |
95306a |
push(@z,$zone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'MyAbbrev'}{$abbrev} = [ @z ];
|
|
Packit |
95306a |
$$self{'data'}{'abbrx'} = undef;
|
|
Packit |
95306a |
return ();
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub define_offset {
|
|
Packit |
95306a |
my($self,$offset,@args) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (lc($offset) eq 'reset') {
|
|
Packit |
95306a |
$$self{'data'}{'MyOffsets'} = {};
|
|
Packit |
95306a |
return (0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
if ($#args == 0 && lc($args[0]) eq 'reset') {
|
|
Packit |
95306a |
delete $$self{'data'}{'MyOffsets'}{$offset};
|
|
Packit |
95306a |
return (0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Check that $offset is valid. If it is, load the
|
|
Packit |
95306a |
# appropriate module.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (ref($offset)) {
|
|
Packit |
95306a |
$offset = $dmb->join('offset',$offset);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
$offset = $dmb->_delta_convert('offset',$offset);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return (9) if (! $offset);
|
|
Packit |
95306a |
return (1) if (! exists $$self{'data'}{'Offmod'}{$offset});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$self->_offmod($offset);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Find out whether we're handling STD, DST, or both.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my(@isdst) = (0,1);
|
|
Packit |
95306a |
if ($args[0] =~ /^std|dst|stdonly|dstonly$/i) {
|
|
Packit |
95306a |
my $tmp = lc(shift(@args));
|
|
Packit |
95306a |
if ($tmp eq 'stdonly') {
|
|
Packit |
95306a |
@isdst = (0);
|
|
Packit |
95306a |
} elsif ($tmp eq 'dstonly') {
|
|
Packit |
95306a |
@isdst = (1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my @zone = @args;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($#isdst == 0 &&
|
|
Packit |
95306a |
! exists($$self{'data'}{'Offsets'}{$offset}{$isdst[0]})) {
|
|
Packit |
95306a |
return (2);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Check to see that each zone is valid, and contains this offset.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my %tmp;
|
|
Packit |
95306a |
foreach my $isdst (0,1) {
|
|
Packit |
95306a |
next if (! exists $$self{'data'}{'Offsets'}{$offset}{$isdst});
|
|
Packit |
95306a |
my @z = @{ $$self{'data'}{'Offsets'}{$offset}{$isdst} };
|
|
Packit |
95306a |
$tmp{$isdst} = { map { $_,1 } @z };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $z (@zone) {
|
|
Packit |
95306a |
my $lcz = lc($z);
|
|
Packit |
95306a |
if (! exists $$self{'data'}{'ZoneNames'}{$lcz}) {
|
|
Packit |
95306a |
return (3,$z);
|
|
Packit |
95306a |
} elsif (! exists $tmp{0}{$lcz} &&
|
|
Packit |
95306a |
! exists $tmp{1}{$lcz}) {
|
|
Packit |
95306a |
return (4,$z);
|
|
Packit |
95306a |
} elsif ($#isdst == 0 &&
|
|
Packit |
95306a |
! exists $tmp{$isdst[0]}{$lcz}) {
|
|
Packit |
95306a |
return (5,$z);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$z = $lcz;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Set the zones accordingly.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $isdst (@isdst) {
|
|
Packit |
95306a |
my @z;
|
|
Packit |
95306a |
foreach my $z (@zone) {
|
|
Packit |
95306a |
push(@z,$z) if (exists $tmp{$isdst}{$z});
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$$self{'data'}{'MyOffsets'}{$offset}{$isdst} = [ @z ];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return (0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# SYSTEM ZONE
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub curr_zone {
|
|
Packit |
95306a |
my($self,$reset) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($reset) {
|
|
Packit |
95306a |
$self->_set_curr_zone();
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($ret) = $self->_now('systz',1);
|
|
Packit |
95306a |
return $$self{'data'}{'ZoneNames'}{$ret}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub curr_zone_methods {
|
|
Packit |
95306a |
my($self,@methods) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (${^TAINT}) {
|
|
Packit |
95306a |
warn "ERROR: [curr_zone_methods] not allowed when taint checking on\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'methods'} = [ @methods ];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _set_curr_zone {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
my $currzone = $self->_get_curr_zone();
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$dmb{'data'}{'now'}{'systz'} = $self->_zone($currzone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This determines the system timezone using all of the methods
|
|
Packit |
95306a |
# applicable to the operating system. The first match is used.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _get_curr_zone {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $t = time;
|
|
Packit |
95306a |
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t);
|
|
Packit |
95306a |
my $currzone = '';
|
|
Packit |
95306a |
my $dstflag = ($isdst ? 'dstonly' : 'stdonly');
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my (@methods) = @{ $$self{'data'}{'methods'} };
|
|
Packit |
95306a |
my $debug = ($ENV{DATE_MANIP_DEBUG} ? 1 : 0);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
defined $$self{'data'}{'path'}
|
|
Packit |
95306a |
and local $ENV{PATH} = $$self{'data'}{'path'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
METHOD:
|
|
Packit |
95306a |
while (@methods) {
|
|
Packit |
95306a |
my $method = shift(@methods);
|
|
Packit |
95306a |
my @zone = ();
|
|
Packit |
95306a |
|
|
Packit |
95306a |
print "*** DEBUG *** METHOD: $method [" if ($debug);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($method eq 'main') {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! @methods) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] main requires argument\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $var = shift(@methods);
|
|
Packit |
95306a |
print "$var] " if ($debug);
|
|
Packit |
95306a |
no strict "refs";
|
|
Packit |
95306a |
my $val = ${ "::$var" };
|
|
Packit |
95306a |
use strict "refs";
|
|
Packit |
95306a |
if (defined $val) {
|
|
Packit |
95306a |
push(@zone,$val);
|
|
Packit |
95306a |
print "$val\n" if ($debug);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "undef\n" if ($debug);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'env') {
|
|
Packit |
95306a |
if (@methods < 2) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] env requires 2 argument\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $type = lc( shift(@methods) );
|
|
Packit |
95306a |
print "$type," if ($debug);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($type ne 'zone' &&
|
|
Packit |
95306a |
$type ne 'offset') {
|
|
Packit |
95306a |
print "?]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] env requires 'offset' or 'zone' " .
|
|
Packit |
95306a |
"as the first argument\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $var = shift(@methods);
|
|
Packit |
95306a |
print "$var] " if ($debug);
|
|
Packit |
95306a |
if (exists $ENV{$var}) {
|
|
Packit |
95306a |
if ($type eq 'zone') {
|
|
Packit |
95306a |
push(@zone,$ENV{$var});
|
|
Packit |
95306a |
print "$ENV{$var}\n" if ($debug);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
my $off = $ENV{$var};
|
|
Packit |
95306a |
print "$ENV{$var} = " if ($debug);
|
|
Packit |
95306a |
$off = $dmb->_delta_convert('time',"0:0:$off");
|
|
Packit |
95306a |
$off = $dmb->_delta_convert('offset',$off);
|
|
Packit |
95306a |
print "$off\n" if ($debug);
|
|
Packit |
95306a |
push(@zone,$off);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "undef\n" if ($debug);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'file') {
|
|
Packit |
95306a |
if (! @methods) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] file requires argument\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $file = shift(@methods);
|
|
Packit |
95306a |
print "$file] " if ($debug);
|
|
Packit |
95306a |
if (! -f $file) {
|
|
Packit |
95306a |
print "not found\n" if ($debug);
|
|
Packit |
95306a |
next;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $in = new IO::File;
|
|
Packit |
95306a |
$in->open($file) || next;
|
|
Packit |
95306a |
my $firstline = 1;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @z;
|
|
Packit |
95306a |
while (! $in->eof) {
|
|
Packit |
95306a |
my $line = <$in>;
|
|
Packit |
95306a |
chomp($line);
|
|
Packit |
95306a |
next if ($line =~ /^\s*\043/ ||
|
|
Packit |
95306a |
$line =~ /^\s*$/);
|
|
Packit |
95306a |
if ($firstline) {
|
|
Packit |
95306a |
$firstline = 0;
|
|
Packit |
95306a |
$line =~ s/^\s*//;
|
|
Packit |
95306a |
$line =~ s/\s*$//;
|
|
Packit |
95306a |
$line =~ s/["']//g; # "
|
|
Packit |
95306a |
$line =~ s/\s+/_/g;
|
|
Packit |
95306a |
@z = ($line);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# We're looking for lines of the form:
|
|
Packit |
95306a |
# TZ = string
|
|
Packit |
95306a |
# TIMEZONE = string
|
|
Packit |
95306a |
# ZONE = string
|
|
Packit |
95306a |
# Alternately, we may use a 1-line file (ignoring comments and
|
|
Packit |
95306a |
# whitespace) which contains only the zone name (it may be
|
|
Packit |
95306a |
# quoted or contain embedded whitespace).
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# 'string' can be:
|
|
Packit |
95306a |
# the name of a timezone enclosed in single/double quotes
|
|
Packit |
95306a |
# with everything after the closing quote ignored (the
|
|
Packit |
95306a |
# name of the timezone may have spaces instead of underscores)
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# a space delimited list of tokens, the first of which
|
|
Packit |
95306a |
# is the time zone
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# the name of a timezone with underscores replaced by
|
|
Packit |
95306a |
# spaces and nothing after the timezone
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# For some reason, RHEL6 desktop version stores timezones as
|
|
Packit |
95306a |
# America/New York
|
|
Packit |
95306a |
# instead of
|
|
Packit |
95306a |
# America/New_York
|
|
Packit |
95306a |
# which is why we have to handle the space/underscore
|
|
Packit |
95306a |
# substitution.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($line =~ /^\s*(?:TZ|TIMEZONE|ZONE)\s*=\s*(.*)\s*$/) {
|
|
Packit |
95306a |
my $val = $1;
|
|
Packit |
95306a |
@z = ();
|
|
Packit |
95306a |
last if (! $val);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($val =~ /^(["'])(.*?)\1/) {
|
|
Packit |
95306a |
my $z = $2;
|
|
Packit |
95306a |
last if (! $z);
|
|
Packit |
95306a |
$z =~ s/\s+/_/g;
|
|
Packit |
95306a |
push(@zone,$z);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($val =~ /\s/) {
|
|
Packit |
95306a |
$val =~ /^(\S+)/;
|
|
Packit |
95306a |
push(@zone,$1);
|
|
Packit |
95306a |
$val =~ s/\s+/_/g;
|
|
Packit |
95306a |
push(@zone,$val);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
push(@zone,$val);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
last;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
close(IN);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
push(@zone,@z) if (@z);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($debug) {
|
|
Packit |
95306a |
if (@zone) {
|
|
Packit |
95306a |
print "@zone\n";
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "no result\n";
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'tzdata') {
|
|
Packit |
95306a |
if (@methods < 2) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] tzdata requires two arguments\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $file = shift(@methods);
|
|
Packit |
95306a |
my $dir = shift(@methods);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $z;
|
|
Packit |
95306a |
if (-f $file && -d $dir) {
|
|
Packit |
95306a |
$z = _get_zoneinfo_zone($file,$dir);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
if (defined($z)) {
|
|
Packit |
95306a |
push @zone, $z;
|
|
Packit |
95306a |
print "] $z\n" if ($debug);
|
|
Packit |
95306a |
} elsif ($debug) {
|
|
Packit |
95306a |
print "] no result\n";
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'command') {
|
|
Packit |
95306a |
if (! @methods) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] command requires argument\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $command = shift(@methods);
|
|
Packit |
95306a |
print "$command] " if ($debug);
|
|
Packit |
95306a |
my ($out) = _cmd($command);
|
|
Packit |
95306a |
push(@zone,$out) if ($out);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($debug) {
|
|
Packit |
95306a |
if ($out) {
|
|
Packit |
95306a |
print "$out\n";
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "no output\n";
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'cmdfield') {
|
|
Packit |
95306a |
if ($#methods < 1) {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] cmdfield requires 2 arguments\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $command = shift(@methods);
|
|
Packit |
95306a |
my $n = shift(@methods);
|
|
Packit |
95306a |
print "$command,$n]\n" if ($debug);
|
|
Packit |
95306a |
my ($out) = _cmd($command);
|
|
Packit |
95306a |
my $val;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($out) {
|
|
Packit |
95306a |
$out =~ s/^\s*//;
|
|
Packit |
95306a |
$out =~ s/\s*$//;
|
|
Packit |
95306a |
my @out = split(/\s+/,$out);
|
|
Packit |
95306a |
$val = $out[$n] if (defined $out[$n]);
|
|
Packit |
95306a |
push(@zone,$val);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($debug) {
|
|
Packit |
95306a |
if ($val) {
|
|
Packit |
95306a |
print "$val\n";
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "no result\n";
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'gmtoff') {
|
|
Packit |
95306a |
print "] " if ($debug);
|
|
Packit |
95306a |
my($secUT,$minUT,$hourUT,$mdayUT,$monUT,$yearUT,$wdayUT,$ydayUT,
|
|
Packit |
95306a |
$isdstUT) = gmtime($t);
|
|
Packit |
95306a |
if ($mdayUT>($mday+1)) {
|
|
Packit |
95306a |
# UT = 28-31 LT = 1
|
|
Packit |
95306a |
$mdayUT=0;
|
|
Packit |
95306a |
} elsif ($mdayUT<($mday-1)) {
|
|
Packit |
95306a |
# UT = 1 LT = 28-31
|
|
Packit |
95306a |
$mday=0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$sec = (($mday*24 + $hour)*60 + $min)*60 + $sec;
|
|
Packit |
95306a |
$secUT = (($mdayUT*24 + $hourUT)*60 + $minUT)*60 + $secUT;
|
|
Packit |
95306a |
my $off = $sec-$secUT;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$off = $dmb->_delta_convert('time',"0:0:$off");
|
|
Packit |
95306a |
$off = $dmb->_delta_convert('offset',$off);
|
|
Packit |
95306a |
push(@zone,$off);
|
|
Packit |
95306a |
print "$off\n" if ($debug);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($method eq 'registry') {
|
|
Packit |
95306a |
print "] " if ($debug);
|
|
Packit |
95306a |
my $z = $self->_windows_registry_val();
|
|
Packit |
95306a |
if ($z) {
|
|
Packit |
95306a |
push(@zone,$z);
|
|
Packit |
95306a |
print "$z\n" if ($debug);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "no result\n" if ($debug);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
print "]\n" if ($debug);
|
|
Packit |
95306a |
warn "ERROR: [_set_curr_zone] invalid method: $method\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
while (@zone) {
|
|
Packit |
95306a |
my $zone = lc(shift(@zone));
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# OpenUNIX puts a colon at the start
|
|
Packit |
95306a |
$zone =~ s/^://;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# If we got a zone name/alias
|
|
Packit |
95306a |
$currzone = $self->_zone($zone);
|
|
Packit |
95306a |
last METHOD if ($currzone);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# If we got an abbreviation (EST)
|
|
Packit |
95306a |
if (exists $$self{'data'}{'Abbrev'}{$zone}) {
|
|
Packit |
95306a |
$currzone = $$self{'data'}{'Abbrev'}{$zone}[0];
|
|
Packit |
95306a |
last METHOD;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# If we got an offset
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$currzone = $self->__zone([],'',$zone,'',$dstflag);
|
|
Packit |
95306a |
last METHOD if ($currzone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $currzone) {
|
|
Packit |
95306a |
warn "ERROR: Date::Manip unable to determine Time Zone.\n";
|
|
Packit |
95306a |
die;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return $currzone;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#######################
|
|
Packit |
95306a |
# The following section comes from the DateTime-TimeZone module
|
|
Packit |
95306a |
|
|
Packit |
95306a |
{
|
|
Packit |
95306a |
my $want_content;
|
|
Packit |
95306a |
my $want_size;
|
|
Packit |
95306a |
my $zoneinfo;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _get_zoneinfo_zone {
|
|
Packit |
95306a |
my($localtime,$z) = @_;
|
|
Packit |
95306a |
$zoneinfo = $z;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# /etc/localtime should be either a link to a tzdata file in
|
|
Packit |
95306a |
# /usr/share/zoneinfo or a copy of one of the files there.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return '' if (! -d $zoneinfo || ! -f $localtime);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
require Cwd;
|
|
Packit |
95306a |
if (-l $localtime) {
|
|
Packit |
95306a |
return _zoneinfo_file_name_to_zone(
|
|
Packit |
95306a |
Cwd::abs_path($localtime),
|
|
Packit |
95306a |
$zoneinfo,
|
|
Packit |
95306a |
);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$want_content = _zoneinfo_file_slurp($localtime);
|
|
Packit |
95306a |
$want_size = -s $localtime;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# File::Find can't bail in the middle of a find, and we only want the
|
|
Packit |
95306a |
# first match, so we'll call it in an eval.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
local $@ = undef;
|
|
Packit |
95306a |
eval {
|
|
Packit |
95306a |
require File::Find;
|
|
Packit |
95306a |
File::Find::find
|
|
Packit |
95306a |
({
|
|
Packit |
95306a |
wanted => \&_zoneinfo_find_file,
|
|
Packit |
95306a |
no_chdir => 1,
|
|
Packit |
95306a |
},
|
|
Packit |
95306a |
$zoneinfo,
|
|
Packit |
95306a |
);
|
|
Packit |
95306a |
1;
|
|
Packit |
95306a |
} and return;
|
|
Packit |
95306a |
ref $@
|
|
Packit |
95306a |
and return $@->{zone};
|
|
Packit |
95306a |
die $@;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _zoneinfo_find_file {
|
|
Packit |
95306a |
my $zone;
|
|
Packit |
95306a |
defined($zone = _zoneinfo_file_name_to_zone($File::Find::name,
|
|
Packit |
95306a |
$zoneinfo))
|
|
Packit |
95306a |
and -f $_
|
|
Packit |
95306a |
and $want_size == -s _
|
|
Packit |
95306a |
and ($want_content eq _zoneinfo_file_slurp($File::Find::name))
|
|
Packit |
95306a |
and die { zone => $zone };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _zoneinfo_file_name_to_zone {
|
|
Packit |
95306a |
my($file,$zoneinfo) = @_;
|
|
Packit |
95306a |
require File::Spec;
|
|
Packit |
95306a |
my $zone = File::Spec->abs2rel($file,$zoneinfo);
|
|
Packit |
95306a |
return $zone if (exists $Date::Manip::Zones::ZoneNames{lc($zone)});
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _zoneinfo_file_slurp {
|
|
Packit |
95306a |
my($file) = @_;
|
|
Packit |
95306a |
open my $fh, '<', $file
|
|
Packit |
95306a |
or return;
|
|
Packit |
95306a |
binmode $fh;
|
|
Packit |
95306a |
local $/ = undef;
|
|
Packit |
95306a |
return <$fh>;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _windows_registry_val {
|
|
Packit |
95306a |
my($self) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
require Win32::TieRegistry;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $lmachine = new Win32::TieRegistry 'LMachine',
|
|
Packit |
95306a |
{ Access => Win32::TieRegistry::KEY_READ(),
|
|
Packit |
95306a |
Delimiter => '/' }
|
|
Packit |
95306a |
or return '';
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $tzinfo = $lmachine->Open('SYSTEM/CurrentControlSet/Control/TimeZoneInformation/');
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Windows Vista, Windows 2008 Server
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $tzkn = $tzinfo->GetValue('TimeZoneKeyName');
|
|
Packit |
95306a |
if (defined($tzkn) && $tzkn) {
|
|
Packit |
95306a |
# For some reason, Vista is tacking on a bunch of stuff at the
|
|
Packit |
95306a |
# end of the timezone, starting with a chr(0). Strip it off.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $c = chr(0);
|
|
Packit |
95306a |
my $i = index($tzkn,$c);
|
|
Packit |
95306a |
if ($i != -1) {
|
|
Packit |
95306a |
$tzkn = substr($tzkn,0,$i);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $z = $self->_zone($tzkn);
|
|
Packit |
95306a |
return $z if ($z);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Windows NT, Windows 2000, Windows XP, Windows 2003 Server
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $stdnam = $tzinfo->GetValue('StandardName');
|
|
Packit |
95306a |
my $z = $self->_zone($stdnam);
|
|
Packit |
95306a |
return $z if ($z);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# For non-English versions, we have to determine which timezone it
|
|
Packit |
95306a |
# actually is.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $atz = $lmachine->Open('SOFTWARE/Microsoft/Windows NT/CurrentVersion/Time Zones/');
|
|
Packit |
95306a |
if (! defined($atz) || ! $atz) {
|
|
Packit |
95306a |
$atz = $lmachine->Open('SOFTWARE/Microsoft/Windows/CurrentVersion/Time Zones/');
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return "" if (! defined($atz) || ! $atz);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $z ($atz->SubKeyNames()) {
|
|
Packit |
95306a |
my $tmp = $atz->Open("$z/");
|
|
Packit |
95306a |
my $znam = $tmp->GetValue('Std');
|
|
Packit |
95306a |
return $z if ($znam eq $stdnam);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# End of DateTime-TimeZone section
|
|
Packit |
95306a |
#######################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# We will be testing commands that don't exist on all architectures,
|
|
Packit |
95306a |
# so disable warnings.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
no warnings;
|
|
Packit |
95306a |
sub _cmd {
|
|
Packit |
95306a |
my($cmd) = @_;
|
|
Packit |
95306a |
local(*IN);
|
|
Packit |
95306a |
open(IN,"$cmd |") || return ();
|
|
Packit |
95306a |
my @out = <IN>;
|
|
Packit |
95306a |
close(IN);
|
|
Packit |
95306a |
chomp(@out);
|
|
Packit |
95306a |
return @out;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
use warnings;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# DETERMINING A TIMEZONE
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub zone {
|
|
Packit |
95306a |
my($self,@args) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
if (! @args) {
|
|
Packit |
95306a |
my($tz) = $self->_now('tz',1);
|
|
Packit |
95306a |
return $$self{'data'}{'ZoneNames'}{$tz}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Parse the arguments
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($zone,$abbrev,$offset,$dstflag) = ('','','','');
|
|
Packit |
95306a |
my $date = [];
|
|
Packit |
95306a |
my $tmp;
|
|
Packit |
95306a |
foreach my $arg (@args) {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (ref($arg) eq 'ARRAY') {
|
|
Packit |
95306a |
if ($#$arg == 5) {
|
|
Packit |
95306a |
# [Y,M,D,H,Mn,S]
|
|
Packit |
95306a |
return undef if (@$date);
|
|
Packit |
95306a |
$date = $arg;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($#$arg == 2) {
|
|
Packit |
95306a |
# [H,Mn,S]
|
|
Packit |
95306a |
return undef if ($offset);
|
|
Packit |
95306a |
$offset = $dmb->join('offset',$arg);
|
|
Packit |
95306a |
return undef if (! $offset);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return undef;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif (ref($arg)) {
|
|
Packit |
95306a |
return undef;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
$arg = lc($arg);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($arg =~ /^(std|dst|stdonly|dstonly)$/) {
|
|
Packit |
95306a |
return undef if ($dstflag);
|
|
Packit |
95306a |
$dstflag = $arg;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($tmp = $self->_zone($arg)) {
|
|
Packit |
95306a |
return undef if ($zone);
|
|
Packit |
95306a |
$zone = $tmp;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif (exists $$self{'data'}{'MyAbbrev'}{$arg} ||
|
|
Packit |
95306a |
exists $$self{'data'}{'Abbrev'}{$arg}) {
|
|
Packit |
95306a |
return undef if ($abbrev);
|
|
Packit |
95306a |
$abbrev = $arg;
|
|
Packit |
95306a |
} elsif (exists $$self{'data'}{'Abbrev'}{$arg}) {
|
|
Packit |
95306a |
return undef if ($abbrev);
|
|
Packit |
95306a |
$abbrev = $arg;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($tmp = $dmb->split('offset',$arg)) {
|
|
Packit |
95306a |
return undef if ($offset);
|
|
Packit |
95306a |
$offset = $dmb->_delta_convert('offset',$arg);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($tmp = $dmb->split('date',$arg)) {
|
|
Packit |
95306a |
return undef if ($date);
|
|
Packit |
95306a |
$date = $tmp;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return undef;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return $self->__zone($date,$offset,$zone,$abbrev,$dstflag);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $date = [Y,M,D,H,Mn,S]
|
|
Packit |
95306a |
# $offset = '-HH:Mn:SS'
|
|
Packit |
95306a |
# $zone = 'us/eastern' (lowercase)
|
|
Packit |
95306a |
# $abbrev = 'est' (lowercase)
|
|
Packit |
95306a |
# $dstflag= 'stdonly' (lowercase)
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub __zone {
|
|
Packit |
95306a |
my($self,$date,$offset,$zone,$abbrev,$dstflag) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Determine the zones that match all data.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @zone;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
while (1) {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# No information
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $zone &&
|
|
Packit |
95306a |
! $abbrev &&
|
|
Packit |
95306a |
! $offset) {
|
|
Packit |
95306a |
my($z) = $self->_now('tz',1);
|
|
Packit |
95306a |
@zone = (lc($z));
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $dstflag
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# $dstflag is "dst' if
|
|
Packit |
95306a |
# zone is passed in as an offset
|
|
Packit |
95306a |
# date is passed in
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$dstflag = "dst" if ($offset && @$date && ! $dstflag);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my(@isdst);
|
|
Packit |
95306a |
if ($dstflag eq 'stdonly') {
|
|
Packit |
95306a |
@isdst = (0);
|
|
Packit |
95306a |
} elsif ($dstflag eq 'dstonly') {
|
|
Packit |
95306a |
@isdst = (1);
|
|
Packit |
95306a |
} elsif ($dstflag eq 'dst') {
|
|
Packit |
95306a |
@isdst = (1,0);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@isdst = (0,1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# We may pass in $zone and not $abbrev when it really should be
|
|
Packit |
95306a |
# $abbrev.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($zone && ! $abbrev) {
|
|
Packit |
95306a |
if (exists $$self{'data'}{'Alias'}{$zone}) {
|
|
Packit |
95306a |
# no change
|
|
Packit |
95306a |
} elsif (exists $$self{'data'}{'MyAbbrev'}{$zone} ||
|
|
Packit |
95306a |
exists $$self{'data'}{'Abbrev'}{$zone}) {
|
|
Packit |
95306a |
$abbrev = $zone;
|
|
Packit |
95306a |
$zone = '';
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $zone
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($zone) {
|
|
Packit |
95306a |
my $z = (exists $$self{'data'}{'Alias'}{$zone} ?
|
|
Packit |
95306a |
$$self{'data'}{'Alias'}{$zone} : $zone);
|
|
Packit |
95306a |
@zone = ($z);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $abbrev
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($abbrev) {
|
|
Packit |
95306a |
my @abbrev_zones;
|
|
Packit |
95306a |
if (exists $$self{'data'}{'MyAbbrev'}{$abbrev}) {
|
|
Packit |
95306a |
@abbrev_zones = @{ $$self{'data'}{'MyAbbrev'}{$abbrev} };
|
|
Packit |
95306a |
} elsif (exists $$self{'data'}{'Abbrev'}{$abbrev}) {
|
|
Packit |
95306a |
@abbrev_zones = @{ $$self{'data'}{'Abbrev'}{$abbrev} };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @z;
|
|
Packit |
95306a |
foreach my $isdst (@isdst) {
|
|
Packit |
95306a |
my @tmp = $self->_check_abbrev_isdst($abbrev,$isdst,@abbrev_zones);
|
|
Packit |
95306a |
if (@tmp) {
|
|
Packit |
95306a |
if (@z) {
|
|
Packit |
95306a |
@z = _list_add(\@z,\@tmp);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@z = @tmp;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (@zone) {
|
|
Packit |
95306a |
@zone = _list_union(\@z,\@zone);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@zone = @z;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
last if (! @zone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $offset
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($offset) {
|
|
Packit |
95306a |
return undef if (! exists $$self{'data'}{'Offmod'}{$offset});
|
|
Packit |
95306a |
$self->_offmod($offset);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @z;
|
|
Packit |
95306a |
foreach my $isdst (@isdst) {
|
|
Packit |
95306a |
my $tmp = $$self{'data'}{'MyOffsets'}{$offset}{$isdst} ||
|
|
Packit |
95306a |
$$self{'data'}{'Offsets'}{$offset}{$isdst};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @tmp;
|
|
Packit |
95306a |
if ($abbrev) {
|
|
Packit |
95306a |
@tmp = $self->_check_offset_abbrev_isdst($offset,$abbrev,$isdst,$tmp);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@tmp = @$tmp if ($tmp);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (@tmp) {
|
|
Packit |
95306a |
if (@z) {
|
|
Packit |
95306a |
@z = _list_add(\@z,\@tmp);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@z = @tmp;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (@zone) {
|
|
Packit |
95306a |
@zone = _list_union(\@zone,\@z);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@zone = @z;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
last if (! @zone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $date
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (@$date) {
|
|
Packit |
95306a |
# Get all periods for the year.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Test all periods to make sure that $date is between the
|
|
Packit |
95306a |
# wallclock times AND matches other criteria. All periods
|
|
Packit |
95306a |
# must be tested since the same wallclock time can be in
|
|
Packit |
95306a |
# multiple periods.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @tmp;
|
|
Packit |
95306a |
my $isdst = '';
|
|
Packit |
95306a |
$isdst = 0 if ($dstflag eq 'stdonly');
|
|
Packit |
95306a |
$isdst = 1 if ($dstflag eq 'dstonly');
|
|
Packit |
95306a |
|
|
Packit |
95306a |
ZONE:
|
|
Packit |
95306a |
foreach my $z (@zone) {
|
|
Packit |
95306a |
$self->_module($z) if (! exists $$self{'data'}{'Zones'}{$z}{'Loaded'});
|
|
Packit |
95306a |
my $y = $$date[0];
|
|
Packit |
95306a |
my @periods = $self->_all_periods($z,$y);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $period (@periods) {
|
|
Packit |
95306a |
next if (($abbrev ne '' && lc($abbrev) ne lc($$period[4])) ||
|
|
Packit |
95306a |
($offset ne '' && $offset ne $$period[2]) ||
|
|
Packit |
95306a |
($isdst ne '' && $isdst ne $$period[5]) ||
|
|
Packit |
95306a |
$dmb->cmp($date,$$period[1]) == -1 ||
|
|
Packit |
95306a |
$dmb->cmp($date,$$period[7]) == 1
|
|
Packit |
95306a |
);
|
|
Packit |
95306a |
push(@tmp,$z);
|
|
Packit |
95306a |
next ZONE;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
@zone = @tmp;
|
|
Packit |
95306a |
last if (! @zone);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
last;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Return the value/list
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (wantarray) {
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
foreach my $z (@zone) {
|
|
Packit |
95306a |
push(@ret,$$self{'data'}{'ZoneNames'}{$z});
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return '' if (! @zone);
|
|
Packit |
95306a |
return $$self{'data'}{'ZoneNames'}{$zone[0]}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This returns a list of all timezones which have the correct
|
|
Packit |
95306a |
# abbrev/isdst combination.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _check_abbrev_isdst {
|
|
Packit |
95306a |
my($self,$abbrev,$isdst,@zones) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
ZONE:
|
|
Packit |
95306a |
foreach my $zone (@zones) {
|
|
Packit |
95306a |
$self->_module($zone) if (! exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $y (sort keys %{ $$self{'data'}{'Zones'}{$zone}{'Dates'} }) {
|
|
Packit |
95306a |
my @periods = @{ $$self{'data'}{'Zones'}{$zone}{'Dates'}{$y} };
|
|
Packit |
95306a |
foreach my $period (@periods) {
|
|
Packit |
95306a |
my($dateUT,$dateLT,$off,$offref,$abb,$dst,$endUT,$endLT) = @$period;
|
|
Packit |
95306a |
next if (lc($abbrev) ne lc($abb) ||
|
|
Packit |
95306a |
$isdst != $dst);
|
|
Packit |
95306a |
push(@ret,$zone);
|
|
Packit |
95306a |
next ZONE;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This returns a list of all timezones which have the correct
|
|
Packit |
95306a |
# abbrev/isdst combination.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _check_offset_abbrev_isdst {
|
|
Packit |
95306a |
my($self,$offset,$abbrev,$isdst,$zones) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
ZONE: foreach my $zone (@$zones) {
|
|
Packit |
95306a |
$self->_module($zone) if (! exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $y (sort keys %{ $$self{'data'}{'Zones'}{$zone}{'Dates'} }) {
|
|
Packit |
95306a |
my @periods = @{ $$self{'data'}{'Zones'}{$zone}{'Dates'}{$y} };
|
|
Packit |
95306a |
foreach my $period (@periods) {
|
|
Packit |
95306a |
my($dateUT,$dateLT,$off,$offref,$abb,$dst,$endUT,$endLT) = @$period;
|
|
Packit |
95306a |
next if (lc($abbrev) ne lc($abb) ||
|
|
Packit |
95306a |
$offset ne $off ||
|
|
Packit |
95306a |
$isdst != $dst);
|
|
Packit |
95306a |
push(@ret,$zone);
|
|
Packit |
95306a |
next ZONE;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This finds the elements common to two lists, and preserves the order
|
|
Packit |
95306a |
# from the first list.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _list_union {
|
|
Packit |
95306a |
my($list1,$list2) = @_;
|
|
Packit |
95306a |
my(%list2) = map { $_,1 } @$list2;
|
|
Packit |
95306a |
my(@ret);
|
|
Packit |
95306a |
foreach my $ele (@$list1) {
|
|
Packit |
95306a |
push(@ret,$ele) if (exists $list2{$ele});
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This adds elements from the second list to the first list, provided
|
|
Packit |
95306a |
# they are not already there.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _list_add {
|
|
Packit |
95306a |
my($list1,$list2) = @_;
|
|
Packit |
95306a |
my(%list1) = map { $_,1 } @$list1;
|
|
Packit |
95306a |
my(@ret) = @$list1;
|
|
Packit |
95306a |
foreach my $ele (@$list2) {
|
|
Packit |
95306a |
next if (exists $list1{$ele});
|
|
Packit |
95306a |
push(@ret,$ele);
|
|
Packit |
95306a |
$list1{$ele} = 1;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# PERIODS METHODS
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub all_periods {
|
|
Packit |
95306a |
my($self,$zone,$year) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $z = $self->_zone($zone);
|
|
Packit |
95306a |
if (! $z) {
|
|
Packit |
95306a |
warn "ERROR: [periods] Invalid zone: $zone\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$zone = $z;
|
|
Packit |
95306a |
$self->_module($zone) if (! exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Run a faster 'dclone' so we don't return the actual data.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @tmp = $self->_all_periods($zone,$year);
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
foreach my $ele (@tmp) {
|
|
Packit |
95306a |
push(@ret,
|
|
Packit |
95306a |
[ [ @{$$ele[0]} ],[ @{$$ele[1]} ],$$ele[2],[ @{$$ele[3]} ],$$ele[4],
|
|
Packit |
95306a |
$$ele[5], [ @{$$ele[6]} ],[ @{$$ele[7]} ],$$ele[8],$$ele[9],
|
|
Packit |
95306a |
$$ele[10],$$ele[11] ]);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _all_periods {
|
|
Packit |
95306a |
my($self,$zone,$year) = @_;
|
|
Packit |
95306a |
$year += 0;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! exists $$self{'data'}{'Zones'}{$zone}{'AllDates'}{$year}) {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# $ym1 is the year prior to $year which contains a rule (which will
|
|
Packit |
95306a |
# end in $year or later). $y is $year IF the zone contains rules
|
|
Packit |
95306a |
# for this year.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($ym1,$ym0);
|
|
Packit |
95306a |
if ($year > $$self{'data'}{'LastYear'} &&
|
|
Packit |
95306a |
exists $$self{'data'}{'Zones'}{$zone}{'LastRule'}{'zone'}) {
|
|
Packit |
95306a |
$ym1 = $year-1;
|
|
Packit |
95306a |
$ym0 = $year;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
foreach my $y (sort { $a <=> $b }
|
|
Packit |
95306a |
keys %{ $$self{'data'}{'Zones'}{$zone}{'Dates'} }) {
|
|
Packit |
95306a |
if ($y < $year) {
|
|
Packit |
95306a |
$ym1 = $y;
|
|
Packit |
95306a |
next;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$ym0 = $year if ($year == $y);
|
|
Packit |
95306a |
last;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$ym1 = 0 if (! $ym1);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Get the periods from the prior year. The last one is used (any others
|
|
Packit |
95306a |
# are discarded).
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my(@periods);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# $ym1 will be 0 in 0001
|
|
Packit |
95306a |
if ($ym1) {
|
|
Packit |
95306a |
my @tmp = $self->_periods($zone,$ym1);
|
|
Packit |
95306a |
push(@periods,pop(@tmp)) if (@tmp);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Add on any periods from the current year.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($ym0) {
|
|
Packit |
95306a |
push(@periods,$self->_periods($zone,$year));
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'AllDates'}{$year} = [ @periods ];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return @{ $$self{'data'}{'Zones'}{$zone}{'AllDates'}{$year} };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub periods {
|
|
Packit |
95306a |
my($self,$zone,$year,$year1) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $z = $self->_zone($zone);
|
|
Packit |
95306a |
if (! $z) {
|
|
Packit |
95306a |
warn "ERROR: [periods] Invalid zone: $zone\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$zone = $z;
|
|
Packit |
95306a |
$self->_module($zone) if (! exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! defined($year1)) {
|
|
Packit |
95306a |
return $self->_periods($zone,$year);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$year = 1 if (! defined($year));
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
my $lastyear = $$self{'data'}{'LastYear'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($year <= $lastyear) {
|
|
Packit |
95306a |
foreach my $y (sort { $a <=> $b }
|
|
Packit |
95306a |
keys %{ $$self{'data'}{'Zones'}{$zone}{'Dates'} }) {
|
|
Packit |
95306a |
last if ($y > $year1 || $y > $lastyear);
|
|
Packit |
95306a |
next if ($y < $year);
|
|
Packit |
95306a |
push(@ret,$self->_periods($zone,$y));
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($year1 > $lastyear) {
|
|
Packit |
95306a |
$year = $lastyear + 1 if ($year <= $lastyear);
|
|
Packit |
95306a |
foreach my $y ($year..$year1) {
|
|
Packit |
95306a |
push(@ret,$self->_periods($zone,$y));
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _periods {
|
|
Packit |
95306a |
my($self,$zone,$year) = @_;
|
|
Packit |
95306a |
$year += 0;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! exists $$self{'data'}{'Zones'}{$zone}{'Dates'}{$year}) {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @periods = ();
|
|
Packit |
95306a |
if ($year > $$self{'data'}{'LastYear'}) {
|
|
Packit |
95306a |
# Calculate periods using the LastRule method
|
|
Packit |
95306a |
@periods = $self->_lastrule($zone,$year);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'Dates'}{$year} = [ @periods ];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# A faster 'dclone' so we don't return the actual data
|
|
Packit |
95306a |
my @ret;
|
|
Packit |
95306a |
foreach my $ele (@{ $$self{'data'}{'Zones'}{$zone}{'Dates'}{$year} }) {
|
|
Packit |
95306a |
push(@ret,
|
|
Packit |
95306a |
[ [ @{$$ele[0]} ],[ @{$$ele[1]} ],$$ele[2],[ @{$$ele[3]} ],$$ele[4],$$ele[5],
|
|
Packit |
95306a |
[ @{$$ele[6]} ],[ @{$$ele[7]} ],$$ele[8],$$ele[9],$$ele[10],$$ele[11] ]);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
return @ret;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub date_period {
|
|
Packit |
95306a |
my($self,$date,$zone,$wallclock,$isdst) = @_;
|
|
Packit |
95306a |
$wallclock = 0 if (! $wallclock);
|
|
Packit |
95306a |
$isdst = 0 if (! $isdst);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $z = $self->_zone($zone);
|
|
Packit |
95306a |
if (! $z) {
|
|
Packit |
95306a |
warn "ERROR: [date_period] Invalid zone: $zone\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$zone = $z;
|
|
Packit |
95306a |
$self->_module($zone) if (! exists $$self{'data'}{'Zones'}{$zone}{'Loaded'});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
my @date = @$date;
|
|
Packit |
95306a |
my $year = $date[0];
|
|
Packit |
95306a |
my $dates= $dmb->_date_fields(@$date);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($wallclock) {
|
|
Packit |
95306a |
# A wallclock date
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @period = $self->_all_periods($zone,$year);
|
|
Packit |
95306a |
my $beg = $period[0]->[9];
|
|
Packit |
95306a |
my $end = $period[-1]->[11];
|
|
Packit |
95306a |
if (($dates cmp $beg) == -1) {
|
|
Packit |
95306a |
@period = $self->_all_periods($zone,$year-1);
|
|
Packit |
95306a |
} elsif (($dates cmp $end) == 1) {
|
|
Packit |
95306a |
@period = $self->_all_periods($zone,$year+1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my(@per);
|
|
Packit |
95306a |
foreach my $period (@period) {
|
|
Packit |
95306a |
my($begUT,$begLT,$offsetstr,$offset,$abbrev,$dst,$endUT,$endLT,
|
|
Packit |
95306a |
$begUTs,$begLTs,$endUTs,$endLTs) = @$period;
|
|
Packit |
95306a |
if (($dates cmp $begLTs) != -1 && ($dates cmp $endLTs) != 1) {
|
|
Packit |
95306a |
push(@per,$period);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($#per == -1) {
|
|
Packit |
95306a |
return ();
|
|
Packit |
95306a |
} elsif ($#per == 0) {
|
|
Packit |
95306a |
return $per[0];
|
|
Packit |
95306a |
} elsif ($#per == 1) {
|
|
Packit |
95306a |
if ($per[0][5] == $isdst) {
|
|
Packit |
95306a |
return $per[0];
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return $per[1];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
warn "ERROR: [date_period] Impossible error\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
# A GMT date
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @period = $self->_all_periods($zone,$year);
|
|
Packit |
95306a |
foreach my $period (@period) {
|
|
Packit |
95306a |
my($begUT,$begLT,$offsetstr,$offset,$abbrev,$isdst,$endUT,$endLT,
|
|
Packit |
95306a |
$begUTs,$begLTs,$endUTs,$endLTs) = @$period;
|
|
Packit |
95306a |
if (($dates cmp $begUTs) != -1 && ($dates cmp $endUTs) != 1) {
|
|
Packit |
95306a |
return $period;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
warn "ERROR: [date_period] Impossible error\n";
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Calculate critical dates from the last rule. If $endonly is passed
|
|
Packit |
95306a |
# in, it only calculates the ending of the zone period before the
|
|
Packit |
95306a |
# start of the first one. This is necessary so that the last period in
|
|
Packit |
95306a |
# one year can find out when it ends (which is determined in the
|
|
Packit |
95306a |
# following year).
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Returns:
|
|
Packit |
95306a |
# [begUT, begLT, offsetstr, offset, abb, ISDST, endUT, endLT,
|
|
Packit |
95306a |
# begUTstr, begLTstr, endUTstr, endLTstr]
|
|
Packit |
95306a |
# for each.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _lastrule {
|
|
Packit |
95306a |
my($self,$zone,$year,$endonly) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Get the list of rules (actually, the month in which the
|
|
Packit |
95306a |
# rule triggers a time change). If there are none, then
|
|
Packit |
95306a |
# this zone doesn't have a LAST RULE.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @mon = (sort keys
|
|
Packit |
95306a |
%{ $$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'} });
|
|
Packit |
95306a |
return () if (! @mon);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Analyze each time change.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @dates = ();
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $stdoff = $$self{'data'}{'Zones'}{$zone}{'LastRule'}{'zone'}{'stdoff'};
|
|
Packit |
95306a |
my $dstoff = $$self{'data'}{'Zones'}{$zone}{'LastRule'}{'zone'}{'dstoff'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my (@period);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $mon (@mon) {
|
|
Packit |
95306a |
my $flag =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'flag'};
|
|
Packit |
95306a |
my $dow =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'dow'};
|
|
Packit |
95306a |
my $num =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'num'};
|
|
Packit |
95306a |
my $isdst=
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'isdst'};
|
|
Packit |
95306a |
my $time =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'time'};
|
|
Packit |
95306a |
my $type =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'type'};
|
|
Packit |
95306a |
my $abb =
|
|
Packit |
95306a |
$$self{'data'}{'Zones'}{$zone}{'LastRule'}{'rules'}{$mon}{'abb'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# The end of the current period and the beginning of the next
|
|
Packit |
95306a |
my($endUT,$endLT,$begUT,$begLT) =
|
|
Packit |
95306a |
$dmb->_critical_date($year,$mon,$flag,$num,$dow,
|
|
Packit |
95306a |
$isdst,$time,$type,$stdoff,$dstoff);
|
|
Packit |
95306a |
return ($endUT,$endLT) if ($endonly);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (@period) {
|
|
Packit |
95306a |
push(@period,$endUT,$endLT);
|
|
Packit |
95306a |
push(@dates,[@period]);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $offsetstr = ($isdst ? $dstoff : $stdoff);
|
|
Packit |
95306a |
my $offset = $dmb->split('offset',$offsetstr);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
@period = ($begUT,$begLT,$offsetstr,$offset,$abb,$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
push(@period,$self->_lastrule($zone,$year+1,1));
|
|
Packit |
95306a |
push(@dates,[@period]);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
foreach my $period (@dates) {
|
|
Packit |
95306a |
my($begUT,$begLT,$offsetstr,$offset,$abbrev,$dst,$endUT,$endLT) = @$period;
|
|
Packit |
95306a |
my $begUTstr = $dmb->join("date",$begUT);
|
|
Packit |
95306a |
my $begLTstr = $dmb->join("date",$begLT);
|
|
Packit |
95306a |
my $endUTstr = $dmb->join("date",$endUT);
|
|
Packit |
95306a |
my $endLTstr = $dmb->join("date",$endLT);
|
|
Packit |
95306a |
$period = [$begUT,$begLT,$offsetstr,$offset,$abbrev,$dst,$endUT,$endLT,
|
|
Packit |
95306a |
$begUTstr,$begLTstr,$endUTstr,$endLTstr];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return @dates;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# CONVERSION
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub convert {
|
|
Packit |
95306a |
my($self,$date,$from,$to,$isdst) = @_;
|
|
Packit |
95306a |
$self->_convert('convert',$date,$from,$to,$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub convert_to_gmt {
|
|
Packit |
95306a |
my($self,$date,@arg) = @_;
|
|
Packit |
95306a |
my($err,$from,$isdst) = _convert_args('convert_to_gmt',@arg);
|
|
Packit |
95306a |
return (1) if ($err);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $from) {
|
|
Packit |
95306a |
$from = $self->_now('tz',1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$self->_convert('convert_to_gmt',$date,$from,'GMT',$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub convert_from_gmt {
|
|
Packit |
95306a |
my($self,$date,@arg) = @_;
|
|
Packit |
95306a |
my($err,$to,$isdst) = _convert_args('convert_from_gmt',@arg);
|
|
Packit |
95306a |
return (1) if ($err);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $to) {
|
|
Packit |
95306a |
$to = $self->_now('tz',1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$self->_convert('convert_from_gmt',$date,'GMT',$to,$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub convert_to_local {
|
|
Packit |
95306a |
my($self,$date,@arg) = @_;
|
|
Packit |
95306a |
my($err,$from,$isdst) = _convert_args('convert_to_local',@arg);
|
|
Packit |
95306a |
return (1) if ($err);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $from) {
|
|
Packit |
95306a |
$from = 'GMT';
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$self->_convert('convert_to_local',$date,$from,$self->_now('tz',1),$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub convert_from_local {
|
|
Packit |
95306a |
my($self,$date,@arg) = @_;
|
|
Packit |
95306a |
my($err,$to,$isdst) = _convert_args('convert_from_local',@arg);
|
|
Packit |
95306a |
return (1) if ($err);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $to) {
|
|
Packit |
95306a |
$to = 'GMT';
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$self->_convert('convert_from_local',$date,$self->_now('tz',1),$to,$isdst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _convert_args {
|
|
Packit |
95306a |
my($caller,@args) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($#args == -1) {
|
|
Packit |
95306a |
return (0,'',0);
|
|
Packit |
95306a |
} elsif ($#args == 0) {
|
|
Packit |
95306a |
if ($args[0] eq '0' ||
|
|
Packit |
95306a |
$args[0] eq '1') {
|
|
Packit |
95306a |
return (0,'',$args[0]);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return (0,$args[0],0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
} elsif ($#args == 1) {
|
|
Packit |
95306a |
return (0,@args);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
return (1,'',0);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _convert {
|
|
Packit |
95306a |
my($self,$caller,$date,$from,$to,$isdst) = @_;
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Handle $date as a reference and a string
|
|
Packit |
95306a |
my (@date);
|
|
Packit |
95306a |
if (ref($date)) {
|
|
Packit |
95306a |
@date = @$date;
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@date = @{ $dmb->split('date',$date) };
|
|
Packit |
95306a |
$date = [@date];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($from ne $to) {
|
|
Packit |
95306a |
my $tmp = $self->_zone($from);
|
|
Packit |
95306a |
if (! $tmp) {
|
|
Packit |
95306a |
return (2);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$from = $tmp;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$tmp = $self->_zone($to);
|
|
Packit |
95306a |
if (! $tmp) {
|
|
Packit |
95306a |
return (3);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$to = $tmp;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($from eq $to) {
|
|
Packit |
95306a |
my $per = $self->date_period($date,$from,1,$isdst);
|
|
Packit |
95306a |
my $offset = $$per[3];
|
|
Packit |
95306a |
my $abb = $$per[4];
|
|
Packit |
95306a |
return (0,$date,$offset,$isdst,$abb);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Convert $date from $from to GMT
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($from ne "Etc/GMT") {
|
|
Packit |
95306a |
my $per = $self->date_period($date,$from,1,$isdst);
|
|
Packit |
95306a |
if (! $per) {
|
|
Packit |
95306a |
return (4);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
my $offset = $$per[3];
|
|
Packit |
95306a |
@date = @{ $dmb->calc_date_time(\@date,$offset,1) };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Convert $date from GMT to $to
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$isdst = 0;
|
|
Packit |
95306a |
my $offset = [0,0,0];
|
|
Packit |
95306a |
my $abb = 'GMT';
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($to ne "Etc/GMT") {
|
|
Packit |
95306a |
my $per = $self->date_period([@date],$to,0);
|
|
Packit |
95306a |
$offset = $$per[3];
|
|
Packit |
95306a |
$isdst = $$per[5];
|
|
Packit |
95306a |
$abb = $$per[4];
|
|
Packit |
95306a |
@date = @{ $dmb->calc_date_time(\@date,$offset) };
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return (0,[@date],$offset,$isdst,$abb);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# REGULAR EXPRESSIONS FOR TIMEZONE INFORMATION
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Returns regular expressions capable of matching timezones.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# The timezone regular expressions are:
|
|
Packit |
95306a |
# namerx : this will match a zone name or alias (America/New_York)
|
|
Packit |
95306a |
# abbrx : this will match a zone abbreviation (EDT)
|
|
Packit |
95306a |
# zonerx : this will match a zone name or an abbreviation
|
|
Packit |
95306a |
# offrx : this will match a pure offset (+0400)
|
|
Packit |
95306a |
# offabbrx : this will match an offset with an abbreviation (+0400 WET)
|
|
Packit |
95306a |
# offparrx : this will match an offset and abbreviation if parentheses
|
|
Packit |
95306a |
# ("+0400 (WET)")
|
|
Packit |
95306a |
# zrx : this will match all forms
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# The regular expression will have the following named matches:
|
|
Packit |
95306a |
# tzstring : the full string matched
|
|
Packit |
95306a |
# zone : the name/alias
|
|
Packit |
95306a |
# abb : the zone abbrevation
|
|
Packit |
95306a |
# off : the offset
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _zrx {
|
|
Packit |
95306a |
my($self,$re) = @_;
|
|
Packit |
95306a |
return $$self{'data'}{$re} if (defined $$self{'data'}{$re});
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Zone name
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @zone;
|
|
Packit |
95306a |
if (exists $ENV{'DATE_MANIP_DEBUG_ZONES'}) {
|
|
Packit |
95306a |
@zone = split(/\s+/,$ENV{'DATE_MANIP_DEBUG_ZONES'});
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@zone = (keys %{ $$self{'data'}{'Alias'} },
|
|
Packit |
95306a |
keys %{ $$self{'data'}{'MyAlias'} });
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
@zone = sort _sortByLength(@zone);
|
|
Packit |
95306a |
foreach my $zone (@zone) {
|
|
Packit |
95306a |
$zone =~ s/\057/\\057/g; # /
|
|
Packit |
95306a |
$zone =~ s/\055/\\055/g; # -
|
|
Packit |
95306a |
$zone =~ s/\056/\\056/g; # .
|
|
Packit |
95306a |
$zone =~ s/\050/\\050/g; # (
|
|
Packit |
95306a |
$zone =~ s/\051/\\051/g; # )
|
|
Packit |
95306a |
$zone =~ s/\053/\\053/g; # +
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $zone = join('|',@zone);
|
|
Packit |
95306a |
$zone = qr/(?<zone>$zone)/i;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Abbreviation
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my @abb;
|
|
Packit |
95306a |
if (exists $ENV{'DATE_MANIP_DEBUG_ABBREVS'}) {
|
|
Packit |
95306a |
@abb = split(/\s+/,$ENV{'DATE_MANIP_DEBUG_ABBREVS'});
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@abb = (keys %{ $$self{'data'}{'Abbrev'} },
|
|
Packit |
95306a |
keys %{ $$self{'data'}{'MyAbbrev'} });
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
@abb = sort _sortByLength(@abb);
|
|
Packit |
95306a |
foreach my $abb (@abb) {
|
|
Packit |
95306a |
$abb =~ s/\055/\\055/g; # -
|
|
Packit |
95306a |
$abb =~ s/\053/\\053/g; # +
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $abb = join('|',@abb);
|
|
Packit |
95306a |
$abb = qr/(?<abb>$abb)/i;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Offset (+HH, +HHMM, +HH:MM, +HH:MM:SS, +HHMMSS)
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($hr) = qr/(?:[0-1][0-9]|2[0-3])/; # 00 - 23
|
|
Packit |
95306a |
my($mn) = qr/(?:[0-5][0-9])/; # 00 - 59
|
|
Packit |
95306a |
my($ss) = qr/(?:[0-5][0-9])/; # 00 - 59
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($off) = qr/ (?<off> [+-] (?: $hr:$mn:$ss |
|
|
Packit |
95306a |
$hr$mn$ss |
|
|
Packit |
95306a |
$hr:?$mn |
|
|
Packit |
95306a |
$hr
|
|
Packit |
95306a |
)
|
|
Packit |
95306a |
) /ix;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Assemble everything
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# A timezone can be any of the following in this order:
|
|
Packit |
95306a |
# Offset (ABB)
|
|
Packit |
95306a |
# Offset ABB
|
|
Packit |
95306a |
# ABB
|
|
Packit |
95306a |
# Zone
|
|
Packit |
95306a |
# Offset
|
|
Packit |
95306a |
# We put ABB before Zone so CET gets parse as the more common abbreviation
|
|
Packit |
95306a |
# than the less common zone name.
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$self{'data'}{'namerx'} = qr/(?<tzstring>$zone)/;
|
|
Packit |
95306a |
$$self{'data'}{'abbrx'} = qr/(?<tzstring>$abb)/;
|
|
Packit |
95306a |
$$self{'data'}{'zonerx'} = qr/(?<tzstring>(?:$abb|$zone))/;
|
|
Packit |
95306a |
$$self{'data'}{'offrx'} = qr/(?<tzstring>$off)/;
|
|
Packit |
95306a |
$$self{'data'}{'offabbrx'} = qr/(?<tzstring>$off\s+$abb)/;
|
|
Packit |
95306a |
$$self{'data'}{'offparrx'} = qr/(?<tzstring>$off\s*\($abb\))/;
|
|
Packit |
95306a |
$$self{'data'}{'zrx'} = qr/(?<tzstring>(?:$off\s*\($abb\)|$off\s+$abb|$abb|$zone|$off))/;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return $$self{'data'}{$re};
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This sorts from longest to shortest element
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
no strict 'vars';
|
|
Packit |
95306a |
sub _sortByLength {
|
|
Packit |
95306a |
return (length $b <=> length $a);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
use strict 'vars';
|
|
Packit |
95306a |
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
# CONFIG VARS
|
|
Packit |
95306a |
########################################################################
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# This sets a config variable. It also performs all side effects from
|
|
Packit |
95306a |
# setting that variable.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
sub _config_var_tz {
|
|
Packit |
95306a |
my($self,$var,$val) = @_;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($var eq 'tz') {
|
|
Packit |
95306a |
my $err = $self->_config_var_setdate("now,$val",0);
|
|
Packit |
95306a |
return if ($err);
|
|
Packit |
95306a |
$$self{'data'}{'sections'}{'conf'}{'forcedate'} = 0;
|
|
Packit |
95306a |
$val = 1;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($var eq 'setdate') {
|
|
Packit |
95306a |
my $err = $self->_config_var_setdate($val,0);
|
|
Packit |
95306a |
return if ($err);
|
|
Packit |
95306a |
$$self{'data'}{'sections'}{'conf'}{'forcedate'} = 0;
|
|
Packit |
95306a |
$val = 1;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($var eq 'forcedate') {
|
|
Packit |
95306a |
my $err = $self->_config_var_setdate($val,1);
|
|
Packit |
95306a |
return if ($err);
|
|
Packit |
95306a |
$$self{'data'}{'sections'}{'conf'}{'setdate'} = 0;
|
|
Packit |
95306a |
$val = 1;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($var eq 'configfile') {
|
|
Packit |
95306a |
$self->_config_file($val);
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $base = $$self{'base'};
|
|
Packit |
95306a |
$$base{'data'}{'sections'}{'conf'}{$var} = $val;
|
|
Packit |
95306a |
return;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
sub _config_var_setdate {
|
|
Packit |
95306a |
my($self,$val,$force) = @_;
|
|
Packit |
95306a |
my $base = $$self{'base'};
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $dstrx = qr/(?:,\s*(stdonly|dstonly|std|dst))?/i;
|
|
Packit |
95306a |
my $zonrx = qr/,\s*(.+)/;
|
|
Packit |
95306a |
my $da1rx = qr/(\d\d\d\d)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)/;
|
|
Packit |
95306a |
my $da2rx = qr/(\d\d\d\d)\-(\d\d)\-(\d\d)\-(\d\d):(\d\d):(\d\d)/;
|
|
Packit |
95306a |
my $time = time;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($op,$date,$dstflag,$zone,@date,$offset,$abb);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Parse the argument
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($val =~ /^now${dstrx}${zonrx}$/oi) {
|
|
Packit |
95306a |
# now,ZONE
|
|
Packit |
95306a |
# now,DSTFLAG,ZONE
|
|
Packit |
95306a |
# Sets now to the system date/time but sets the timezone to be ZONE
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$op = 'nowzone';
|
|
Packit |
95306a |
($dstflag,$zone) = ($1,$2);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($val =~ /^zone${dstrx}${zonrx}$/oi) {
|
|
Packit |
95306a |
# zone,ZONE
|
|
Packit |
95306a |
# zone,DSTFLAG,ZONE
|
|
Packit |
95306a |
# Converts 'now' to the alternate zone
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$op = 'zone';
|
|
Packit |
95306a |
($dstflag,$zone) = ($1,$2);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($val =~ /^${da1rx}${dstrx}${zonrx}$/o ||
|
|
Packit |
95306a |
$val =~ /^${da2rx}${dstrx}${zonrx}$/o) {
|
|
Packit |
95306a |
# DATE,ZONE
|
|
Packit |
95306a |
# DATE,DSTFLAG,ZONE
|
|
Packit |
95306a |
# Sets the date and zone
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$op = 'datezone';
|
|
Packit |
95306a |
my($y,$m,$d,$h,$mn,$s);
|
|
Packit |
95306a |
($y,$m,$d,$h,$mn,$s,$dstflag,$zone) = ($1,$2,$3,$4,$5,$6,$7,$8);
|
|
Packit |
95306a |
$date = [$y,$m,$d,$h,$mn,$s];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($val =~ /^${da1rx}$/o ||
|
|
Packit |
95306a |
$val =~ /^${da2rx}$/o) {
|
|
Packit |
95306a |
# DATE
|
|
Packit |
95306a |
# Sets the date in the system timezone
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$op = 'date';
|
|
Packit |
95306a |
my($y,$m,$d,$h,$mn,$s) = ($1,$2,$3,$4,$5,$6);
|
|
Packit |
95306a |
$date = [$y,$m,$d,$h,$mn,$s];
|
|
Packit |
95306a |
$zone = $self->_now('systz',1);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif (lc($val) eq 'now') {
|
|
Packit |
95306a |
# now
|
|
Packit |
95306a |
# Resets everything
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my $systz = $$base{'data'}{'now'}{'systz'};
|
|
Packit |
95306a |
$base->_init_now();
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'systz'} = $systz;
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
warn "ERROR: [config_var] invalid SetDate/ForceDate value: $val\n";
|
|
Packit |
95306a |
return 1;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$dstflag = 'std' if (! $dstflag);
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Get the date we're setting 'now' to
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($op eq 'nowzone') {
|
|
Packit |
95306a |
# Use the system localtime
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($s,$mn,$h,$d,$m,$y) = localtime($time);
|
|
Packit |
95306a |
$y += 1900;
|
|
Packit |
95306a |
$m++;
|
|
Packit |
95306a |
$date = [$y,$m,$d,$h,$mn,$s];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($op eq 'zone') {
|
|
Packit |
95306a |
# Use the system GMT time
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($s,$mn,$h,$d,$m,$y) = gmtime($time);
|
|
Packit |
95306a |
$y += 1900;
|
|
Packit |
95306a |
$m++;
|
|
Packit |
95306a |
$date = [$y,$m,$d,$h,$mn,$s];
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Find out what zone was passed in. It can be an alias or an offset.
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($zone) {
|
|
Packit |
95306a |
my ($err,@args);
|
|
Packit |
95306a |
my $dmb = $$self{'base'};
|
|
Packit |
95306a |
$date = [] if (! defined $date);
|
|
Packit |
95306a |
$zone = $self->__zone($date,'',lc($zone),'',lc($dstflag));
|
|
Packit |
95306a |
if (! $zone) {
|
|
Packit |
95306a |
warn "ERROR: [config_var] invalid zone in SetDate: @args\n";
|
|
Packit |
95306a |
return 1;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
$zone = $$base{'data'}{'now'}{'systz'};
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Handle the zone
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
my($isdst,@isdst);
|
|
Packit |
95306a |
if ($dstflag eq 'std') {
|
|
Packit |
95306a |
@isdst = (0,1);
|
|
Packit |
95306a |
} elsif ($dstflag eq 'stdonly') {
|
|
Packit |
95306a |
@isdst = (0);
|
|
Packit |
95306a |
} elsif ($dstflag eq 'dst') {
|
|
Packit |
95306a |
@isdst = (1,0);
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
@isdst = (1);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($op eq 'nowzone' ||
|
|
Packit |
95306a |
$op eq 'datezone' ||
|
|
Packit |
95306a |
$op eq 'date') {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Check to make sure that the date can exist in this zone.
|
|
Packit |
95306a |
my $per;
|
|
Packit |
95306a |
foreach my $dst (@isdst) {
|
|
Packit |
95306a |
next if ($per);
|
|
Packit |
95306a |
$per = $self->date_period($date,$zone,1,$dst);
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if (! $per) {
|
|
Packit |
95306a |
warn "ERROR: [config_var] invalid date: SetDate: $date, $zone\n";
|
|
Packit |
95306a |
return 1;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
$isdst = $$per[5];
|
|
Packit |
95306a |
$abb = $$per[4];
|
|
Packit |
95306a |
$offset = $$per[3];
|
|
Packit |
95306a |
|
|
Packit |
95306a |
} elsif ($op eq 'zone') {
|
|
Packit |
95306a |
|
|
Packit |
95306a |
# Convert to that zone
|
|
Packit |
95306a |
my($err);
|
|
Packit |
95306a |
($err,$date,$offset,$isdst,$abb) = $self->convert_from_gmt($date,$zone);
|
|
Packit |
95306a |
if ($err) {
|
|
Packit |
95306a |
warn "ERROR: [config_var] invalid SetDate date/offset values: $date, $zone\n";
|
|
Packit |
95306a |
return 1;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Set NOW
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'date'} = $date;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'tz'} = $self->_zone($zone);
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'isdst'} = $isdst;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'abb'} = $abb;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'offset'} = $offset;
|
|
Packit |
95306a |
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
# Treate SetDate/ForceDate
|
|
Packit |
95306a |
#
|
|
Packit |
95306a |
|
|
Packit |
95306a |
if ($force) {
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'force'} = 1;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'set'} = 0;
|
|
Packit |
95306a |
} else {
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'force'} = 0;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'set'} = 1;
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'setsecs'} = $time;
|
|
Packit |
95306a |
my($err,$setdate) = $self->convert_to_gmt($date,$zone);
|
|
Packit |
95306a |
$$base{'data'}{'now'}{'setdate'} = $setdate;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
return 0;
|
|
Packit |
95306a |
}
|
|
Packit |
95306a |
|
|
Packit |
95306a |
1;
|
|
Packit |
95306a |
# Local Variables:
|
|
Packit |
95306a |
# mode: cperl
|
|
Packit |
95306a |
# indent-tabs-mode: nil
|
|
Packit |
95306a |
# cperl-indent-level: 3
|
|
Packit |
95306a |
# cperl-continued-statement-offset: 2
|
|
Packit |
95306a |
# cperl-continued-brace-offset: 0
|
|
Packit |
95306a |
# cperl-brace-offset: 0
|
|
Packit |
95306a |
# cperl-brace-imaginary-offset: 0
|
|
Packit |
95306a |
# cperl-label-offset: 0
|
|
Packit |
95306a |
# End:
|