#!/usr/bin/perl -w
############# dump unix zoneinfo/* file(s) ##################################################
use Config;
use strict;
#############################################################################################
my $DEBUG = 2;

my @mnames = ('Jan','Feb', 'Mar','Apr','May', 'Jun','Jul','Aug', 'Sep','Oct','Nov', 'Dec');
my @wdays =  ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');

my @months = #[365]
( 3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3, 3                       #Mar
, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4                          #Apr
, 5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5, 5                       #May
, 6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6                          #Jun
, 7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7, 7                       #Jul
, 8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8, 8                       #Aug
, 9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9                          #Sep
, 10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10, 10 #Oct
, 11,11,11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11     #Nov
, 12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12, 12 #Dec
, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1                       #Jan
, 2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2                              #Feb
);

my @mdays = #[365]
( 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Mar
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30     #Apr
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #May
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30     #Jun
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Jul
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Aug
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30     #Sep
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Oct
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30     #Nov
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Dec
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30, 31 #Jan
, 1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28           #Feb
);

my @moff = #[12+1]
( -28-31                         #Jan -59 (leap: -60)
, -28                            #Feb -28 (leap: -29)
, 0                              #Mar 0
, 31                             #Apr 31
, 31+30                          #May 61
, 31+30+31                       #Jun 92
, 31+30+31 + 30                  #Jul 122
, 31+30+31 + 30+31               #Aug 153
, 31+30+31 + 30+31+31            #Sep 184
, 31+30+31 + 30+31+31 + 30       #Oct 214
, 31+30+31 + 30+31+31 + 30+31    #Nov 245
, 31+30+31 + 30+31+31 + 30+31+30 #Dec 275
, 31+30+31 + 30+31+31 + 30+31+30 + 31
);

my ($base_year, $base_month, $base_mday, $base_wday) = (1600,3,1, 3); # 01-Mar-1600 Wednesday

my $days_to_1601 = 366 - 31 - 29;            # distance to 01-Jan-1601
my $days_to_1970 = $days_to_1601 + 134_774;  # distance to 01-Jan-1970 == 135_080

my $Y1 = 365;
my $Y4 = 4*$Y1 + 1;
my $Y100 = 25*$Y4 - 1;
my $Y400 = 4*$Y100 + 1;

#--------------------------------------------------------------------------------------------

sub days_to_date($)
{
   my ($days) = @_;
   if ( $days < 0 ) {
      return (-1,-1,-1, -1) if ( wantarray() );
      return "Out of Range";
   }

   my ($year,$month,$day, $wday,$yday) = ($base_year,2,29, (($days % 7) + $base_wday) % 7, 31+28);

   my $n400 = int($days / $Y400); $days -= $n400 * $Y400;
   if ( $days == $Y400-1 ) {
      $year += 400*$n400 + 400;
   }
   else {
      my $n100 = int($days / $Y100); $days -= $n100 * $Y100;
      my $n4   = int($days / $Y4  ); $days -= $n4   * $Y4;
      if ( $days == $Y4-1 ) {
         $year += 400*$n400 + 100*$n100 + 4*$n4 + 4;
      }
      else {
         my $n1 = int($days / $Y1); $days -= $n1 * $Y1;
         ($month, $day) = ($months[$days], $mdays[$days]);
         $year += 400*$n400 + 100*$n100 + 4*$n4 +$n1 + ($month < 3 ? 1 : 0);
         my $leap_year = (($year % 4) == 0) && ((($year % 100) != 0) || (($year % 400) == 0));
         $yday = 31+28 + $moff[$month-1] + $day-1 + ($leap_year && $month >= 3);
      }
   }
   

   return ($year,$month,$day, $wday,$yday) if ( wantarray() );
   return sprintf("%02d %s %d, %s", $day, $mnames[$month-1], $year, $wdays[$wday]);
}

sub date_to_days($$$)
{
   my ($year, $month, $day) = @_;
   --$month;
   if ((my $neg = ($month < 0)) || $month >= 12) {
   #  $year += int($month/12); if (($month %= 12) < 0) { $month += 12; --$year; } # C
      $year += int($month/12); $month %= 12; if ( $neg && $month ) { --$year; }   # Perl
   }

   my $leap_year = (($year % 4) == 0) && ((($year % 100) != 0) || (($year % 400) == 0));
   return -1 if ( ($year -= $base_year) < 0 );

   my $days = 365*$year + int($year/4) - int($year/100) + int($year/400);
   my $moff = $moff[$month] - $leap_year*($month < 2);
   return $days + $moff + $day-1;
}

if ($DEBUG >= 2) {
   for my $t (
      0, 1, 31, 32, $days_to_1601-1, $days_to_1601, $days_to_1601+1,
      $days_to_1601+31, $days_to_1601+32, $days_to_1601+364, $days_to_1601+365,
      $Y1-1,$Y1,$Y1+1, 2*$Y1-1,2*$Y1,2*$Y1+1, $Y4-1,$Y4,$Y4+1, 2*$Y100-1,2*$Y100,2*$Y100+1,
      $Y400-1,$Y400,$Y400+1, 2*$Y400+3*$Y100+10*$Y4+2*$Y1+31+30+10,
      $days_to_1970-1, $days_to_1970, $days_to_1970+1, $days_to_1970+365,
      $days_to_1970 + 8094 #, 1_000_000_000
   ) {
      my ($y, $m, $d, $wd, $yd) = days_to_date($t);
      my $ttt = date_to_days($y, $m, $d); die "$t!=$ttt $y/$m/$d" if ($t != $ttt);
      my $ss = days_to_date($t);
      print sprintf("%-6d -- %s UTC yday=%d\n", $t, $ss, $yd);
   }
   my $d1900 = date_to_days(1900, 1, 1);
   my $d1970 = date_to_days(1970, 1, 1);
   print "d1900=$d1900 d1970=$d1970 diff=", $d1970-$d1900, "\n";
}

#--------------------------------------------------------------------------------------------

sub ftime_to_datetime($)
{
   my ($ftime) = @_;
   if ( $ftime < 0 ) {
      return (-1,-1,-1, -1, -1,-1,-1, -1) if ( wantarray() ); 
      return "Out of Range";
   }
   my $secs = int($ftime / 10_000_000);
   my ($year,$month,$day, $wday,$yday) = days_to_date(int($secs/(24*3600)) + $days_to_1601);

   my $hours = int($secs / 3600) % 24;
   my $mins = int($secs / 60) % 60;  
   $secs %= 60;
   my $msec = int(($ftime % 10_000_000) / 10_000);

   return ($year,$month,$day, $hours,$mins,$secs, $msec, $wday,$yday) if ( wantarray() );
   return sprintf(
      "%02d %s %d, %s %02d:%02d:%02d.%03d",
      $day, $mnames[$month-1], $year, $wdays[$wday], $hours,$mins,$secs, $msec
   );
}

sub datetime_to_ftime($$$;$$$$)
{
   my ($year,$month,$day, $hour,$min,$sec, $msec) = @_;
   $hour = $hour || 0; $min = $min || 0; $sec = $sec || 0; $msec = $msec || 0;
   my $neg;

   if (($neg = ($msec < 0)) || $msec >= 1_000) {
      $sec += int($msec/1_000); $msec %= 1_000; if ( $neg && $msec ) { --$sec; }
   }
   if (($neg = ($sec < 0)) || $sec >= 60) {
      $min += int($sec/60); $sec %= 60; if ( $neg && $sec ) { --$min; }
   }
   if (($neg = ($min < 0)) || $min >= 60) {
      $hour += int($min/60); $min %= 60; if ( $neg && $min ) { --$hour; }
   }
   if (($neg = ($hour < 0)) || $hour >= 24) {
      $day += int($hour/24); $hour %= 24; if ( $neg && $hour ) { --$day; }
   }

   my $days = date_to_days($year, $month, $day);
   return -1 if ($days < 0);
   return 10_000*($msec + 1_000*($sec + 60*($min + 60*($hour + 24*($days-$days_to_1601)))));
}

if ($DEBUG >= 2) {
   for my $t (0, 134_774 * 24*3600 * 10_000_000 + 1_230_000 + 7201*10_000_000) {
      my ($y,$m,$d, $h,$mi,$s, $ms) = ftime_to_datetime($t);
      my $ttt = datetime_to_ftime($y,$m,$d, $h,$mi,$s, $ms);
      die "$t!=$ttt $y/$m/$d $h:$mi:$s.$ms" if ($t != $ttt);
      my $ss = ftime_to_datetime($t);
      print sprintf("%-18d --- %s UTC\n", $t, $ss);
   }
}

#--------------------------------------------------------------------------------------------

sub unixtime_to_datetime($)
{
   my ($secs) = @_;

   my ($days, $year,$month,$day, $wday,$yday, $hours,$mins);
   if ( $secs >= 0 ) {
      $days = int($secs / (24*3600));
   }
   else {
      my $t = $secs % (24*3600); # $t += 24*3600 if ( $t < 0 ); # it's Perl...
      $days = int(($secs - $t) / (24*3600)); $secs = $t;
   }

   ($year,$month,$day, $wday,$yday) = days_to_date($days + $days_to_1970);
   if ( $wday < 0 ) {
      return (-1,-1,-1, -1, -1,-1,-1) if ( wantarray() ); 
      return "Out of Range";
   }

   $hours = int($secs / 3600) % 24;
   $mins = int($secs / 60) % 60;  
   $secs %= 60;

   return ($year,$month,$day, $hours,$mins,$secs, $wday,$yday) if ( wantarray() );
   return sprintf(
      "%02d %s %d, %s %02d:%02d:%02d",
      $day, $mnames[$month-1], $year, $wdays[$wday], $hours,$mins,$secs
   );
}

sub datetime_to_unixtime($$$;$$$)
{
   my ($year,$month,$day, $hour,$min,$sec) = @_;
   $hour = $hour || 0; $min = $min || 0; $sec = $sec || 0;
   my $neg;

   if (($neg = ($sec < 0)) || $sec >= 60) {
      $min += int($sec/60); $sec %= 60; if ( $neg && $sec ) { --$min; }
   }
   if (($neg = ($min < 0)) || $min >= 60) {
      $hour += int($min/60); $min %= 60; if ( $neg && $min ) { --$hour; }
   }
   if (($neg = ($hour < 0)) || $hour >= 24) {
      $day += int($hour/24); $hour %= 24; if ( $neg && $hour ) { --$day; }
   }

   my $days = date_to_days($year, $month, $day);
   die "Out of Range" if ($days < 0); #return -1 if ($days < 0);
   return $sec + 60*($min + 60*($hour + 24*($days-$days_to_1970)));
}

if ($DEBUG >= 2) {
   for my $t (0, 1, -1, 0x7fffffff, -0x80000000, time()-12*3600, 0x29AFAE00) {
      my ($y,$m,$d, $h,$mi,$s) = unixtime_to_datetime($t);
      my $ttt = datetime_to_unixtime($y,$m,$d, $h,$mi,$s);
      die "$t!=$ttt $y/$m/$d $h:$mi:$s" if ($t != $ttt);
      my $ss = unixtime_to_datetime($t);
      print sprintf("%-11d ---- %s UTC\n", $t, $ss);
   }
}

exit 0 if ($DEBUG >= 2);
#############################################################################################

sub mkt($) { my $s = unixtime_to_datetime($_[0]); return $s; }

sub mkts($$)
{
   my ($pref, $toff) = @_;
   $toff = int($toff /60);
   return "${pref}0" unless $toff;
   if ($toff > 0) { $pref .= '-' unless ($pref); } else { $toff = -$toff; }
   return $pref . $toff/60 if (($toff % 60) == 0);
   return $pref . sprintf("%d:%02d", int($toff/60), $toff % 60);
}

sub mkdlt($$)
{
   my ($t, $o) = @_;
   my ($y,$m,$d, $h,$mi,$s, $wd) = unixtime_to_datetime($t + $o);
   my $wn = 1 + int(($d - 1)/7);
   if ( $wn == 4 ) { # maybe 'last week'
      my $mlen = $moff[$m] - $moff[$m-1];
      if ($mlen == 28) {
         my $leap_year = (($y % 4) == 0) && ((($y % 100) != 0) || (($y % 400) == 0));
         $mlen += $leap_year;
      }
      $wn = 5 if ($d + 7 > $mlen);
   }
   my $dlt = ",M$m.$wn.$wd";
   $dlt .= mkts("/", 60*(60*$h+$mi)) if ($h != 2 || $mi != 0);
   return $dlt;
}

sub make_tz(\@$\@$)
{
   my ($types, $nty, $times, $ntm) = @_;
   die "Empty transition types" unless $nty > 0;

   my ($j1,$j11, $j2,$j22, $t1, $t2) = (0,-1, -1,-1, 0, 0);
   my ($s,$mi,$h, $d,$m,$y) = gmtime(time());
   my ($b, $e) = (datetime_to_unixtime($y+1900,1,1), datetime_to_unixtime($y+1900+1,1,1));

   for (my $i = $ntm-1; $i >= 0; --$i) {
      $t1 = $times->[$i]->{time};
      next if ( $t1 >= $e );
      $j1 = $times->[$i]->{type};
      if ( $i > 0 && $t1 < $e && $t1 >= $b ) {
         $t2 = $times->[$i-1]->{time};
         if ($t2 >= $b && $t2 < $e) {
            $j2 = $j11 = $times->[$i-1]->{type};
            $j22 = $i >= 2 ? $times->[$i-2]->{type} : $j1;
         }
      } 
      last;
   }

   if ( $j2 >= 0 && !$types->[$j2]->{is_dst} && $types->[$j1]->{is_dst} )
   { my $t=$j1; $j1=$j2; $j2=$t;  $t=$t1; $t1=$t2; $t2=$t;  $t=$j11; $j11=$j22; $j22=$t; } 

   my $tz = $types->[$j1]->{abbr};
   $tz = "<$tz>" if ($tz =~ m!^GMT(\+|\-)\d+$!o);
   $tz .= mkts('', $types->[$j1]->{gmt_off});
   return $tz unless $j2 >= 0;

   $tz .= $types->[$j2]->{abbr};
   $tz .= mkts('', $types->[$j2]->{gmt_off}) if ($types->[$j2]->{gmt_off}-$types->[$j1]->{gmt_off} != 3600);

   $tz .= mkdlt($t2, $types->[$j22]->{gmt_off}) . mkdlt($t1, $types->[$j11]->{gmt_off});
   return $tz;
}

#--------------------------------------------------------------------------------------------

sub dump_file($;$)
{
   my ($file,$mode, $data, $i,$j, $t,$t1,$t2, $r, $tz,$tz1);
   ($file, $mode) = @_; $mode = $mode || 'd';
   print "$file:\n";

   open(IN, "<:raw", $file) || die "Can't open file: $file -- $!";
   { local $/; $data = <IN>;  close(IN); }
   my ($pos, $len, $O, $On, $sig,$ver,$res) = (0, length($data), 'l>', 4);

   for (;;) {
      die "bad zoneinfo file length ($file)" if ( $len < $pos + 4+16 + 6*4 );

      ($sig, $ver, $res) = (substr($data,$pos,4), substr($data,$pos+4,1), substr($data,$pos+5,15));
      if ($mode eq 'd') { print "  sig='$sig' ver='$ver'"; print " res='$res'" if (0); print "\n"; }
      die "bad zoneinfo signature: $sig ($file)" if $sig ne "TZif";
      my (
         $tzh_ttisgmtcnt, $tzh_ttisstdcnt, $tzh_leapcnt, $tzh_timecnt, $tzh_typecnt, $tzh_charcnt
      ) = unpack "NNNNNN", substr($data, $pos+20, 6*4);
      if ($mode eq 'd' ) { 
         print "  tzh_ttisgmtcnt=$tzh_ttisstdcnt tzh_ttisstdcnt=$tzh_ttisstdcnt tzh_leapcnt=$tzh_leapcnt";
         print  " tzh_timecnt=$tzh_timecnt tzh_typecnt=$tzh_typecnt tzh_charcnt=$tzh_charcnt\n";
      }
      $pos += 4 + 16 + 6*4;
      my (@transitions, @transition_types, $abbr, @leaps);

      for ( $i = 0; $i < $tzh_timecnt; ++$i ) { 
         ($t) = unpack "$O", substr($data, $pos, $On); $pos += $On;
         $transitions[$i] = { time => $t };
      }
      for ( $i = 0; $i < $tzh_timecnt; ++$i ) { 
         ($t) = unpack "C", substr($data, $pos, 1); $pos += 1;
         $r = $transitions[$i]; $r->{type} = $t;
      }
      for  ( $i = 0; $i < $tzh_typecnt; ++$i ) {
         ($t,$t1,$t2) = unpack "l>CC", substr($data, $pos, 6); $pos += 4 + 2;
         $transition_types[$i] = { gmt_off => $t, is_dst => $t1, abbr_idx => $t2 };
      }
      $abbr = substr($data, $pos, $tzh_charcnt); $pos += $tzh_charcnt; 
      for ( $i = 0; $i < $tzh_typecnt; ++$i )
      { $transition_types[$i]->{abbr} = unpack("Z*", substr($abbr,$transition_types[$i]->{abbr_idx},10));}
      for ( $i = 0; $i < $tzh_leapcnt; ++$i ) {
         ($t, $t1) = unpack "${O}N", substr($data, $pos, $On+4); $pos += $On + 4;
         $leaps[$i] = { when => $t, add => $t1 };
      }
      for ( $i = 0; $i < $tzh_ttisstdcnt; ++$i ) { 
         ($t) = unpack "C", substr($data, $pos, 1); $pos += 1;
         $r = $transition_types[$i]; $r->{is_std} = $t;
      }
      for ( $i = 0; $i < $tzh_ttisgmtcnt; ++$i ) { 
         ($t) = unpack "C", substr($data, $pos, 1); $pos += 1;
         $r = $transition_types[$i]; $r->{is_gmt} = $t;
      }

      if ( $mode eq 'd' ) { 
         for ($i = 0; $i < $tzh_typecnt; ++$i) {
            my $abbr = $transition_types[$i]->{abbr}; 
            print sprintf("   ttype #%02d: gmt_off=%-6d is_dst=%d abbr=%-4s is_std=%d is_gmt=%d\n",
               $i, $transition_types[$i]->{gmt_off}, $transition_types[$i]->{is_dst}, $abbr,
               $transition_types[$i]->{is_std}, $transition_types[$i]->{is_gmt}
            ); 
         }
         for ($i = 0; $i < $tzh_timecnt; ++$i) { 
            print sprintf("   trans #%03d: time=%sZ type=%d\n",
               $i, mkt($transitions[$i]->{time}), $transitions[$i]->{type} ); 
         }
         print "pos=$pos\n" if 0;
         print "\n";
      }
      else { # 't' --tz
         $tz1 = make_tz(@transition_types, $tzh_typecnt, @transitions, $tzh_timecnt);
         die "TZ: $tz != $tz1" if ($tz && ($tz ne $tz1));
         $tz = $tz1;
      }

      last if ( $pos >= $len || $On == 8 );
      ($O, $On) = ('q>', 8);
      die "This perl version doen't work with 8-byte integers" unless $Config{ivsize} == 8;
   }
   if ( $pos < $len && $ver ge '2' && substr($data,$pos,1) eq "\n" ) {
      if ($mode eq 'd') { print "  TZ=", substr($data,$pos+1), "\n"; }
      else {
         my $f = substr($data, $pos);
         if ( $f ne "\n\n" ) {
            die "bad final TZ($file): $f" if ( $f !~ m!\n([A-Z<][A-Za-z0-9\.>,/:+-]+)\n!om );
            $tz1 = $1;
            die "final TZ($file): $tz1 != $tz" if ($tz ne $tz1);
         }
      }
   }
   print "  TZ=$tz\n" if ($mode ne 'd');
}

#############################################################################################

sub gmoff($$$$$)
{
   my ($y,$m,$d, $h,$mi, $s) = @_;
   my $gm_t = datetime_to_unixtime($y,$m,$d, $h,$mi,0);
   ($s,$mi,$h, $d,$m) = localtime($gm_t);
   my $lc_t = datetime_to_unixtime($y,$m+1,$d, $h,$mi,$s);
   return $lc_t - $gm_t;
}

sub find_change($$$$$$)
{
   my ($y, $m1,$d1, $m2,$d2, $off) = @_;
   my $b = datetime_to_unixtime($y,$m1,$d1,  0, 0,0);
   my $e = datetime_to_unixtime($y,$m2,$d2, 23,59,0);
   while ( $e-$b > 60 ) {
      my $t = $b + 60 * int(($e-$b)/120);
      my ($s,$mi,$h, $d,$m) = localtime($t);
      my $o = datetime_to_unixtime($y,$m+1,$d, $h,$mi,$s) - $t;
      if ( $o == $off ) { $b = $t; } else { $e = $t; }
   }
   return $e;
}

sub default_tz()
{
   print "Current:\n";
   my ($s,$mi,$h, $d,$m,$y) = localtime(time()); $y += 1900;

   my ($jan_off, $jul_off, $tz) = (gmoff($y,1,1, 0,0), gmoff($y,7,1, 0,0));
   if ( $jan_off == $jul_off ) {
      $tz = "STD" . mkts("", $jul_off); 
   }
   else {
      my ($dlt, $north) = ($jul_off-$jan_off, 1);
      ($dlt, $north) = (-$dlt, 0) if ($dlt < 0);
      $tz = "STD" . mkts('', $north ? $jan_off : $jul_off) . "DLT";
      $tz .= mkts('', $north ? $jul_off : $jan_off) if ($dlt != 60*60);

      my $shift1 = find_change($y, 1,1,  6,30, $jan_off);
      my $shift2 = find_change($y, 7,1, 12,31, $jul_off);

      if ( $north ) { $tz .= mkdlt($shift1, $jan_off) . mkdlt($shift2, $jul_off); }
      else          { $tz .= mkdlt($shift2, $jul_off) . mkdlt($shift1, $jan_off); }
   }
   print "  TZ=$tz\n";
}

#############################################################################################

if (@ARGV && $ARGV[0] =~ m!^--?(def|default|deftz)$!io) { default_tz(); exit 0; }
my $mode ='d'; if (@ARGV && $ARGV[0] =~ m!^--?(tz|timezone)$!io) { $mode= 't'; shift @ARGV; }
die "\nusage:\n  zdump.pl [--tz] zoneinf0-file ...\n  zdump.pl --def\n" if !@ARGV;
foreach my $f (@ARGV) { dump_file($f, $mode); }

#############################################################################################
exit 0;
