Blame lib/Date/Manip/TZ.pm

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: