Blame internal/tzdata

Packit 95306a
#!/usr/bin/perl -w
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
###############################################################################
Packit 95306a
# This script is used to automatically generate the Date::Manip::Zones
Packit 95306a
# and Date::Manip::TZ::_ZONE_ modules from the original time zone data.
Packit 95306a
Packit 95306a
use lib "./lib";
Packit 95306a
use lib "./internal";
Packit 95306a
Packit 95306a
require 5.010000;
Packit 95306a
use YAML;
Packit 95306a
use IO::File;
Packit 95306a
use Date::Manip::Base;
Packit 95306a
use Date::Manip::TZdata;
Packit 95306a
use strict;
Packit 95306a
use warnings;
Packit 95306a
Packit 95306a
our $VERSION;
Packit 95306a
$VERSION='6.60';
Packit 95306a
Packit 95306a
our ($dmb);
Packit 95306a
$dmb = new Date::Manip::Base;
Packit 95306a
Packit 95306a
our $curry = ( localtime(time) )[5] + 1900;
Packit 95306a
Packit 95306a
##############################################################################
Packit 95306a
# GLOBAL VARIABLES
Packit 95306a
###############################################################################
Packit 95306a
Packit 95306a
our ($first_date,$last_date,$tzdata_src,$tzdata_dir,$tzdata_data,$tzdata_code,
Packit 95306a
     $mod_dir,$off_dir,
Packit 95306a
     $curr_year,$keep_year,$test_year,$zones_pm,$zones_pod,
Packit 95306a
     %def_off,%nontzdata_zones,%def_alias2,%def_abbrev,%no_last,
Packit 95306a
     %last_zone_offsets
Packit 95306a
    );
Packit 95306a
Packit 95306a
# The first and last dates (UT) known by this module (everything in the
Packit 95306a
# 0001 - 9999 range except for the first and last 24 hours of that range).
Packit 95306a
Packit 95306a
$first_date = "0001010200:00:00";
Packit 95306a
$last_date  = "9999123100:00:00";
Packit 95306a
Packit 95306a
# The source for the tzdata/tzcode files:
Packit 95306a
Packit 95306a
$tzdata_src = "ftp.iana.org";
Packit 95306a
$tzdata_dir = "tz";
Packit 95306a
$tzdata_data= "tzdata-latest.tar.gz";
Packit 95306a
$tzdata_code= "tzcode-latest.tar.gz";
Packit 95306a
Packit 95306a
Packit 95306a
require "data.offset.pl";
Packit 95306a
require "data.abbrev.pl";
Packit 95306a
require "data.alias.pl";
Packit 95306a
require "data.misc.pl";
Packit 95306a
Packit 95306a
# so the CPAN indexer won't treat this as a POD file
Packit 95306a
our $podstr = '=pod';
Packit 95306a
our $hdstr  = '=head1';
Packit 95306a
Packit 95306a
###############################################################################
Packit 95306a
# HELP
Packit 95306a
###############################################################################
Packit 95306a
Packit 95306a
our ($usage);
Packit 95306a
my $COM = $0;
Packit 95306a
$COM =~ s/^.*\///;
Packit 95306a
Packit 95306a
$usage=
Packit 95306a
  "usage: $COM OPTIONS
Packit 95306a
      -h/--help       : Print help.
Packit 95306a
      -v/--verbose    : Increasing levels of verbosity
Packit 95306a
Packit 95306a
      -a/--all        : Do all steps
Packit 95306a
Packit 95306a
      -f/--ftp        : Download the tzdata/tzcode files from
Packit 95306a
                        the source and build the tools
Packit 95306a
      -l/--list       : Get a list of all time zones to dump
Packit 95306a
      -d/--dump       : This dumps out zone info for all of
Packit 95306a
                        the zones
Packit 95306a
      -m/--mods       : This creates the modules from the dumps
Packit 95306a
      -o/--offset     : Creates the offset modules
Packit 95306a
      -z/--zones      : Create the zones module
Packit 95306a
      -c/--clean      : Removes tzdata files
Packit 95306a
";
Packit 95306a
Packit 95306a
###############################################################################
Packit 95306a
# PARSE ARGUMENTS
Packit 95306a
###############################################################################
Packit 95306a
Packit 95306a
our ($verbose);
Packit 95306a
$verbose     = 0;
Packit 95306a
my $do_all   = 0;
Packit 95306a
my $do_ftp   = 0;
Packit 95306a
my $do_build = 0;
Packit 95306a
my $do_list  = 0;
Packit 95306a
my $do_dump  = 0;
Packit 95306a
my $do_mods  = 0;
Packit 95306a
my $do_off   = 0;
Packit 95306a
my $do_zones = 0;
Packit 95306a
my $do_clean = 0;
Packit 95306a
Packit 95306a
while ($_ = shift) {
Packit 95306a
Packit 95306a
   (print $usage),   exit  if ($_ eq "-h"   ||  $_ eq "--help");
Packit 95306a
   $verbose = 1,     next  if ($_ eq "-v"   ||  $_ eq "--verbose");
Packit 95306a
Packit 95306a
   $do_all = 1,      next  if ($_ eq "-a"   ||  $_ eq "--all");
Packit 95306a
Packit 95306a
   $do_ftp = 1,      next  if ($_ eq "-f"   ||  $_ eq "--ftp");
Packit 95306a
   $do_build = 1,    next  if ($_ eq "-b"   ||  $_ eq "--build");
Packit 95306a
   $do_list = 1,     next  if ($_ eq "-l"   ||  $_ eq "--list");
Packit 95306a
   $do_dump = 1,     next  if ($_ eq "-d"   ||  $_ eq "--dump");
Packit 95306a
   $do_mods = 1,     next  if ($_ eq "-m"   ||  $_ eq "--mods");
Packit 95306a
   $do_off = 1,      next  if ($_ eq "-o"   ||  $_ eq "--offset");
Packit 95306a
   $do_zones = 1,    next  if ($_ eq "-z"   ||  $_ eq "--zones");
Packit 95306a
   $do_clean = 1,    next  if ($_ eq "-c"   ||  $_ eq "--clean");
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# MAIN PROGRAM
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
do_ftp()    if ($do_all  ||  $do_ftp);
Packit 95306a
do_build()  if ($do_all  ||  $do_build);
Packit 95306a
do_list()   if ($do_all  ||  $do_list);
Packit 95306a
do_dump()   if ($do_all  ||  $do_dump);
Packit 95306a
do_mods()   if ($do_all  ||  $do_mods);
Packit 95306a
do_off()    if ($do_all  ||  $do_off);
Packit 95306a
do_zones()  if ($do_all  ||  $do_zones);
Packit 95306a
do_clean()  if (             $do_clean);
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_FTP
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
# FTP the tzdata/tzcode packages
Packit 95306a
#
Packit 95306a
sub do_ftp {
Packit 95306a
   print "FTP...\n";
Packit 95306a
Packit 95306a
   system("rm -rf tzdata; mkdir tzdata");
Packit 95306a
   chdir("tzdata");
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Get the tz*latest.tar.gz links to determine the versions
Packit 95306a
   #
Packit 95306a
Packit 95306a
   system("wget -q 'ftp://$tzdata_src/$tzdata_dir/$tzdata_data' " .
Packit 95306a
          "'ftp://$tzdata_src/$tzdata_dir/$tzdata_code'");
Packit 95306a
   if (! -f $tzdata_data) {
Packit 95306a
      die "ERROR: unable to ftp data.  Try again later.";
Packit 95306a
   }
Packit 95306a
   if (! -f $tzdata_code) {
Packit 95306a
      die "ERROR: unable to ftp code.  Try again later.";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   system("tar xzf $tzdata_data");
Packit 95306a
   my $tzdata_vers = _release('data');
Packit 95306a
Packit 95306a
   system("tar xzf $tzdata_code");
Packit 95306a
   my $tzcode_vers = _release('code');
Packit 95306a
Packit 95306a
   print "  TZdata : $tzdata_vers\n";
Packit 95306a
   print "  Tzcode : $tzcode_vers\n";
Packit 95306a
Packit 95306a
   system("echo $tzdata_vers > _version; " .
Packit 95306a
          "echo $tzcode_vers >> _version; ");
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _release {
Packit 95306a
   my($type) = @_;
Packit 95306a
   if (! -f 'NEWS') {
Packit 95306a
      die "ERROR: unable to determine version (no NEWS): $type\n";
Packit 95306a
   }
Packit 95306a
   my $vers = `grep Release NEWS | head -1 | awk '{print \$2}'`;
Packit 95306a
   chomp($vers);
Packit 95306a
   if ($vers !~ /^\d\d\d\d[a-z]$/) {
Packit 95306a
      die "ERROR: uknown version format: $type: $vers\n";
Packit 95306a
   }
Packit 95306a
   system("mv NEWS NEWS.$type");
Packit 95306a
   return $vers;
Packit 95306a
}
Packit 95306a
Packit 95306a
# Build the package
Packit 95306a
#
Packit 95306a
sub do_build {
Packit 95306a
   print "Build...\n";
Packit 95306a
Packit 95306a
   system("cd tzdata; " .
Packit 95306a
          "touch NEWS; " .
Packit 95306a
          "make TOPDIR=./tmp INSTALL;");
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_LIST
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
# Get a list of all zones in the tzdata files which we will create
Packit 95306a
# modules for. Store a list of them and the associated module name.
Packit 95306a
#
Packit 95306a
# Stored in: _zone
Packit 95306a
#
Packit 95306a
sub do_list {
Packit 95306a
   print "List...\n";
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Get a list of zones from all Zone lines in the standard files in
Packit 95306a
   # the tzdata package.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   my(@zone);
Packit 95306a
Packit 95306a
   foreach my $file (@Date::Manip::TZdata::StdFiles) {
Packit 95306a
      my @tmp = `grep '^Zone' tzdata/$file | awk '{print \$2}'`;
Packit 95306a
      chomp(@tmp);
Packit 95306a
      push(@zone,@tmp);
Packit 95306a
   }
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Generate a module name for every zone (excepting some which
Packit 95306a
   # we're ignoring, or creating in other ways).
Packit 95306a
   #
Packit 95306a
Packit 95306a
   my %module  = ();
Packit 95306a
   my %modname = ();
Packit 95306a
   my %alias   = ();
Packit 95306a
   foreach my $zone (sort @zone) {
Packit 95306a
      next  if (exists $nontzdata_zones{$zone}  ||
Packit 95306a
                exists $def_alias2{$zone});
Packit 95306a
      my $module        = _do_list_modname(\%modname,$zone);
Packit 95306a
      $module{$zone}    = [ $module, "tzdata" ];
Packit 95306a
      $alias{$zone}     = [ $zone, "tzdata" ];
Packit 95306a
   }
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Generate a module name for every zone which is created as
Packit 95306a
   # an offset (e.g. GMT-3).
Packit 95306a
   #
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %nontzdata_zones) {
Packit 95306a
      my($type,$val) = @{ $nontzdata_zones{$zone} };
Packit 95306a
      if ($type eq "offset") {
Packit 95306a
         my $module        = _do_list_modname(\%modname,$zone);
Packit 95306a
         $module{$zone}    = [ $module, "offset", $val ];
Packit 95306a
         $alias{$zone}     = [ $zone, "offset" ];
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Handle all other special cases such as special aliases and
Packit 95306a
   # ignored zones.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %nontzdata_zones) {
Packit 95306a
      my($type,$val) = @{ $nontzdata_zones{$zone} };
Packit 95306a
      if ($type eq "offset") {
Packit 95306a
         next;
Packit 95306a
      } elsif ($type eq "alias") {
Packit 95306a
         warn "[do_list] unknown alias [$zone: $val]\n"
Packit 95306a
           if (! exists $module{$val});
Packit 95306a
         $alias{$zone}     = [ $val, $type ];
Packit 95306a
      } elsif ($type eq "ignore") {
Packit 95306a
         $alias{$zone}     = [ $val, $type ];
Packit 95306a
      } else {
Packit 95306a
         warn "[do_list] unknown type [$zone: $type]\n";
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Write out the official list of zones and aliases.
Packit 95306a
Packit 95306a
   _yaml_write(\%module,"tzdata/_zone_list",0);
Packit 95306a
   _yaml_write(\%alias, "tzdata/_alias_list",0);
Packit 95306a
}
Packit 95306a
Packit 95306a
# Takes a hashref $module{MODNAME} = ZONE and a zone and comes up
Packit 95306a
# with a unique module name for it. It returns the name of the module
Packit 95306a
# (as well as adds it to the hash).
Packit 95306a
#
Packit 95306a
sub _do_list_modname {
Packit 95306a
   my($modnames,$zone) = @_;
Packit 95306a
Packit 95306a
   my $modname = "";
Packit 95306a
   if ($zone =~ /\//) {
Packit 95306a
      my @tmp = split(/\//,$zone);
Packit 95306a
      $modname = substr($tmp[0],0,2) . substr($tmp[$#tmp],0,4);
Packit 95306a
   } else {
Packit 95306a
      $modname = substr($zone,0,6);
Packit 95306a
   }
Packit 95306a
   $modname =~ s/\-/m/g;
Packit 95306a
   $modname =~ s/\+/p/g;
Packit 95306a
Packit 95306a
   my $i = "00";
Packit 95306a
   while (exists $$modnames{"$modname$i"}) {
Packit 95306a
      $i++;
Packit 95306a
   }
Packit 95306a
   $modname .= $i;
Packit 95306a
   $$modnames{$modname} = 1;
Packit 95306a
   return lc($modname);
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_DUMP
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
# Dump every zone.
Packit 95306a
#
Packit 95306a
# Stored in: dump/MODNAME
Packit 95306a
#
Packit 95306a
sub do_dump {
Packit 95306a
   print "Dump...\n";
Packit 95306a
Packit 95306a
   my $tmp    = _yaml_read("tzdata/_zone_list");
Packit 95306a
   my %module = %$tmp;
Packit 95306a
   my $num    = keys %module;
Packit 95306a
   my $len    = length($num);
Packit 95306a
   my $i      = 0;
Packit 95306a
Packit 95306a
   system("rm -rf tzdata/dump; " .
Packit 95306a
          "mkdir tzdata/dump");
Packit 95306a
Packit 95306a
   print "   dumping "," "x($len-length($i)),"$i / $num";
Packit 95306a
Packit 95306a
   foreach my $zone (keys %module) {
Packit 95306a
      $i++;
Packit 95306a
      print "\010"x($len*2+3)," "x($len-length($i)),"$i / $num";
Packit 95306a
      my($module,$type) = @{ $module{$zone} };
Packit 95306a
      next  if ($type ne "tzdata");
Packit 95306a
      system("cd tzdata; " .
Packit 95306a
             "tmp/etc/zdump -c $test_year -v $zone > dump/$module");
Packit 95306a
   }
Packit 95306a
   print "\n";
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_MODS
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
# Creates the modules.
Packit 95306a
#
Packit 95306a
sub do_mods {
Packit 95306a
   print "Modules...\n";
Packit 95306a
   my $tzd = Date::Manip::TZdata->new();
Packit 95306a
   system("rm -f $mod_dir/*");
Packit 95306a
Packit 95306a
   my $zone_list = _yaml_read("tzdata/_zone_list");
Packit 95306a
   my %zone_list = %$zone_list;
Packit 95306a
   my $num       = keys %zone_list;
Packit 95306a
   my $len       = length($num);
Packit 95306a
   my $i         = 0;
Packit 95306a
Packit 95306a
   my $abbrev = {};
Packit 95306a
   my $data   = {};
Packit 95306a
Packit 95306a
   print "   module "," "x($len-length($i)),"$i / $num";
Packit 95306a
Packit 95306a
   foreach my $zone (keys %zone_list) {
Packit 95306a
      $i++;
Packit 95306a
      print "\010"x($len*2+3)," "x($len-length($i)),"$i / $num";
Packit 95306a
      my($module,$type,@args) = @{ $zone_list{$zone} };
Packit 95306a
Packit 95306a
      if ($type eq "tzdata") {
Packit 95306a
         _do_mods_tzdata($tzd,$abbrev,$data,$zone,$module,@args);
Packit 95306a
Packit 95306a
      } elsif ($type eq "offset") {
Packit 95306a
         _do_mods_offset($tzd,$abbrev,$data,$zone,$module,@args);
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
   print "\n";
Packit 95306a
Packit 95306a
   # $data now contains a hash of:
Packit 95306a
   #    YEAR => [ TYPE, VAL1, VAL2, ... ]
Packit 95306a
   # where TYPE is currently blank.
Packit 95306a
   #
Packit 95306a
   # VALi is a reference to a time change [ ABB, OFFSET, ISDST ]
Packit 95306a
   #
Packit 95306a
   # TYPE will be set to one of the following:
Packit 95306a
   #
Packit 95306a
   #    std0, : a standard year is  one with two time changes which must
Packit 95306a
   #    std1    be  with ISDST  =  1 and  0, and  both must be integers
Packit 95306a
   #            that differ by exactly 1.  If the ISDST = 0 comes first,
Packit 95306a
   #            it is set to std0.  Otherwise it is set to std1.
Packit 95306a
   #    last0,
Packit 95306a
   #    last1 : if it's a standard year AND the year is after $keep_year
Packit 95306a
   #    end   : a non-standard year after $keep_year
Packit 95306a
   #    non   : a non-standard year before $keep_year
Packit 95306a
Packit 95306a
   foreach my $zone (keys %$data) {
Packit 95306a
      my $lasttype = '';
Packit 95306a
      foreach my $year (keys %{ $$data{$zone} }) {
Packit 95306a
         my $type;
Packit 95306a
         my @tmp = @{ $$data{$zone}{$year} };
Packit 95306a
         shift(@tmp);
Packit 95306a
Packit 95306a
         # Standard times must have two changes
Packit 95306a
Packit 95306a
         $type = 'std';
Packit 95306a
         $type = 'non'  if (@tmp != 2);
Packit 95306a
Packit 95306a
         # Standard times must have both offsets on the hour.
Packit 95306a
Packit 95306a
         my ($off1,$off2);
Packit 95306a
         if ($type eq 'std') {
Packit 95306a
            $off1 = $tmp[0][1];
Packit 95306a
            $off2 = $tmp[1][1];
Packit 95306a
            if ($off1 !~ /:00:00$/  ||
Packit 95306a
                $off2 !~ /:00:00$/) {
Packit 95306a
               $type = 'non';
Packit 95306a
            } else {
Packit 95306a
               $type = 'std';
Packit 95306a
            }
Packit 95306a
         }
Packit 95306a
Packit 95306a
         # Standard times must have offsets that are 1 hour apart.
Packit 95306a
Packit 95306a
         if ($type eq 'std') {
Packit 95306a
            $off1 =~ s/:00:00$//;
Packit 95306a
            $off2 =~ s/:00:00$//;
Packit 95306a
            $type = 'non'  if (abs($off1 - $off2) != 1);
Packit 95306a
         }
Packit 95306a
Packit 95306a
         # Standard times have offsets with two offsets with ISDST = 0 and 1
Packit 95306a
Packit 95306a
         my $std;
Packit 95306a
         if      ($type eq 'std'   &&
Packit 95306a
                  $tmp[0][2] == 1  &&
Packit 95306a
                  $tmp[1][2] == 0) {
Packit 95306a
            $std = 1;
Packit 95306a
         } elsif ($type eq 'std'  &&
Packit 95306a
                  $tmp[0][2] == 0  &&
Packit 95306a
                  $tmp[1][2] == 1) {
Packit 95306a
            $std = 0;
Packit 95306a
         } else {
Packit 95306a
            $type = 'non';
Packit 95306a
         }
Packit 95306a
Packit 95306a
         # Set the type
Packit 95306a
Packit 95306a
         if ($type eq 'std') {
Packit 95306a
            if ($year > $keep_year) {
Packit 95306a
               $type  = "last$std";
Packit 95306a
            } else {
Packit 95306a
               $type .= $std;
Packit 95306a
            }
Packit 95306a
Packit 95306a
         } elsif ($year > $keep_year) {
Packit 95306a
            $type = 'end';
Packit 95306a
         }
Packit 95306a
Packit 95306a
         # We'll discard the very last year of a standard timezone
Packit 95306a
         # because they end on the first change of a year instead of
Packit 95306a
         # the second.
Packit 95306a
         next  if ($lasttype =~ /last/  &&  $type eq 'end');
Packit 95306a
         $lasttype = $type;
Packit 95306a
Packit 95306a
         $$data{$zone}{$year}[0] = $type;
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Create a list of all EST5EDT style time zone aliases.
Packit 95306a
   # These only apply during standard years.
Packit 95306a
   #
Packit 95306a
   # Also, we will ignore aliases when the abbreviations are
Packit 95306a
   # offsets.
Packit 95306a
Packit 95306a
   my $alias2 = {};
Packit 95306a
 ZONE: foreach my $zone (keys %$data) {
Packit 95306a
      foreach my $year (sort keys %{ $$data{$zone} }) {
Packit 95306a
         my ($type,@tmp) = @{ $$data{$zone}{$year} };
Packit 95306a
         next  if ($type ne 'std0'  &&
Packit 95306a
                   $type ne 'std1'  &&
Packit 95306a
                   $type ne 'last0' &&
Packit 95306a
                   $type ne 'last1');
Packit 95306a
Packit 95306a
         # The format is
Packit 95306a
         #   STDABB STDOFFHR DSTABB
Packit 95306a
         # where STDABB and DSTABB are the abbreviations, and STDOFFHR
Packit 95306a
         # is the offset for standard time as an integer negated.
Packit 95306a
Packit 95306a
         my ($stdabb,$dstabb,$stdoff);
Packit 95306a
         if ($type eq 'std0'  ||  $type eq 'last0') {
Packit 95306a
            $stdabb = $tmp[0][0];
Packit 95306a
            $dstabb = $tmp[1][0];
Packit 95306a
            $stdoff = $tmp[0][1];
Packit 95306a
         } else {
Packit 95306a
            $stdabb = $tmp[1][0];
Packit 95306a
            $dstabb = $tmp[0][0];
Packit 95306a
            $stdoff = $tmp[1][1];
Packit 95306a
         }
Packit 95306a
         $stdoff    =~ s/:00:00$//;
Packit 95306a
         $stdoff   *= -1;
Packit 95306a
Packit 95306a
         next  if ($stdabb =~ /^[+-]?\d+$/  ||
Packit 95306a
                   $dstabb =~ /^[+-]?\d+$/);
Packit 95306a
Packit 95306a
         my $alias  = "${stdabb}${stdoff}${dstabb}";
Packit 95306a
Packit 95306a
         if ($type eq 'last0'  ||  $type eq 'last1') {
Packit 95306a
            _do_mods_years($alias2,$zone,$alias,$year,9999);
Packit 95306a
            next ZONE;
Packit 95306a
         }
Packit 95306a
         _do_mods_years($alias2,$zone,$alias,$year,$year);
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   _yaml_write($data,  'tzdata/_data',0);
Packit 95306a
   _yaml_write($alias2,'tzdata/_alias2_un',0);
Packit 95306a
Packit 95306a
   $abbrev = _order_elements($abbrev);
Packit 95306a
   $alias2 = _order_elements($alias2);
Packit 95306a
Packit 95306a
   _yaml_write($abbrev,'tzdata/_abbrev_or',0);
Packit 95306a
   _yaml_write($alias2,'tzdata/_alias2_or',0);
Packit 95306a
}
Packit 95306a
Packit 95306a
# This creates a module from a tzdata dump.
Packit 95306a
#
Packit 95306a
sub _do_mods_tzdata {
Packit 95306a
   my($tzd,$abbrev,$data,$zone,$module) = @_;
Packit 95306a
Packit 95306a
   my @lines  = `cat tzdata/dump/$module`;
Packit 95306a
   chomp(@lines);
Packit 95306a
Packit 95306a
   while (@lines  &&  $lines[0] =~ /NULL$/) {
Packit 95306a
      shift(@lines);
Packit 95306a
   }
Packit 95306a
   while (@lines  &&  $lines[$#lines] =~ /NULL$/) {
Packit 95306a
      pop(@lines);
Packit 95306a
   }
Packit 95306a
Packit 95306a
   if (! @lines) {
Packit 95306a
      warn "[_do_mods_tzdata] empty zone [$zone]\n";
Packit 95306a
      return;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Check the format of every line
Packit 95306a
   my $err = _do_mods_tzdata_check($zone,@lines);
Packit 95306a
   return  if ($err);
Packit 95306a
   _do_mods_tzdata_mod($tzd,$abbrev,$data,$zone,$module,@lines);
Packit 95306a
}
Packit 95306a
Packit 95306a
# This checks every line in a zdump file to make sure it is the
Packit 95306a
# correct format.
Packit 95306a
#
Packit 95306a
sub _do_mods_tzdata_check {
Packit 95306a
   my($zone,@lines) = @_;
Packit 95306a
   my($dow)  = '(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)';
Packit 95306a
   my($mon)  = '(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
Packit 95306a
   my($dom)  = '(?:\d+)';
Packit 95306a
   my($time) = '(?:\d\d:\d\d:\d\d)';
Packit 95306a
   my($year) = '(?:\d\d\d\d)';
Packit 95306a
   my($drx)  = qr/$dow\s+$mon\s+$dom\s+$time\s+$year/;
Packit 95306a
   my($rx)   = qr/\Q$zone\E\s+$drx\s+UT\s+=\s+$drx\s+\S+\s+isdst=[01]\s+gmtoff=\-?\d+$/;
Packit 95306a
Packit 95306a
   my($err)  = 0;
Packit 95306a
   foreach my $line (@lines) {
Packit 95306a
      if ($line !~ /$rx/) {
Packit 95306a
         warn "[_do_mods_tzdata] invalid line [$zone]\n   $line\n";
Packit 95306a
         $err = 1;
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
   return $err;
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_mods_tzdata_mod {
Packit 95306a
   my($tzd,$abbrev,$data,$zone,$module,@lines) = @_;
Packit 95306a
Packit 95306a
   ###
Packit 95306a
   ### Analyze the dump file and store information about all
Packit 95306a
   ### time zone periods in a list. A time zone period is a
Packit 95306a
   ### starting time and ending time during which the abbreviation,
Packit 95306a
   ### offset, and ISDST values remain unchanged.
Packit 95306a
   ###
Packit 95306a
   ### The first line in the dump file defines when the pre-use
Packit 95306a
   ### period (i.e. the period of time before the time zone was
Packit 95306a
   ### actually defined) ended.
Packit 95306a
   ###
Packit 95306a
   ### After the first line, all lines (except the last one) appear as
Packit 95306a
   ### pairs. The first one tells the time when a new time zone period
Packit 95306a
   ### starts (which should be exactly 1 second after the previous
Packit 95306a
   ### period ended) and the second line tells when the period ends.
Packit 95306a
   ###
Packit 95306a
   ### The last line defines the start of a new period that doesn't
Packit 95306a
   ### have an end defined. If the year is after $keep_year, then
Packit 95306a
   ### the period switches to LASTRULE handline. If it is before
Packit 95306a
   ### $keep_year, then the time zone stopped doing DST changes and
Packit 95306a
   ### stay in the same period for good.
Packit 95306a
   ###
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Parse the first dump line to determine the end of the
Packit 95306a
   # pre-zone period.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   my @dates;
Packit 95306a
   my $last = 1;   # Whether or not to do LAST RULE
Packit 95306a
   my ($year,$year2);
Packit 95306a
Packit 95306a
   my $line = shift(@lines);
Packit 95306a
   my($dowU,$monU,$domU,$timeU,$yearU,$dowL,$monL,$domL,$timeL,$yearL,
Packit 95306a
      $abb,$isdst) = _do_mods_splitdump($line);
Packit 95306a
Packit 95306a
   if ($isdst) {
Packit 95306a
      warn "[_do_mods_tzdata] first line in DST [$zone]\n";
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Calculate the offset of the pre-zone period.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   my @endUT    = ($yearU,$monU,$domU,@{ $dmb->split("time",$timeU) });
Packit 95306a
   my @endLT    = ($yearL,$monL,$domL,@{ $dmb->split("time",$timeL) });
Packit 95306a
   my @offset   = @{ $dmb->calc_date_date(\@endUT,\@endLT) };
Packit 95306a
   my $offset   = $dmb->join("offset",\@offset);
Packit 95306a
Packit 95306a
   if ($offset eq ""  ||
Packit 95306a
       $abb    eq ""  ||
Packit 95306a
       $isdst  eq "") {
Packit 95306a
      warn "[_do_mods_tzdata] blank value in zone [$zone, @endUT]\n";
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # The pre-zone period starts on Jan 2 0001 at 00:00:00 and
Packit 95306a
   # ends at the time from the first dump line.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   my @begUT    = @{ $dmb->split("date",$first_date) };
Packit 95306a
   my @begLT    = @{ $dmb->calc_date_time(\@begUT,\@offset) };
Packit 95306a
   @dates       = ("0001",[@begUT],[@begLT],$offset,[@offset],
Packit 95306a
                   $abb,$isdst,[@endUT],[@endLT]);
Packit 95306a
Packit 95306a
   $year2       = $endUT[0];
Packit 95306a
   _do_mods_years($abbrev,$zone,$abb,"0001",$year2)  if ($abb !~ /^[+-]?\d*$/);
Packit 95306a
   $$data{$zone}{"0001"} = [ '', [$abb,$offset,$isdst] ];
Packit 95306a
Packit 95306a
   #
Packit 95306a
   # Parse every pair of dump lines.
Packit 95306a
   #
Packit 95306a
Packit 95306a
   while (@lines) {
Packit 95306a
Packit 95306a
      #
Packit 95306a
      # The first line is the start of the period
Packit 95306a
      #
Packit 95306a
Packit 95306a
      $line     = shift(@lines);
Packit 95306a
      ($dowU,$monU,$domU,$timeU,$yearU,$dowL,$monL,$domL,$timeL,$yearL,
Packit 95306a
       $abb,$isdst) = _do_mods_splitdump($line);
Packit 95306a
      $year     = $yearU;
Packit 95306a
Packit 95306a
      @begUT    = ($yearU,$monU,$domU,@{ $dmb->split("time",$timeU) });
Packit 95306a
      @begLT    = ($yearL,$monL,$domL,@{ $dmb->split("time",$timeL) });
Packit 95306a
Packit 95306a
      my @tmp   = @{ $dmb->calc_date_time(\@endUT,[0,0,1]) };
Packit 95306a
      if ($dmb->cmp(\@tmp,\@begUT) != 0) {
Packit 95306a
         warn "[_do_mods_tzdata] invalid start in zone [$zone, @begUT]\n";
Packit 95306a
         return 1;
Packit 95306a
      }
Packit 95306a
Packit 95306a
      @offset   = @{ $dmb->calc_date_date(\@begUT,\@begLT) };
Packit 95306a
      $offset   = $dmb->join("offset",\@offset);
Packit 95306a
Packit 95306a
      if ($offset eq ""  ||
Packit 95306a
          $abb    eq ""  ||
Packit 95306a
          $isdst  eq "") {
Packit 95306a
         warn "[_do_mods_tzdata] blank value in zone [$zone, @begUT]\n";
Packit 95306a
         return 1;
Packit 95306a
      }
Packit 95306a
Packit 95306a
      #
Packit 95306a
      # If a second line exists, it is the end of the period.
Packit 95306a
      #
Packit 95306a
      # If no second line exists, then either we need to switch to
Packit 95306a
      # LAST RULE behavior (if the year of the first line is after
Packit 95306a
      # $keep_year), or the zone abandoned doing daylight savings
Packit 95306a
      # time and this line reflects the time until 9999.
Packit 95306a
      #
Packit 95306a
Packit 95306a
      if (@lines) {
Packit 95306a
Packit 95306a
         # A second line marks the end of the period
Packit 95306a
Packit 95306a
         my ($a,$i);
Packit 95306a
         $line     = shift(@lines);
Packit 95306a
         ($dowU,$monU,$domU,$timeU,$yearU,$dowL,$monL,$domL,$timeL,$yearL,
Packit 95306a
          $a,$i)   = _do_mods_splitdump($line);
Packit 95306a
Packit 95306a
         @endUT    = ($yearU,$monU,$domU,@{ $dmb->split("time",$timeU) });
Packit 95306a
         @endLT    = ($yearL,$monL,$domL,@{ $dmb->split("time",$timeL) });
Packit 95306a
Packit 95306a
         my @o     = @{ $dmb->calc_date_date(\@endUT,\@endLT) };
Packit 95306a
         my $o     = $dmb->join("offset",\@o);
Packit 95306a
Packit 95306a
         if ($o  eq ""  ||
Packit 95306a
             $a  eq ""  ||
Packit 95306a
             $i  eq "") {
Packit 95306a
            warn "[_do_mods_tzdata] blank value in zone [$zone, @endUT]\n";
Packit 95306a
            return 1;
Packit 95306a
         }
Packit 95306a
Packit 95306a
         if ($o ne $offset  ||
Packit 95306a
             $a ne $abb     ||
Packit 95306a
             $i ne $isdst) {
Packit 95306a
            warn "[_do_mods_tzdata] invalid value in zone [$zone, @endUT]\n";
Packit 95306a
            return 1;
Packit 95306a
         }
Packit 95306a
Packit 95306a
      } elsif ($year > $keep_year  &&
Packit 95306a
               ! exists $no_last{$zone}) {
Packit 95306a
Packit 95306a
         # If it's a single line after $keep_year, then it's the start
Packit 95306a
         # of a regular LAST RULE style time change. Discard it... we'll
Packit 95306a
         # use the LAST RULE to come up with those periods.
Packit 95306a
Packit 95306a
         last;
Packit 95306a
Packit 95306a
      } else {
Packit 95306a
Packit 95306a
         # A single line before $keep_year means that the time zone
Packit 95306a
         # stopped doing DST stuff, and switched to a single offset.
Packit 95306a
         # There is no LAST RULE in this case.
Packit 95306a
         #
Packit 95306a
         # This will also apply to zones which do not use the LAST
Packit 95306a
         # RULE method.
Packit 95306a
Packit 95306a
         @endUT    = @{ $dmb->split("date",$last_date) };
Packit 95306a
         @endLT    = @{ $dmb->calc_date_time(\@endUT,\@offset) };
Packit 95306a
         $last     = 0;
Packit 95306a
      }
Packit 95306a
Packit 95306a
      # Now store the data for this time zone period
Packit 95306a
Packit 95306a
      push(@dates,$year,[@begUT],[@begLT],$offset,[@offset],$abb,$isdst,[@endUT],[@endLT]);
Packit 95306a
      $year2 = $endUT[0];
Packit 95306a
Packit 95306a
      if (exists $$data{$zone}{$year}) {
Packit 95306a
         push(@{ $$data{$zone}{$year} },[$abb,$offset,$isdst]);
Packit 95306a
      } else {
Packit 95306a
         $$data{$zone}{$year} = [ '', [$abb,$offset,$isdst] ];
Packit 95306a
      }
Packit 95306a
Packit 95306a
      _do_mods_years($abbrev,$zone,$abb,$year,$year2)  if ($abb !~ /^[+-]?\d*$/);
Packit 95306a
   }
Packit 95306a
Packit 95306a
   ###
Packit 95306a
   ### Now we'll analyze all the critical dates. Three different things
Packit 95306a
   ### will occur:
Packit 95306a
   ###
Packit 95306a
   ### 1) For years < $keep_year, the data will simply get stored in
Packit 95306a
   ###    the module.
Packit 95306a
   ### 2) For year = $keep_year, the data will be stored in the module
Packit 95306a
   ###    and used to determine how LAST RULE critical dates are
Packit 95306a
   ###    determined.
Packit 95306a
   ### 3) For year > $keep_year, critical dates will not be stored, but
Packit 95306a
   ###    will be tested to make sure they are consistant with the methods
Packit 95306a
   ###    determined in 2). However, this step will be elsewhere. I will
Packit 95306a
   ###    use a dump script to create actual dumps and compare them to
Packit 95306a
   ###    the standard tzcode dump.
Packit 95306a
   ###
Packit 95306a
Packit 95306a
   my @mod;                # data to store in the module
Packit 95306a
   my %last;               # LAST RULE description
Packit 95306a
   my @mon;
Packit 95306a
Packit 95306a
   if ($last) {
Packit 95306a
      %last    = _do_mods_lastrule($tzd,$zone);
Packit 95306a
      @mon     = sort keys %{ $last{"rules"} };
Packit 95306a
   }
Packit 95306a
Packit 95306a
   foreach my $mon (@mon) {
Packit 95306a
      if ($mon == 1  ||  $mon == 12) {
Packit 95306a
         # If a change ever happens in Jan/Dec in the LAST RULE, we
Packit 95306a
         # may need to make sure that the year won't change (it would
Packit 95306a
         # be horrible if it did).
Packit 95306a
         warn "[_do_mods_tzdata] LAST RULE in Jan/Dec [$zone, $mon]\n";
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   my $didlast = 0;
Packit 95306a
   my($begUT,$begLT,$endUT,$endLT,$offsetref);
Packit 95306a
   while (@dates) {
Packit 95306a
      ($year,$begUT,$begLT,$offset,$offsetref,$abb,$isdst,$endUT,$endLT,@dates) = @dates;
Packit 95306a
      @offset = @$offsetref;
Packit 95306a
Packit 95306a
      if      ($year <= $keep_year  ||  ! $last) {
Packit 95306a
Packit 95306a
         #
Packit 95306a
         # Store critical dates from dump files for years <= $keep_year
Packit 95306a
         #
Packit 95306a
Packit 95306a
         push(@mod,$year,$begUT,$begLT,$offset,$offsetref,$abb,$isdst,$endUT,$endLT);
Packit 95306a
Packit 95306a
         if ($year == $keep_year  &&  $last) {
Packit 95306a
Packit 95306a
            my $mon                        = shift(@mon);
Packit 95306a
            return 1  if (! $mon);
Packit 95306a
Packit 95306a
            if ($isdst != $last{"rules"}{$mon}{"isdst"}) {
Packit 95306a
               warn "[_do_mods_tzdata] isdst mismatch in LAST RULE " .
Packit 95306a
                 "[$zone, $mon]\n";
Packit 95306a
               return 1;
Packit 95306a
            }
Packit 95306a
            if ($offset ne
Packit 95306a
                $last{"zone"}{ ($isdst ? "dstoff" : "stdoff") }) {
Packit 95306a
               warn "[_do_mods_tzdata] offset mismatch in LAST RULE " .
Packit 95306a
                 "[$zone, $mon]\n";
Packit 95306a
               return 1;
Packit 95306a
            }
Packit 95306a
Packit 95306a
            $last{"rules"}{$mon}{"abb"}    = $abb;
Packit 95306a
            _do_mods_years($abbrev,$zone,$abb,$keep_year+1,9999)
Packit 95306a
              if ($abb !~ /^[+-]?\d*$/);
Packit 95306a
            $didlast++;
Packit 95306a
         }
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   if ($last  &&  $didlast != 2) {
Packit 95306a
      warn "[_do_mods_tzdata] LAST RULE incomplete [$zone]\n";
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   _do_mods_write($zone,$module,[@mod],%last);
Packit 95306a
}
Packit 95306a
Packit 95306a
# This returns a hash of information concerning "last rules". This
Packit 95306a
# information will allow us to calculate critical dates in future
Packit 95306a
# years.
Packit 95306a
#
Packit 95306a
# Information consists of:
Packit 95306a
#    flag,dow,num     : See TZdata.pm (used to calculate a DoM)
Packit 95306a
#    add              : Some of the DoM calculations do not
Packit 95306a
#                       return the final DoM after offsets have
Packit 95306a
#                       been applied. If this is +1, it'll add
Packit 95306a
#                       a day. If it's -1, it'll subtract a day.
Packit 95306a
#    time,abb,offset  : Information that should be constant.
Packit 95306a
#    dst              : Whether it is a change to DST or not.
Packit 95306a
#
Packit 95306a
sub _do_mods_lastrule {
Packit 95306a
   my($tzd,$zone) = @_;
Packit 95306a
Packit 95306a
   # Get the rule dates that apply to $keep_year
Packit 95306a
Packit 95306a
   my @rules = $tzd->_zoneInfo($zone,"rules",$keep_year);
Packit 95306a
   my @r;
Packit 95306a
   while (@rules) {
Packit 95306a
      my $rule = shift(@rules);
Packit 95306a
      my $type = shift(@rules);
Packit 95306a
Packit 95306a
      # All LAST RULES are currently of type TZ_RULE . If this
Packit 95306a
      # ever changes, we'll have to add support.
Packit 95306a
      if ($type != $Date::Manip::TZdata::TZ_RULE) {
Packit 95306a
         warn "[_do_mods_lastrule] unsupported rule type [$zone]\n";
Packit 95306a
         return "";
Packit 95306a
      }
Packit 95306a
Packit 95306a
      push(@r,$tzd->_ruleInfo($rule,"rules",$keep_year));
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Make sure that there are exactly two rules. If there are
Packit 95306a
   # not, we'll need to add support.
Packit 95306a
Packit 95306a
   if ($#r != 1) {
Packit 95306a
      warn "[_do_mods_lastrule] two rules required [$zone]\n";
Packit 95306a
      return "";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Also get the zone line that applies. There must be one or
Packit 95306a
   # we'll need to add support.
Packit 95306a
Packit 95306a
   my @zone = $tzd->_zoneInfo($zone,"zonelines",$keep_year);
Packit 95306a
   if ($#zone != 0) {
Packit 95306a
      warn "[_do_mods_lastrule] one zone line required [$zone]\n";
Packit 95306a
      return "";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Analyze the rules/zone to get the "last rule" (i.e. information
Packit 95306a
   # that can be used to calculate critical dates in future years).
Packit 95306a
   #
Packit 95306a
   # Some additional information will be added once dump lines are
Packit 95306a
   # analyzed.
Packit 95306a
Packit 95306a
   my %last = ( "year"  => $keep_year + 1,
Packit 95306a
                "zone"  => { "stdoff" => $dmb->_delta_convert("offset",$zone[0][0]),
Packit 95306a
                             "dstoff" => '' },
Packit 95306a
                "rules" => {},
Packit 95306a
              );
Packit 95306a
Packit 95306a
   my $totdst = 0;
Packit 95306a
   my $totst  = 0;
Packit 95306a
   foreach my $rule (@r) {
Packit 95306a
      my($y0,$y1,$ytype,$mon,$flag,$dow,$num,$timetype,$time,$offset,
Packit 95306a
         $lett) = @$rule;
Packit 95306a
      my $isdst = ($offset eq "00:00:00" ? 0 : 1);
Packit 95306a
      $totdst  += $isdst;
Packit 95306a
      $totst   += (1-$isdst);
Packit 95306a
Packit 95306a
      if ($isdst) {
Packit 95306a
         my $dstoff = $dmb->calc_time_time( $dmb->split("time",$last{"zone"}{"stdoff"}),
Packit 95306a
                                            $dmb->split("time",$offset));
Packit 95306a
         $dstoff    = $dmb->join("offset",$dstoff);
Packit 95306a
         $last{"zone"}{"dstoff"} = $dstoff;
Packit 95306a
      }
Packit 95306a
Packit 95306a
      $mon="0$mon"  if (length($mon) != 2);
Packit 95306a
Packit 95306a
      $last{"rules"}{$mon} = { "flag"   => $flag,
Packit 95306a
                               "dow"    => $dow,
Packit 95306a
                               "num"    => $num,
Packit 95306a
                               "type"   => $timetype,
Packit 95306a
                               "time"   => $time,
Packit 95306a
                               "isdst"  => $isdst,
Packit 95306a
                               "abb"    => "",
Packit 95306a
                             };
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # One rule must be standard time, one must be daylight savings time.
Packit 95306a
   # If this is not the case, we'll have to add support.
Packit 95306a
Packit 95306a
   if (exists $last_zone_offsets{$zone}) {
Packit 95306a
      if (! $last{"zone"}{"dstoff"}) {
Packit 95306a
         $last{"zone"}{"dstoff"} = $last{"zone"}{"stdoff"};
Packit 95306a
      }
Packit 95306a
Packit 95306a
      my $expdst = $last_zone_offsets{$zone}{"dst"};
Packit 95306a
      my $expst  = $last_zone_offsets{$zone}{"st"};
Packit 95306a
      if ($totdst != $expdst  ||
Packit 95306a
          $totst  != $expst) {
Packit 95306a
         warn "\n" .
Packit 95306a
           "[_do_mods_lastrule] wrong number of DST/STD offsets\n" .
Packit 95306a
           "                    [exp $expdst/$expst got $totdst/$totst] [$zone]\n";
Packit 95306a
         return "";
Packit 95306a
      }
Packit 95306a
Packit 95306a
   } elsif ($totdst != 1  ||  $totst != 1) {
Packit 95306a
      warn "[_do_mods_lastrule] 1 DST and 1 STD rule required [$zone]\n";
Packit 95306a
      return "";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   return %last;
Packit 95306a
}
Packit 95306a
Packit 95306a
# Split a dump line and return the values.
Packit 95306a
#
Packit 95306a
sub _do_mods_splitdump {
Packit 95306a
   my($line) = @_;
Packit 95306a
   my(%mon)  = qw(Jan 01 Feb 02 Mar 03 Apr 04 May 05 Jun 06
Packit 95306a
                  Jul 07 Aug 08 Sep 09 Oct 10 Nov 11 Dec 12);
Packit 95306a
   my(%dow)   = qw(Mon 1 Tue 2 Wed 3 Thu 4 Fri 5 Sat 6 Sun 7);
Packit 95306a
Packit 95306a
   my($z,$dowU,$monU,$domU,$timeU,$yearU,$utc,$equal,
Packit 95306a
      $dowW,$monW,$domW,$timeW,$yearW,$abb,$isdst) = split(/\s+/,$line);
Packit 95306a
   $isdst =~ s/isdst=//;
Packit 95306a
Packit 95306a
   $monU = $mon{$monU}  if (exists $mon{$monU});
Packit 95306a
   $monW = $mon{$monW}  if (exists $mon{$monW});
Packit 95306a
   $monU = "0$monU"     if (length($monU) != 2);
Packit 95306a
   $monW = "0$monW"     if (length($monW) != 2);
Packit 95306a
Packit 95306a
   $dowU = $dow{$dowU}  if (exists $dow{$dowU});
Packit 95306a
   $dowU = $dow{$dowW}  if (exists $dow{$dowW});
Packit 95306a
Packit 95306a
   $domU = "0$domU"     if (length($domU) != 2);
Packit 95306a
   $domW = "0$domW"     if (length($domW) != 2);
Packit 95306a
Packit 95306a
   return ($dowU,$monU,$domU,$timeU,$yearU,$dowW,$monW,$domW,$timeW,$yearW,
Packit 95306a
           $abb,$isdst);
Packit 95306a
}
Packit 95306a
Packit 95306a
# This records an element as having been used in a given year.
Packit 95306a
#
Packit 95306a
sub _do_mods_years {
Packit 95306a
   my($hash,$zone,$ele,$year,$year2) = @_;
Packit 95306a
Packit 95306a
   if (exists $$hash{$ele}{$zone}) {
Packit 95306a
      $$hash{$ele}{$zone}[1] = $year2;
Packit 95306a
Packit 95306a
   } else {
Packit 95306a
      $$hash{$ele}{$zone} = [$year,$year2];
Packit 95306a
   }
Packit 95306a
}
Packit 95306a
Packit 95306a
# This creates a module from an offset.
Packit 95306a
#
Packit 95306a
sub _do_mods_offset {
Packit 95306a
   my($tzd,$abbrev,$data,$zone,$module,$offset) = @_;
Packit 95306a
Packit 95306a
   my($abb) = $zone;
Packit 95306a
   $abb =~ s/Etc\///;
Packit 95306a
   _do_mods_years($abbrev,$zone,$abb,"0001","9999");
Packit 95306a
Packit 95306a
   $offset      = $dmb->_delta_convert("offset",$offset);
Packit 95306a
   my @offset   = @{ $dmb->split("offset",$offset) };
Packit 95306a
Packit 95306a
   my @begUT    = @{ $dmb->split("date",$first_date) };
Packit 95306a
   my @begLT    = @{ $dmb->calc_date_time(\@begUT,\@offset) };
Packit 95306a
Packit 95306a
   my @endUT    = @{ $dmb->split("date",$last_date) };
Packit 95306a
   my @endLT    = @{ $dmb->calc_date_time(\@endUT,\@offset) };
Packit 95306a
Packit 95306a
   _do_mods_write($zone,$module,
Packit 95306a
                  ["0001",[@begUT],[@begLT],$offset,[@offset],$abb,0,
Packit 95306a
                   [@endUT],[@endLT]],
Packit 95306a
                  ());
Packit 95306a
   $$data{$zone}{"0001"} = [ '', [$abb,$offset,0] ];
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_mods_write {
Packit 95306a
   my($zone,$module,$dates,%last) = @_;
Packit 95306a
Packit 95306a
   # Store the critical dates in the module
Packit 95306a
Packit 95306a
   my @tmp = `cat tzdata/_version`;
Packit 95306a
   chomp(@tmp);
Packit 95306a
   my $tzdata_vers = "tzdata" . $tmp[0];
Packit 95306a
   my $tzcode_vers = "tzcode" . $tmp[1];
Packit 95306a
   my $timestamp   = `date`;
Packit 95306a
   chomp($timestamp);
Packit 95306a
Packit 95306a
   my $out = new IO::File;
Packit 95306a
   $out->open(">$mod_dir/$module.pm");
Packit 95306a
   print $out "package #
Packit 95306a
Date::Manip::TZ::$module;
Packit 95306a
# Copyright (c) 2008-$curr_year 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
# This file was automatically generated.  Any changes to this file will
Packit 95306a
# be lost the next time 'tzdata' is run.
Packit 95306a
#    Generated on: $timestamp
Packit 95306a
#    Data version: $tzdata_vers
Packit 95306a
#    Code version: $tzcode_vers
Packit 95306a
Packit 95306a
# This module contains data from the zoneinfo time zone database.  The original
Packit 95306a
# data was obtained from the URL:
Packit 95306a
#    ftp://$tzdata_src/$tzdata_dir
Packit 95306a
Packit 95306a
use strict;
Packit 95306a
use warnings;
Packit 95306a
require 5.010000;
Packit 95306a
Packit 95306a
our (\%Dates,\%LastRule);
Packit 95306a
END {
Packit 95306a
   undef \%Dates;
Packit 95306a
   undef \%LastRule;
Packit 95306a
}
Packit 95306a
Packit 95306a
our (\$VERSION);
Packit 95306a
\$VERSION='6.60';
Packit 95306a
END { undef \$VERSION; }
Packit 95306a
Packit 95306a
\%Dates         = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   my @dates    = @$dates;
Packit 95306a
   my $lastyear = 0;
Packit 95306a
   my ($year,$begUT,$begLT,$offset,$offsetref,$abb,$isdst,$endUT,$endLT);
Packit 95306a
Packit 95306a
   while (@dates) {
Packit 95306a
      ($year,$begUT,$begLT,$offset,$offsetref,$abb,$isdst,$endUT,$endLT,@dates) =
Packit 95306a
        @dates;
Packit 95306a
      $year += 0;
Packit 95306a
      my $yrprt = $year . " "x(4-length($year));
Packit 95306a
      if ($year != $lastyear) {
Packit 95306a
         if ($lastyear) {
Packit 95306a
            print $out "     ],\n";
Packit 95306a
         }
Packit 95306a
         print $out "   $yrprt =>\n";
Packit 95306a
         print $out "     [\n";
Packit 95306a
         $lastyear = $year;
Packit 95306a
      }
Packit 95306a
      my $begUTs = $dmb->join("date",$begUT);
Packit 95306a
      my $begLTs = $dmb->join("date",$begLT);
Packit 95306a
      my $endUTs = $dmb->join("date",$endUT);
Packit 95306a
      my $endLTs = $dmb->join("date",$endLT);
Packit 95306a
      $begUT     = join(",",map { $_+0 } @$begUT);
Packit 95306a
      $begLT     = join(",",map { $_+0 } @$begLT);
Packit 95306a
      $endUT     = join(",",map { $_+0 } @$endUT);
Packit 95306a
      $endLT     = join(",",map { $_+0 } @$endLT);
Packit 95306a
      $offsetref = join(",",map { $_+0 } @$offsetref);
Packit 95306a
Packit 95306a
      print $out "        [ [$begUT],[$begLT],'$offset',[$offsetref],\n";
Packit 95306a
      print $out "          '$abb',$isdst,[$endUT],[$endLT],\n";
Packit 95306a
      print $out "          '$begUTs','$begLTs','$endUTs','$endLTs' ],\n";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out "     ],\n";
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
Packit 95306a
\%LastRule      = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   if (exists $last{"year"}) {
Packit 95306a
      print $out "   'zone'   => {\n";
Packit 95306a
      foreach my $key (sort keys %{ $last{"zone"} }) {
Packit 95306a
         my $val = $last{"zone"}{$key};
Packit 95306a
         print $out " "x16,"'$key' => '$val',\n";
Packit 95306a
      }
Packit 95306a
Packit 95306a
      print $out "               },
Packit 95306a
   'rules'  => {\n";
Packit 95306a
Packit 95306a
      foreach my $mon (sort keys %{ $last{"rules"} }) {
Packit 95306a
         print $out " "x16,"'$mon' => {\n";
Packit 95306a
         my $flag = $last{"rules"}{$mon}{"flag"};
Packit 95306a
         if ($flag == $Date::Manip::TZdata::TZ_DOM) {
Packit 95306a
            $flag = "dom";
Packit 95306a
Packit 95306a
         } elsif ($flag == $Date::Manip::TZdata::TZ_LAST) {
Packit 95306a
            $flag = "last";
Packit 95306a
Packit 95306a
         } elsif ($flag == $Date::Manip::TZdata::TZ_GE) {
Packit 95306a
            $flag = "ge";
Packit 95306a
Packit 95306a
         } elsif ($flag == $Date::Manip::TZdata::TZ_LE) {
Packit 95306a
            $flag = "le";
Packit 95306a
         }
Packit 95306a
         $last{"rules"}{$mon}{"flag"} = $flag;
Packit 95306a
Packit 95306a
         foreach my $key (qw(flag dow num type time isdst abb)) {
Packit 95306a
            print $out " "x25,"'$key'", " "x(7-length($key))," => '",
Packit 95306a
              $last{"rules"}{$mon}{$key},"',\n";
Packit 95306a
         }
Packit 95306a
         print $out " "x24,"},\n";
Packit 95306a
      }
Packit 95306a
Packit 95306a
      print $out "               },\n";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
Packit 95306a
1;
Packit 95306a
";
Packit 95306a
Packit 95306a
   $out->close;
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_OFF
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
sub do_off {
Packit 95306a
   print "Offset modules...\n";
Packit 95306a
Packit 95306a
   my $data = _yaml_read("tzdata/_data");
Packit 95306a
Packit 95306a
   # Get a list of all zones which an offset appears in, and the year
Packit 95306a
   # range of the offset.
Packit 95306a
Packit 95306a
   my %offset_un = ( 0 => {}, 1 => {} );
Packit 95306a
Packit 95306a
 ZONE:foreach my $zone (keys %$data) {
Packit 95306a
      my $lastoffset = '';
Packit 95306a
      my $lastisdst  = '';
Packit 95306a
      my @year       = sort keys %{ $$data{$zone} };
Packit 95306a
      while (@year) {
Packit 95306a
         my $year = shift(@year);
Packit 95306a
Packit 95306a
         # The offset at the end of the previous year is still in
Packit 95306a
         # affect.
Packit 95306a
Packit 95306a
         if ($lastoffset) {
Packit 95306a
            _do_mods_years($offset_un{$lastisdst},$zone,$lastoffset,$year,$year);
Packit 95306a
         }
Packit 95306a
Packit 95306a
         my ($type,@tmp) = @{ $$data{$zone}{$year} };
Packit 95306a
Packit 95306a
         foreach my $tmp (@tmp) {
Packit 95306a
            my($abb,$offset,$isdst) = @$tmp;
Packit 95306a
            $lastoffset = $offset;
Packit 95306a
            $lastisdst  = $isdst;
Packit 95306a
Packit 95306a
            if ($type =~ /last/) {
Packit 95306a
               _do_mods_years($offset_un{$isdst},$zone,$offset,$year,9999);
Packit 95306a
            } else {
Packit 95306a
               _do_mods_years($offset_un{$isdst},$zone,$offset,$year,$year);
Packit 95306a
            }
Packit 95306a
         }
Packit 95306a
Packit 95306a
         next ZONE  if ($type =~ /last/);
Packit 95306a
         _do_mods_years($offset_un{$lastisdst},$zone,$lastoffset,$year,9999)
Packit 95306a
           if (! @year);
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Convert %offset to a couple other formats that will be useful.
Packit 95306a
Packit 95306a
   my %offset_or;
Packit 95306a
   $offset_or{0} = _order_elements($offset_un{0});
Packit 95306a
   $offset_or{1} = _order_elements($offset_un{1});
Packit 95306a
Packit 95306a
   my %offset2_or;
Packit 95306a
   foreach my $isdst (keys %offset_un) {
Packit 95306a
      foreach my $offset (keys %{ $offset_un{$isdst} }) {
Packit 95306a
         $offset2_or{$offset}{$isdst} = $offset_or{$isdst}{$offset};
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Come up with a module name for each offset.
Packit 95306a
Packit 95306a
   my %offmod;
Packit 95306a
   my $o = "000";
Packit 95306a
   foreach my $offset (sort keys %offset2_or) {
Packit 95306a
      my $offmod = "off$o";
Packit 95306a
      $offmod{$offset} = $offmod;
Packit 95306a
      $o++;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Write out each module
Packit 95306a
Packit 95306a
   my $num  = keys %offmod;
Packit 95306a
   my $len  = length($num);
Packit 95306a
   my $i    = 0;
Packit 95306a
Packit 95306a
   print "   module "," "x($len-length($i)),"$i / $num";
Packit 95306a
Packit 95306a
   _warn_changes($offset_or{0},$def_off{0},15,12,33,"0");
Packit 95306a
   _warn_changes($offset_or{1},$def_off{1},15,12,33,"1");
Packit 95306a
Packit 95306a
   system("rm -f $off_dir/*");
Packit 95306a
   foreach my $offset (sort keys %offset2_or) {
Packit 95306a
      $i++;
Packit 95306a
      print "\010"x($len*2+3)," "x($len-length($i)),"$i / $num";
Packit 95306a
Packit 95306a
      my $offmod = $offmod{$offset};
Packit 95306a
      _do_off($offset,$offmod,\%offset_or);
Packit 95306a
   }
Packit 95306a
   print "\n";
Packit 95306a
Packit 95306a
   _yaml_write(\%offmod,"tzdata/_offmod",0);
Packit 95306a
   _yaml_write(\%offset_un,"tzdata/_offset_un",0);
Packit 95306a
   _yaml_write(\%offset_or,"tzdata/_offset_or",0);
Packit 95306a
   _yaml_write(\%offset2_or,"tzdata/_offset2_or",0);
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_off {
Packit 95306a
   my($offset,$module,$offset_or) = @_;
Packit 95306a
Packit 95306a
   my @tmp = `cat tzdata/_version`;
Packit 95306a
   chomp(@tmp);
Packit 95306a
   my $tzdata_vers = "tzdata" . $tmp[0];
Packit 95306a
   my $tzcode_vers = "tzcode" . $tmp[1];
Packit 95306a
   my $timestamp   = `date`;
Packit 95306a
   chomp($timestamp);
Packit 95306a
Packit 95306a
   my $out = new IO::File;
Packit 95306a
   my $mod = "Date::Manip::Offset::$module";
Packit 95306a
   $out->open(">$off_dir/$module.pm");
Packit 95306a
   print $out "package #
Packit 95306a
Date::Manip::Offset::$module;
Packit 95306a
# Copyright (c) 2008-$curr_year 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
# This file was automatically generated.  Any changes to this file will
Packit 95306a
# be lost the next time 'tzdata' is run.
Packit 95306a
#    Generated on: $timestamp
Packit 95306a
#    Data version: $tzdata_vers
Packit 95306a
#    Code version: $tzcode_vers
Packit 95306a
Packit 95306a
# This module contains data from the zoneinfo time zone database.  The original
Packit 95306a
# data was obtained from the URL:
Packit 95306a
#    ftp://$tzdata_src/$tzdata_dir
Packit 95306a
Packit 95306a
use strict;
Packit 95306a
use warnings;
Packit 95306a
require 5.010000;
Packit 95306a
Packit 95306a
our (\$VERSION);
Packit 95306a
\$VERSION='6.60';
Packit 95306a
END { undef \$VERSION; }
Packit 95306a
Packit 95306a
our (\$Offset,\%Offset);
Packit 95306a
END {
Packit 95306a
   undef \$Offset;
Packit 95306a
   undef \%Offset;
Packit 95306a
}
Packit 95306a
Packit 95306a
\$Offset        = '$offset';
Packit 95306a
Packit 95306a
\%Offset        = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   foreach my $isdst (sort keys %$offset_or) {
Packit 95306a
      next  if (! exists $$offset_or{$isdst}{$offset});
Packit 95306a
      my @tmp = @{ $$offset_or{$isdst}{$offset} };
Packit 95306a
Packit 95306a
      print $out " "x3,$isdst," => [\n";
Packit 95306a
      while (@tmp) {
Packit 95306a
         my $zone  = shift(@tmp);
Packit 95306a
         my $year1 = shift(@tmp);
Packit 95306a
         my $year2 = shift(@tmp);
Packit 95306a
         $zone = lc($zone);
Packit 95306a
         print $out " "x6,"'$zone',\n";
Packit 95306a
      }
Packit 95306a
      print $out " "x6,"],\n";
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
Packit 95306a
1;
Packit 95306a
";
Packit 95306a
Packit 95306a
   $out->close;
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_ZONES
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
sub do_zones {
Packit 95306a
   print "Zones module...\n";
Packit 95306a
Packit 95306a
   my @tmp = `cat tzdata/_version`;
Packit 95306a
   chomp(@tmp);
Packit 95306a
   my $tzdata_vers = "tzdata" . $tmp[0];
Packit 95306a
   my $tzcode_vers = "tzcode" . $tmp[1];
Packit 95306a
   my $timestamp   = `date`;
Packit 95306a
   chomp($timestamp);
Packit 95306a
Packit 95306a
   my $zone_list   = _yaml_read("tzdata/_zone_list");
Packit 95306a
   my $alias_list  = _yaml_read("tzdata/_alias_list");
Packit 95306a
   my $offset2_or  = _yaml_read("tzdata/_offset2_or");
Packit 95306a
Packit 95306a
   my $out = new IO::File;
Packit 95306a
   $out->open(">$zones_pm");
Packit 95306a
   my $pod = new IO::File;
Packit 95306a
   $pod->open(">$zones_pod");
Packit 95306a
Packit 95306a
   print $out "package Date::Manip::Zones;
Packit 95306a
# Copyright (c) 2008-$curr_year 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
# This file was automatically generated.  Any changes to this file will
Packit 95306a
# be lost the next time 'tzdata' is run.
Packit 95306a
#    Generated on: $timestamp
Packit 95306a
#    Data version: $tzdata_vers
Packit 95306a
#    Code version: $tzcode_vers
Packit 95306a
Packit 95306a
# This module contains data from the zoneinfo time zone database.  The original
Packit 95306a
# data was obtained from the URL:
Packit 95306a
#    ftp://$tzdata_src/$tzdata_dir
Packit 95306a
Packit 95306a
use strict;
Packit 95306a
use warnings;
Packit 95306a
require 5.010000;
Packit 95306a
Packit 95306a
our (\$VERSION);
Packit 95306a
\$VERSION='6.60';
Packit 95306a
END { undef \$VERSION; }
Packit 95306a
Packit 95306a
our (\$TzdataVersion,\$TzcodeVersion,
Packit 95306a
     \$FirstDate,\$LastDate,\$LastYear,
Packit 95306a
     \%Module,\%ZoneNames,\%Alias,\%Abbrev,\%Offmod);
Packit 95306a
END {
Packit 95306a
   undef \$TzdataVersion;
Packit 95306a
   undef \$TzcodeVersion;
Packit 95306a
   undef \$FirstDate;
Packit 95306a
   undef \$LastDate;
Packit 95306a
   undef \$LastYear;
Packit 95306a
   undef \%Module;
Packit 95306a
   undef \%ZoneNames;
Packit 95306a
   undef \%Alias;
Packit 95306a
   undef \%Abbrev;
Packit 95306a
   undef \%Offmod;
Packit 95306a
}
Packit 95306a
Packit 95306a
\$TzdataVersion = '$tzdata_vers';
Packit 95306a
\$TzcodeVersion = '$tzcode_vers';
Packit 95306a
\$FirstDate     = '$first_date';
Packit 95306a
\$LastDate      = '$last_date';
Packit 95306a
\$LastYear      = '$keep_year';
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
# Copyright (c) 2008-$curr_year 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
# This file was automatically generated.  Any changes to this file will
Packit 95306a
# be lost the next time 'tzdata' is run.
Packit 95306a
#    Generated on: $timestamp
Packit 95306a
#    Data version: $tzdata_vers
Packit 95306a
#    Code version: $tzcode_vers
Packit 95306a
Packit 95306a
# This module contains data from the zoneinfo time zone database.  The original
Packit 95306a
# data was obtained from the URL:
Packit 95306a
#    ftp://$tzdata_src/$tzdata_dir
Packit 95306a
Packit 95306a
$podstr
Packit 95306a
Packit 95306a
$hdstr NAME
Packit 95306a
Packit 95306a
Date::Manip::Zones - Time zone information
Packit 95306a
Packit 95306a
$hdstr DESCRIPTION
Packit 95306a
Packit 95306a
This module is automatically generated. It contains a complete list of
Packit 95306a
time zones specified in the standard zoneinfo (or Olson) databases
Packit 95306a
obtained from:
Packit 95306a
Packit 95306a
L<ftp://$tzdata_src/$tzdata_dir/tzdata_vers.tar.gz>
Packit 95306a
Packit 95306a
All information is stored in variables, so this module provide no
Packit 95306a
routines for dealing with time zone information. For routines related
Packit 95306a
to time zones, see the documentation for the L<Date::Manip::TZ> module.
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _do_zones_zones($out,$pod,$zone_list);
Packit 95306a
   _do_zones_names($out,$zone_list);
Packit 95306a
   _do_zones_aliases($out,$pod,$zone_list,$alias_list);
Packit 95306a
   _do_zones_defaults($out,$pod,$offset2_or);
Packit 95306a
   _do_zones_abbrevs($out,$pod);
Packit 95306a
   _do_zones_offsets($out,$pod);
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
1;
Packit 95306a
";
Packit 95306a
Packit 95306a
   $out->close;
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
$hdstr KNOWN BUGS
Packit 95306a
Packit 95306a
None known.
Packit 95306a
Packit 95306a
$hdstr BUGS AND QUESTIONS
Packit 95306a
Packit 95306a
Please refer to the L<Date::Manip::Problems> documentation for
Packit 95306a
information on submitting bug reports or questions to the author.
Packit 95306a
Packit 95306a
$hdstr SEE ALSO
Packit 95306a
Packit 95306a
L<Date::Manip>        - main module documentation
Packit 95306a
Packit 95306a
$hdstr LICENSE
Packit 95306a
Packit 95306a
This script is free software; you can redistribute it and/or
Packit 95306a
modify it under the same terms as Perl itself.
Packit 95306a
Packit 95306a
$hdstr AUTHOR
Packit 95306a
Packit 95306a
Sullivan Beck (sbeck\@cpan.org)
Packit 95306a
Packit 95306a
=cut
Packit 95306a
";
Packit 95306a
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_zones {
Packit 95306a
   my($out,$pod,$zone_list) = @_;
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
\%Module = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
$hdstr TIME ZONES
Packit 95306a
Packit 95306a
A description for each time zone from the zoneinfo database is stored
Packit 95306a
in a separate module. These modules will be loaded automatically as
Packit 95306a
needed, and are documented here for the sake of completeness.
Packit 95306a
Packit 95306a
The modules are available as:
Packit 95306a
Packit 95306a
   Date::Manip::TZ::_MODULE_
Packit 95306a
Packit 95306a
where _MODULE_ is the name of the module for that specific time zone.
Packit 95306a
Packit 95306a
The following time zones are derived from the standard zoneinfo
Packit 95306a
database:
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'TIME ZONE',35,  2,'MODULE NAME',20);
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %$zone_list) {
Packit 95306a
      my($mod,$type) = @{ $$zone_list{$zone} };
Packit 95306a
      next  if ($type ne "tzdata");
Packit 95306a
Packit 95306a
      _print_pod_row($pod,0,   5,$zone,35,             2,$mod,0);
Packit 95306a
      _print_mod_row($out,     2,$zone,35,'hashkey',   2,$mod,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
The following time zones are NOT derived from the standard zoneinfo
Packit 95306a
database. They are derived from other standard sources (including
Packit 95306a
RFC 822):
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'TIME ZONE',35,  2,'MODULE NAME',20);
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %$zone_list) {
Packit 95306a
      my($mod,$type) = @{ $$zone_list{$zone} };
Packit 95306a
      next  if ($type eq "tzdata");
Packit 95306a
Packit 95306a
      _print_pod_row($pod,0,   5,$zone,35,             2,$mod,0);
Packit 95306a
      _print_mod_row($out,     2,$zone,35,'hashkey',   2,$mod,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
";
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_names {
Packit 95306a
   my($out,$zone_list) = @_;
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
\%ZoneNames = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %$zone_list) {
Packit 95306a
      my($mod,$type) = @{ $$zone_list{$zone} };
Packit 95306a
      next  if ($type ne "tzdata");
Packit 95306a
Packit 95306a
      _print_mod_row($out,     2,$zone,35,'hashkey',   2,$zone,0,'hashval,nocase');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %$zone_list) {
Packit 95306a
      my($mod,$type) = @{ $$zone_list{$zone} };
Packit 95306a
      next  if ($type eq "tzdata");
Packit 95306a
Packit 95306a
      _print_mod_row($out,     2,$zone,35,'hashkey',   2,$zone,0,'hashval,nocase');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
";
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_aliases {
Packit 95306a
   my($out,$pod,$zone_list,$alias_list) = @_;
Packit 95306a
Packit 95306a
   my $tzd       = Date::Manip::TZdata->new();
Packit 95306a
   my %tzdalias  = %{ $$tzd{"alias"} };
Packit 95306a
   my $alias2_un = _yaml_read("tzdata/_alias2_un");
Packit 95306a
   my $alias2_or = _yaml_read("tzdata/_alias2_or");
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
\%Alias = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   # Print out the standard 'zone => zone' aliases
Packit 95306a
Packit 95306a
   foreach my $zone (sort keys %$zone_list) {
Packit 95306a
      _print_mod_row($out,     2,$zone,35,'hashkey',   2,$zone,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Print out alternate time zone names from tzdata files
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
$hdstr TIME ZONE NAMES, ALIASES, AND ABBREVIATIONS
Packit 95306a
Packit 95306a
Time zones may be referred to as their full name
Packit 95306a
(e.g. America/New_York), but there are also a number of standard
Packit 95306a
aliases and abbreviations that may be used.
Packit 95306a
Packit 95306a
Standard aliases are listed below. Additional aliases can be created,
Packit 95306a
or existing aliases overridden using the C<new_alias> method of the
Packit 95306a
L<Date::Manip::TZ> module.
Packit 95306a
Packit 95306a
The zoneinfo database provides several standard aliases, including:
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'ALTERNATE NAME',35,  2,'TIME ZONE',20);
Packit 95306a
Packit 95306a
   foreach my $alias (sort keys %tzdalias) {
Packit 95306a
      my $zone = $tzdalias{$alias};
Packit 95306a
Packit 95306a
      # Don't duplicate the 'zone => zone' or 'EST5EDT => zone' aliases
Packit 95306a
      next  if (exists $$zone_list{$zone}   &&  $alias eq $zone);
Packit 95306a
      next  if (exists $$alias_list{$zone}  &&  $$alias_list{$zone}[1] ne "tzdata");
Packit 95306a
      next  if (exists $$alias2_un{$zone});
Packit 95306a
Packit 95306a
      _print_pod_row($pod,0,   5,$alias,35,             2,$zone,0);
Packit 95306a
      _print_mod_row($out,     2,$alias,35,'hashkey',   2,$zone,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Do the EST5EDT style aliases
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
There are a large number of possible time zone aliases of the form
Packit 95306a
EST5EDT. The main 4 used in the United States are CST6CDT, EST5EDT,
Packit 95306a
MST7MDT, and PST8PDT and these are specifically called for in RFC 822,
Packit 95306a
so whenever possible, these will refer to the US time zones, but some
Packit 95306a
aliases may possibly refer to more than one time zone. In these
Packit 95306a
instances, I have selected one of them to be the default time zone to
Packit 95306a
use (based on how recently it was used, and for what period of
Packit 95306a
time). In the list below, all possible time zones are listed for each
Packit 95306a
alias. The first time zone listed is the one used by default. The
Packit 95306a
default alias can be overridden as described above.
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'ALTERNATE NAME',35,  2,'TIME ZONE',20);
Packit 95306a
Packit 95306a
   _warn_changes($alias2_or,\%def_alias2,3,16,42);
Packit 95306a
Packit 95306a
   foreach my $ele (sort keys %$alias2_or) {
Packit 95306a
      my @tmp = @{ $$alias2_or{$ele} };
Packit 95306a
      my $first = $ele;
Packit 95306a
      while (@tmp) {
Packit 95306a
         my $alias  = shift(@tmp);
Packit 95306a
         my $year1  = shift(@tmp);
Packit 95306a
         my $year2  = shift(@tmp);
Packit 95306a
         _print_pod_row($pod,0,  5,$first,35,             2,$alias,0);
Packit 95306a
         _print_mod_row($out,    2,$first,35,'hashkey',   2,$alias,0,'hashval')  if ($first);
Packit 95306a
         $first     = '';
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Print out alternate time zone names other sources
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
There are also a number of standard aliases. Some of these are
Packit 95306a
included to fix minor issues with the tzdata files. Others come from
Packit 95306a
standard sources including RFC 822 or the list of time zone names used
Packit 95306a
on Microsoft Windows operating systems.
Packit 95306a
Packit 95306a
Aliases include:
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'ALTERNATE NAME',35,  2,'TIME ZONE',20);
Packit 95306a
Packit 95306a
   foreach my $alias (sort keys %$alias_list) {
Packit 95306a
      my($zone,$type) = @{ $$alias_list{$alias} };
Packit 95306a
      next  if ($type eq "tzdata"  ||  $type eq "ignore");
Packit 95306a
Packit 95306a
      # Don't duplicate the 'zone => zone' aliases
Packit 95306a
      next  if (exists $$zone_list{$zone}   &&  $alias eq $zone);
Packit 95306a
Packit 95306a
      _print_pod_row($pod,0,   5,$alias,35,             2,$zone,0);
Packit 95306a
      _print_mod_row($out,     2,$alias,35,'hashkey',   2,$zone,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out ");
Packit 95306a
";
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_defaults {
Packit 95306a
   my($out,$pod,$offset2_or) = @_;
Packit 95306a
Packit 95306a
   # Start the defaults (POD only)
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
Periodically, we need to be able to determine a time zone based on an
Packit 95306a
offset. In addition, the ISDST may be known, and a date/time may be
Packit 95306a
available. The following table shows what time zones are examined based
Packit 95306a
on the offset, and in what order. The first match is used. If the
Packit 95306a
ISDST time is not known, the standard zones will be tested followed by
Packit 95306a
the DST zones.
Packit 95306a
Packit 95306a
The default order can be overridden with the C<off_zones> method in the
Packit 95306a
L<Date::Manip::TZ> module.
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'ISDST',5,  2,'OFFSET',10,  2,'TIME ZONE',25);
Packit 95306a
Packit 95306a
   foreach my $isdst (0,1) {
Packit 95306a
Packit 95306a
      foreach my $off (sort { _cmp_zoneoffsets($a,$b) } keys %$offset2_or) {
Packit 95306a
         next  unless (exists $$offset2_or{$off}{$isdst});
Packit 95306a
         my @tmp  = @{ $$offset2_or{$off}{$isdst} };
Packit 95306a
         my $zone = shift(@tmp);
Packit 95306a
         my $year1= shift(@tmp);
Packit 95306a
         my $year2= shift(@tmp);
Packit 95306a
Packit 95306a
         my $dst  = $isdst;
Packit 95306a
Packit 95306a
         _print_pod_row($pod,0,   5,$dst,5,  2,$off,10,  2,$zone,0);
Packit 95306a
Packit 95306a
         $off = "";
Packit 95306a
         $dst = " ";
Packit 95306a
         while (@tmp) {
Packit 95306a
            $zone  = shift(@tmp);
Packit 95306a
            $year1 = shift(@tmp);
Packit 95306a
            $year2 = shift(@tmp);
Packit 95306a
Packit 95306a
            _print_pod_row($pod,0,   5,$dst,5,  2,$off,10,  2,$zone,0);
Packit 95306a
         }
Packit 95306a
      }
Packit 95306a
      print $pod "\n";
Packit 95306a
   }
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _cmp_zoneoffsets {
Packit 95306a
   my($x,$y) = @_;
Packit 95306a
Packit 95306a
   # A negative offset comes before a positive one
Packit 95306a
Packit 95306a
   if      ($x =~ /^-/  &&  $y =~ /^\+/) {
Packit 95306a
      return -1;
Packit 95306a
   } elsif ($y =~ /^-/  &&  $x =~ /^\+/) {
Packit 95306a
      return +1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Netgative offsets are sorted reverse.
Packit 95306a
Packit 95306a
   if ($x =~ /^-/) {
Packit 95306a
      return ($y cmp $x);
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # Positive offsets are sorted normally.
Packit 95306a
Packit 95306a
   return ($x cmp $y);
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_abbrevs {
Packit 95306a
   my($out,$pod) = @_;
Packit 95306a
Packit 95306a
   my $abbrev_or = _yaml_read("tzdata/_abbrev_or");
Packit 95306a
Packit 95306a
   # Start the aliases output (both POD and module)
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
\%Abbrev = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   # Print out EST => ZONE aliases for abbreviations which only occur
Packit 95306a
   # in a single zone.
Packit 95306a
Packit 95306a
   delete $$abbrev_or{'LMT'};
Packit 95306a
   delete $$abbrev_or{'zzz'};
Packit 95306a
Packit 95306a
   # Print out EST => ZONE aliases
Packit 95306a
Packit 95306a
   print $pod "
Packit 95306a
In the time zone definitions, abbreviations are used to specify the
Packit 95306a
current time (e.g. EST in the America/New_York time zone). In some
Packit 95306a
cases, the abbreviation appears in only a single time zone, so for
Packit 95306a
these, there is no ambiguity.
Packit 95306a
Packit 95306a
More often though, abbreviations are used in multiple time zones. When
Packit 95306a
a date is parsed that contains one of these abbreviations, it will try
Packit 95306a
to interpret the date using each of the time zones in the order listed
Packit 95306a
below until one is found which yields a valid date.
Packit 95306a
Packit 95306a
The abbreviations LMT and zzz which occur in the zoneinfo databases
Packit 95306a
are ignored (and when parsing a date including them, the local time
Packit 95306a
zone will be used).
Packit 95306a
Packit 95306a
The default order can be overridden using the C<abbrev> method of the
Packit 95306a
L<Date::Manip::TZ> module.
Packit 95306a
Packit 95306a
The order given here is open to discussion (and possible change) based
Packit 95306a
on changes to the timezone database.  I will always place emphasis on a
Packit 95306a
time zone that used the abbreviation more recently than another time zone.
Packit 95306a
Within those constraints, I've tried to put the more commonly used time zone
Packit 95306a
at a higher priority. Since I'm not always able to decide which is the
Packit 95306a
most commonly used, I'm willing to entertain arguments for altering the order.
Packit 95306a
Packit 95306a
";
Packit 95306a
Packit 95306a
   _print_pod_row($pod,1,   5,'ALIAS',15,  2,'TIME ZONE',20);
Packit 95306a
Packit 95306a
   _warn_changes($abbrev_or,\%def_abbrev,3,14,44);
Packit 95306a
Packit 95306a
   my (@abb) = sort keys %$abbrev_or;
Packit 95306a
   foreach my $abb (@abb) {
Packit 95306a
      my @tmp = @{ $$abbrev_or{$abb} };
Packit 95306a
Packit 95306a
      my $first = $abb;
Packit 95306a
      my $opts1 = 'hashkey';
Packit 95306a
Packit 95306a
      while (@tmp) {
Packit 95306a
         my $zone  = shift(@tmp);
Packit 95306a
         shift(@tmp);
Packit 95306a
         shift(@tmp);
Packit 95306a
Packit 95306a
         _print_pod_row($pod,0,   5,$first,15,  2,$zone,0);
Packit 95306a
         my $opts2 = 'list,hashval';
Packit 95306a
         $opts2   .= ',firstlist'  if ($first);
Packit 95306a
         $opts2   .= ',lastlist'   if (! @tmp);
Packit 95306a
         _print_mod_row($out,     2,$first,12,$opts1,   2,$zone,0,$opts2);
Packit 95306a
Packit 95306a
         $first = '';
Packit 95306a
         $opts1 = 'hashkey,noquote';
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
);
Packit 95306a
";
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _do_zones_offsets {
Packit 95306a
   my($out,$pod) = @_;
Packit 95306a
Packit 95306a
   my $offmod = _yaml_read("tzdata/_offmod");
Packit 95306a
Packit 95306a
   # Start the offset output (in this case, no POD output since it
Packit 95306a
   # doesn't seem usefule.
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
\%Offmod = (
Packit 95306a
";
Packit 95306a
Packit 95306a
   foreach my $offset (sort keys %$offmod) {
Packit 95306a
      my $mod = $$offmod{$offset};
Packit 95306a
      _print_mod_row($out,   2,$offset,10,'hashkey',   2,$mod,0,'hashval');
Packit 95306a
   }
Packit 95306a
Packit 95306a
   print $out "
Packit 95306a
);
Packit 95306a
";
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# PRINT OUT POD AND MODULE LINES
Packit 95306a
Packit 95306a
sub _print_pod_row {
Packit 95306a
   my($out,$header,@cols) = @_;
Packit 95306a
   my $under = '';
Packit 95306a
Packit 95306a
   while (@cols) {
Packit 95306a
      my $indent = shift(@cols);
Packit 95306a
      my $val    = shift(@cols);
Packit 95306a
      my $wid    = shift(@cols);
Packit 95306a
      $wid       = length($val)  if (! $wid);
Packit 95306a
Packit 95306a
      print $out ' 'x$indent,$val,' 'x($wid-length($val));
Packit 95306a
      $under .= ' 'x$indent . '-'x$wid;
Packit 95306a
   }
Packit 95306a
   print $out "\n";
Packit 95306a
   print $out "$under\n"  if ($header);
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _print_mod_row {
Packit 95306a
   my($out,@cols) = @_;
Packit 95306a
Packit 95306a
   while (@cols) {
Packit 95306a
      my $indent = shift(@cols);
Packit 95306a
      my $val    = shift(@cols);
Packit 95306a
      my $wid    = shift(@cols);
Packit 95306a
      my $opts   = shift(@cols);
Packit 95306a
Packit 95306a
      $val       = "'$val'"   unless ($opts =~ /noquote/);
Packit 95306a
      $val       = lc($val)   unless ($opts =~ /nocase/);
Packit 95306a
Packit 95306a
      if      ($opts =~ /hashkey/) {
Packit 95306a
         # nothing
Packit 95306a
Packit 95306a
      } elsif ($opts =~ /hashval/) {
Packit 95306a
Packit 95306a
         if      ($opts =~ /firstlist/  &&   $opts =~ /lastlist/) {
Packit 95306a
            $val = "=> [ $val ],";
Packit 95306a
Packit 95306a
         } elsif ($opts =~ /firstlist/) {
Packit 95306a
            $val = "=> [ $val,";
Packit 95306a
Packit 95306a
         } elsif ($opts =~ /lastlist/) {
Packit 95306a
            $val = "     $val ],";
Packit 95306a
Packit 95306a
         } elsif ($opts =~ /list/) {
Packit 95306a
            $val = "     $val,";
Packit 95306a
Packit 95306a
         } else {
Packit 95306a
            $val = "=> $val,";
Packit 95306a
         }
Packit 95306a
Packit 95306a
      } else {
Packit 95306a
         $val   .= ',';
Packit 95306a
      }
Packit 95306a
Packit 95306a
      $wid       = length($val)  if (! $wid);
Packit 95306a
Packit 95306a
      print $out ' 'x$indent,$val,' 'x($wid-length($val));
Packit 95306a
   }
Packit 95306a
   print $out "\n";
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DEALING WITH THE ORDER OF ELEMENTS
Packit 95306a
Packit 95306a
# This takes a hash:
Packit 95306a
#    $in  = { ELE => SUBELE => [YEAR1,YEAR2] }
Packit 95306a
# and returns a hash of the form:
Packit 95306a
#    $out = { ELE => [ SUBELE, YEAR1, YEAR2,
Packit 95306a
#                      SUBELE, YEAR1, YEAR2, ... ] }
Packit 95306a
#
Packit 95306a
# The order of the elements sorted based on the range.
Packit 95306a
#
Packit 95306a
sub _order_elements {
Packit 95306a
   my($in) = @_;
Packit 95306a
   my $out;
Packit 95306a
Packit 95306a
   foreach my $ele (keys %$in) {
Packit 95306a
      my @in = _sort_by_years($$in{$ele});
Packit 95306a
Packit 95306a
      $$out{$ele} = [];
Packit 95306a
      foreach my $subele (@in) {
Packit 95306a
         my($year1,$year2) = @{ $$in{$ele}{$subele} };
Packit 95306a
         push(@{ $$out{$ele} },($subele,$year1,$year2));
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
Packit 95306a
   return $out;
Packit 95306a
}
Packit 95306a
Packit 95306a
# This sorts the keys of a hash of the form:
Packit 95306a
#   $hash = { ELE => [YEAR1,YEAR2] }
Packit 95306a
# by years.
Packit 95306a
#
Packit 95306a
# o  An element that is active now always comes before one that isn't
Packit 95306a
#    active now.
Packit 95306a
#       i.e. [2000-2020] < [1900-2000]  (now = 2010)
Packit 95306a
#
Packit 95306a
# o  A modern European timezone (WET, CET, EET) comes before others.
Packit 95306a
#
Packit 95306a
# o  An Antarctica element comes after one that is not Antarctica
Packit 95306a
#
Packit 95306a
# o  A military timezone (A-Z) comes after one that is not
Packit 95306a
#
Packit 95306a
# o  A UT/UTC/*GMT* timezone comes after one that is not
Packit 95306a
#
Packit 95306a
# o  An element that is active later comes before one that is active
Packit 95306a
#    earlier.
Packit 95306a
#       i.e. [X-1970] < [X-1960]; [X-2040] < [X-2020]
Packit 95306a
#
Packit 95306a
# o  An element that is active further in the past comes before one
Packit 95306a
#    that is active later.
Packit 95306a
#       i.e. [1930-X] < [1940-X]
Packit 95306a
#
Packit 95306a
# o  Alphabetize the rest.
Packit 95306a
#
Packit 95306a
sub _sort_by_years {
Packit 95306a
   my($hash) = @_;
Packit 95306a
Packit 95306a
   return sort { __sort_by_years($$hash{$a},$$hash{$b},$a,$b) } keys %$hash;
Packit 95306a
}
Packit 95306a
sub __sort_by_years {
Packit 95306a
   my($a,$b,$namea,$nameb) = @_;
Packit 95306a
Packit 95306a
   # Find out which elements are currently active
Packit 95306a
   my $curra = ($$a[0] <= $curry  &&  $$a[1] >= $curry ? 1 : 0);
Packit 95306a
   my $currb = ($$b[0] <= $curry  &&  $$b[1] >= $curry ? 1 : 0);
Packit 95306a
Packit 95306a
   # An element that is active now always comes before one that isn't
Packit 95306a
   # active now.
Packit 95306a
   if ($curra != $currb) {
Packit 95306a
      return -1  if ($curra);
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # A modern European timezone (WET, CET, EET) comes before others.
Packit 95306a
   if      ($namea =~ /^(WET|CET|EET)$/) {
Packit 95306a
      return -1;
Packit 95306a
   } elsif ($nameb =~ /^(WET|CET|EET)$/) {
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # An Antarctica element comes after one that is not Antarctica
Packit 95306a
   if ($namea =~ /Antarctica/) {
Packit 95306a
      if ($nameb =~ /Antarctica/) {
Packit 95306a
         return $namea cmp $nameb;
Packit 95306a
      } else {
Packit 95306a
         return 1;
Packit 95306a
      }
Packit 95306a
   } elsif ($nameb =~ /Antarctica/) {
Packit 95306a
      return -1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # A military timezone (A-Z) comes after one that is not
Packit 95306a
Packit 95306a
   if ($namea =~ /^[A-Z]$/) {
Packit 95306a
      if ($nameb =~ /^[A-Z]$/) {
Packit 95306a
         return $namea cmp $nameb;
Packit 95306a
      } else {
Packit 95306a
         return 1;
Packit 95306a
      }
Packit 95306a
   } elsif ($nameb =~ /^[A-Z]$/) {
Packit 95306a
      return -1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # A UT/UTC/*GMT* timezone comes after one that is not
Packit 95306a
Packit 95306a
   if ($namea =~ /UT/  ||  $namea =~ /GMT/) {
Packit 95306a
      if ($nameb =~ /UT/  ||  $nameb =~ /GMT/) {
Packit 95306a
         return $namea cmp $nameb;
Packit 95306a
      } else {
Packit 95306a
         return 1;
Packit 95306a
      }
Packit 95306a
   } elsif ($nameb =~ /UT/  ||  $nameb =~ /GMT/) {
Packit 95306a
      return -1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # An element that is active later comes before one that is active
Packit 95306a
   # earlier.
Packit 95306a
   if ($$a[1] != $$b[1]) {
Packit 95306a
      return -1  if ($$a[1] > $$b[1]);
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # An element that is active further in the past comes before one
Packit 95306a
   # that is active later.
Packit 95306a
   if ($$a[0] != $$b[0]) {
Packit 95306a
      return -1  if ($$a[0] < $$b[0]);
Packit 95306a
      return 1;
Packit 95306a
   }
Packit 95306a
Packit 95306a
   # We'll order anything else as America < Europe < Asia < other
Packit 95306a
   my ($posa,$posb);
Packit 95306a
   if    ($namea =~ /^America/) { $posa = 1; }
Packit 95306a
   elsif ($namea =~ /^Europe/)  { $posa = 2; }
Packit 95306a
   elsif ($namea =~ /^Asia/)    { $posa = 3; }
Packit 95306a
   else                         { $posa = 4; }
Packit 95306a
   if    ($nameb =~ /^America/) { $posb = 1; }
Packit 95306a
   elsif ($nameb =~ /^Europe/)  { $posb = 2; }
Packit 95306a
   elsif ($nameb =~ /^Asia/)    { $posb = 3; }
Packit 95306a
   else                         { $posb = 4; }
Packit 95306a
   return ($posa <=> $posb)     if ($posa != $posb);
Packit 95306a
Packit 95306a
   # Alphabetize the rest
Packit 95306a
   return $namea cmp $nameb;
Packit 95306a
}
Packit 95306a
Packit 95306a
# This will warn if %curr is different than %prev.
Packit 95306a
#
Packit 95306a
sub _warn_changes {
Packit 95306a
   my($curr,$prev,$indent,$col1_len,$col2_len,$header) = @_;
Packit 95306a
Packit 95306a
   my %tmp = map { $_,1 } (keys %$curr, keys %$prev);
Packit 95306a
Packit 95306a
   foreach my $ele (sort keys %tmp) {
Packit 95306a
Packit 95306a
      if (! exists $$curr{$ele}) {
Packit 95306a
Packit 95306a
         # If the element doesn't exist in the current set of
Packit 95306a
         # elements, remove it from the previous set.
Packit 95306a
Packit 95306a
         _warn_changes_ele($header,$ele,
Packit 95306a
                           undef,$$prev{$ele},$indent,$col1_len,$col2_len);
Packit 95306a
Packit 95306a
      } elsif (! exists $$prev{$ele}) {
Packit 95306a
Packit 95306a
         # If the element doesn't exist in the old set, add it.
Packit 95306a
Packit 95306a
         _warn_changes_ele($header,$ele,
Packit 95306a
                           $$curr{$ele},undef,$indent,$col1_len,$col2_len);
Packit 95306a
Packit 95306a
      } elsif (! ref($$prev{$ele})) {
Packit 95306a
Packit 95306a
         # The previous element is defined as either a scalar:
Packit 95306a
         #    PREV_FIRST
Packit 95306a
         # The current first element must be the same.
Packit 95306a
Packit 95306a
         if ($$curr{$ele}[0] ne $$prev{$ele}) {
Packit 95306a
            _warn_changes_ele($header,$ele,
Packit 95306a
                              $$curr{$ele},$$prev{$ele},
Packit 95306a
                              $indent,$col1_len,$col2_len);
Packit 95306a
         }
Packit 95306a
Packit 95306a
      } else {
Packit 95306a
Packit 95306a
         # The previous element may be defined as a listref:
Packit 95306a
         #    [ OVERRIDE, PREV_FIRST ]
Packit 95306a
         #
Packit 95306a
         # This will warn if the current first element is not the
Packit 95306a
         # same as PREV_FIRST.
Packit 95306a
         #
Packit 95306a
         # In the second case, it will also complain if OVERRIDE is not
Packit 95306a
         # in the list.  Finally, it will reorder the list to move OVERRIDE
Packit 95306a
         # to the start of the list.
Packit 95306a
Packit 95306a
         if ($$prev{$ele}[1] ne $$curr{$ele}[0]) {
Packit 95306a
            _warn_changes_ele($header,$ele,
Packit 95306a
                              $$curr{$ele},$$prev{$ele},
Packit 95306a
                              $indent,$col1_len,$col2_len);
Packit 95306a
Packit 95306a
         } else {
Packit 95306a
            my @old = @{ $$curr{$ele} };
Packit 95306a
            my @new;
Packit 95306a
            my $found = 0;
Packit 95306a
            while (@old) {
Packit 95306a
               my $tz  = shift(@old);
Packit 95306a
               my $y1  = shift(@old);
Packit 95306a
               my $y2  = shift(@old);
Packit 95306a
               if ($tz eq $$prev{$ele}[0]) {
Packit 95306a
                  @new = ($tz,$y1,$y2,@new,@old);
Packit 95306a
                  $found = 1;
Packit 95306a
                  last;
Packit 95306a
               } else {
Packit 95306a
                  push(@new,$tz,$y1,$y2);
Packit 95306a
               }
Packit 95306a
            }
Packit 95306a
            if ($found) {
Packit 95306a
               $$curr{$ele} = [@new];
Packit 95306a
            } else {
Packit 95306a
               _warn_changes_ele($header,$ele,
Packit 95306a
                                 $$curr{$ele},$$prev{$ele}[1],
Packit 95306a
                                 $indent,$col1_len,$col2_len);
Packit 95306a
            }
Packit 95306a
         }
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _warn_changes_ele {
Packit 95306a
   my($header,$ele,$curr,$prev,$indent,$col1_len,$col2_len) = @_;
Packit 95306a
Packit 95306a
   my $val;
Packit 95306a
   if (! defined($curr)) {
Packit 95306a
      warn "*** REMOVE ***\n";
Packit 95306a
      if (ref($prev)) {
Packit 95306a
         $val = "[ $$prev[0], $$prev[1] ]";
Packit 95306a
      } else {
Packit 95306a
         $val = $prev;
Packit 95306a
      }
Packit 95306a
   } elsif (! defined($prev)) {
Packit 95306a
      warn "*** NEW ELEMENT ***\n";
Packit 95306a
      $val = $$curr[0];
Packit 95306a
   } else {
Packit 95306a
      warn "*** CHANGE ELEMENT ***\n";
Packit 95306a
      if (ref($prev)) {
Packit 95306a
         $val = "[ $$prev[0] => $$curr[0] ]";
Packit 95306a
      } else {
Packit 95306a
         $val = $$curr[0];
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
   warn "$header\n"  if (defined $header);
Packit 95306a
Packit 95306a
   $ele = "'$ele'";
Packit 95306a
   warn " "x$indent, $ele," "x($col1_len-length($ele)),"=> '$val'\n";
Packit 95306a
Packit 95306a
   if (defined($curr)) {
Packit 95306a
      my @tmp = @$curr;
Packit 95306a
      while (@tmp) {
Packit 95306a
         my $subele = shift(@tmp);
Packit 95306a
         my $year1  = shift(@tmp);
Packit 95306a
         my $year2  = shift(@tmp);
Packit 95306a
         my($col2)  = "'$subele'";
Packit 95306a
         my($col3)  = ",$year1,$year2,";
Packit 95306a
Packit 95306a
         warn "#"," "x($indent + $col1_len + 3), $col2,
Packit 95306a
           " "x($col2_len-length($col2)),"$col3\n";
Packit 95306a
      }
Packit 95306a
   }
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
# DO_CLEAN
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
sub do_clean {
Packit 95306a
   print "Cleaning...\n";
Packit 95306a
   system("rm -rf tzdata* tzcode*");
Packit 95306a
}
Packit 95306a
Packit 95306a
############################################################################
Packit 95306a
Packit 95306a
sub _yaml_read {
Packit 95306a
   my($file) = @_;
Packit 95306a
   return {}  if (! -e $file);
Packit 95306a
   my($data) = YAML::LoadFile($file);
Packit 95306a
   return {}  if (! defined $data);
Packit 95306a
   return $data;
Packit 95306a
}
Packit 95306a
Packit 95306a
sub _yaml_write {
Packit 95306a
   my($data,$file,$backup) = @_;
Packit 95306a
Packit 95306a
   rename($file,"$file.bak")  if ($backup  &&  -e $file);
Packit 95306a
   YAML::DumpFile($file,$data);
Packit 95306a
}
Packit 95306a
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: