diff options
Diffstat (limited to 'libjava/classpath/scripts/timezones.pl')
-rwxr-xr-x | libjava/classpath/scripts/timezones.pl | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/libjava/classpath/scripts/timezones.pl b/libjava/classpath/scripts/timezones.pl new file mode 100755 index 00000000000..142ea4b7582 --- /dev/null +++ b/libjava/classpath/scripts/timezones.pl @@ -0,0 +1,366 @@ +#!/usr/bin/perl -w +# Create the timezone tables for java/util/TimeZone from the +# standard timezone sources by Arthur David Olson (as used by glibc) +# +# This needs the files from the package tzdata2000h which may be found +# at ftp://ftp.cs.mu.oz.au/pub/. + +$TIMEZONEDIR = "tzdata"; +@TIMEZONEFILES = ("africa", "antarctica", "asia", "australasia", + "europe", "northamerica", "pacificnew", "southamerica", + "../tzabbrevs"); + +# rules hash table: +# key is a rule name +# value is either "-" (no daylight savings) or a list of three elements: +# $value[0] = end savings rule (list containing MONTH, DAY and TIME) +# $value[1] = start savings rule (ditto) +# $value[2] = daylight offset in milliseconds +my %rules = ("-" => "-"); + +# timezones list, list of pairs: +# $timezones[$i][0] is a timezone name +# $timezones[$i][1] = raw offset in milliseconds +# $timezones[$i][2] = rule in the same format as the value of +# the rules table, but TIME in milliseconds +# $timezones[$i][3] = list of timezone names with this rule (aliases) +my @timezones = ( [ "GMT", 0, "-", [ "GMT", "UTC" ] ]); + + +# parse the offset of form +/-hh:mm:ss (:ss is optional) and return it +# in milliseconds against UTC +sub parseOffset($) { + my $offset = $_[0]; + $offset =~ /^([+-]?)(\d+)(:(\d+)(:(\d+))?)?$/ + or die "Can't parse offset $offset"; + my $seconds = $2 * 3600; + $seconds += $4 * 60 if ($3); + $seconds += $6 if ($3 && $5); + if ($1 eq "-") { + $seconds = - $seconds; + } + return $seconds * 1000; +} + +# parse the time of form +/-hh:mm:ss[swguz] (:ss is optional) and return it +# in milliseconds since midnight in local wall time + my $timezonename; +sub parseTime($$$) { + my ($rawoffset, $stdoffset, $time) = @_; + $time =~ /^([+-]?)(\d+):(\d+)(:(\d+))?([swguz]?)$/ + or die "Can't parse time $time"; + my ($hour, $min) = ($2, $3); + my $sec = ($4) ? $5 : 0; + my $millis = ((($hour * 60) + $min) * 60 + $sec) * 1000; + if ($1 eq "-") { + $millis = -$millis; + } + # Normally millis is in wall time, adjust for utc and standard time. + if ($6 =~ /[guz]/) { + $millis += $rawoffset + $stdoffset; + } elsif ($6 =~ /s/) { + $millis += $stdoffset; + } + return $millis; +} + +my %monthnames = + ( "Jan" => "1", + "Feb" => "2", + "Mar" => "3", + "Apr" => "4", + "May" => "5", + "Jun" => "6", + "Jul" => "7", + "Aug" => "8", + "Sep" => "9", + "Oct" => "10", + "Nov" => "11", + "Dec" => "12" ); +sub parseMonth($) { + my $month = $monthnames{"$_[0]"} or die "Unknown month $_[0]"; + return $month; +} + +my %weekdaynames = + ( "Sun" => "7", + "Mon" => "1", + "Tue" => "2", + "Wed" => "3", + "Thu" => "4", + "Fri" => "5", + "Sat" => "6" ); +sub parseWeekday($) { + my $weekday = $weekdaynames{"$_[0]"} or die "Unknown weekday $_[0]"; + return $weekday; +} + +my @weekdayjavanames = + ( "Calendar.SUNDAY", + "Calendar.MONDAY", + "Calendar.TUESDAY", + "Calendar.WEDNESDAY", + "Calendar.THURSDAY", + "Calendar.FRIDAY", + "Calendar.SATURDAY" ); +my @daysInMonths = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); +sub parseDay($$$) { + my ($dayoffset, $month, $day) = @_; + if ($day =~ /^\d+$/) { + return "$day, 0"; + } elsif ($day =~ /^last([A-Z][a-z][a-z])$/) { + my $weekday = ( parseWeekday($1) + $dayoffset + 7 ) % 7; + if ($dayoffset) { + my $day = $daysInMonths[$month - 1] + $dayoffset; + warn "Can only approximate $day with dayoffset in $file" + if ($month == 2); + return "$day, -$weekdayjavanames[$weekday]"; + } else { + return "-1, $weekdayjavanames[$weekday]"; + } + } elsif ($day =~ /^([A-Z][a-z][a-z])>=(\d+)$/) { + my $start = $2 + $dayoffset; + my $weekday = ( parseWeekday($1) + $dayoffset + 7 ) % 7; + if (($start % 7) == 1) { + $start = ($start + 6) / 7; + return "$start, $weekdayjavanames[$weekday]"; + } else { + return "$start, -$weekdayjavanames[$weekday]"; + } + } else { + die "Unknown day $day"; + } +} + +my @monthjavanames = + ( "Calendar.JANUARY", + "Calendar.FEBRUARY", + "Calendar.MARCH", + "Calendar.APRIL", + "Calendar.MAY", + "Calendar.JUNE", + "Calendar.JULY", + "Calendar.AUGUST", + "Calendar.SEPTEMBER", + "Calendar.OCTOBER", + "Calendar.NOVEMBER", + "Calendar.DECEMBER" ); + +sub parseRule($$$) { + my ($rawoffset, $stdoffset, $rule) = @_; + my $monthnr = parseMonth($rule->[0]); + my $time = parseTime($rawoffset, $stdoffset, $rule->[2]); + my $dayoffset = 0; + while ($time < 0) { + $time += 24*3600*1000; + $dayoffset--; + } + while ($time > 24*3600*1000) { + $time -= 24*3600*1000; + $dayoffset++; + } + $day = parseDay($dayoffset, $monthnr, $rule->[1]); + return [ $monthjavanames[$monthnr-1], $day, $time ]; +} + + +sub ruleEquals($$) { + my ($rule1, $rule2) = @_; + # check month names + return (($rule1->[0] eq $rule2->[0]) + && ($rule1->[1] eq $rule2->[1]) + && ($rule1->[2] == $rule2->[2])); +} + +sub findAlias($$) { + my ($rawoffset, $rule) = @_; + foreach $tz (@timezones) { + my ($key, $tzoffset, $tzrule, $aliaslist) = @{$tz}; + next if ($tzoffset != $rawoffset); + if ($rule eq "-") { + return $tz if ($tzrule eq "-"); + } elsif ($tzrule ne "-") { + next if $rule->[2] != $tzrule->[2]; + if (ruleEquals($rule->[0], $tzrule->[0]) + && ruleEquals($rule->[1], $tzrule->[1])) { + return $tz; + } + } + } + return ""; +} + +sub makePretty($) { + my ($offset) = @_; + if (($offset % 3600) == 0) { + $offset /= 3600; + return "$offset * 3600"; + } else { + return "$offset"; + } +} + +sub tzcompare($$) { + my ($a, $b) = @_; + if (($a =~ /\//) != ($b =~ /\//)) { + return ($a =~ /\//) ? 1 : -1; + } else { + return $a cmp $b; + } +} + +foreach $file (@TIMEZONEFILES) { +# print STDERR "$file\n"; + open INPUT, "$TIMEZONEDIR/$file" or die "Can't open $TIMEZONEDIR/$file"; + my $in_time_zone = 0; + while (<INPUT>) { + $_ = $1 if /^([^\#]*)\#/; + next if /^\s*$/; + my @entries = split; +# $, = ","; print "'$_' -> [",@entries,"]\n"; + if (!$in_time_zone) { + if ($entries[0] eq "Rule") { + # check if rule still applies + # column 3 is TO entry. + if ($entries[3] eq "max") { + my $rulename = $entries[1]; + my $month = $entries[5]; + my $day = $entries[6]; + my $time = $entries[7]; + if ($entries[8] eq "0") { + # This is the end time rule + $rules{"$rulename"}[0] = [ $month, $day, $time ]; + } else { + # This is the start time rule + $rules{"$rulename"}[1] = [ $month, $day, $time ]; + $rules{"$rulename"}[2] = parseOffset($entries[8]); + } + } + } elsif ($entries[0] eq "Zone") { + $in_time_zone = 1; + shift @entries; + $timezonename = shift @entries; + } elsif ($entries[0] eq "Remove") { + my $found = 0; + foreach $tz (@timezones) { + my @newaliases; + foreach $tzname (@{$tz->[3]}) { + if ($tzname eq $entries[1]) { + $found = 1; + } else { + push @newaliases, $tzname; + } + } + if ($found) { + if ($tz->[0] eq $entries[1]) { + $tz->[0] = $newaliases[0]; + } + $tz->[3] = \@newaliases; + last; + } + } + + die "Unknown link $_" if ! $found; + } elsif ($entries[0] eq "Link") { + my $alias = 0; + foreach $tz (@timezones) { + foreach $tzname (@{$tz->[3]}) { + if ($tzname eq $entries[1]) { + $alias = $tz; + last; + } + } + } + + die "Unknown link $_" if ! $alias; + die "@entries" if $entries[1] =~ /^\d+$/; + push @{$alias->[3]}, $entries[2]; + } else { + die "Unknown command: $_"; + } + } + if ($in_time_zone) { + die "early end of Zone: $_" if ($entries[0] =~ /^[A-Za-z]+/); + if (@entries <= 3) { +# print "found ZONE $timezonename $entries[0] $entries[1] $entries[2]\n"; + # This is the last line and the only we look at. + # other lines are for historic time zones. + my $rawoffset = parseOffset($entries[0]); + my $rule = $rules{"$entries[1]"} || "-"; + if ($rule ne "-") { + if (!defined($rule->[2])) { + $rule = "-"; + } else { + # now we can parse the time since we know raw offset. + my $savings = $rule->[2]; + my $endrule = parseRule($rawoffset, $savings, + $rule->[0]); + my $startrule = parseRule($rawoffset, $savings, + $rule->[1]); + $rule = [ $endrule, $startrule, $savings ]; +# print "start",@{$rule->[1]}, "end", @{$rule->[0]}, +# "offset", $rule->[2],"\n"; + } + } + my $alias = findAlias($rawoffset, $rule); + if ($alias) { + if (($alias->[0] =~ /\//) + && ($timezonename !~ /\//)) { + # alias is of Country/City form, timezonename not + # make timezonename the real zone name + $alias->[0] = $timezonename; + } + push @{$alias->[3]}, $timezonename; + } else { + push @timezones, [ $timezonename, $rawoffset, $rule, + [ $timezonename ] ]; + } + $in_time_zone = 0; + } + } + } + close INPUT; +} + +@timezones = sort { if ($a->[1] != $b->[1]) { $a->[1] <=> $b->[1] } + else { $a->[0] cmp $b->[0] } } @timezones; +for (@timezones) { + my ($name, $rawoffset, $rule, $aliaslist) = @{$_}; + my @aliases = sort { tzcompare($a, $b); } @{$aliaslist}; + $name = $aliases[0]; + $rawoffset = makePretty($rawoffset); + if ($rule eq "-") { + print <<EOF + tz = new SimpleTimeZone($rawoffset, \"$name\"); +EOF + } else { + my ($endmonth, $endday, $endtime) = @{$rule->[0]}; + my ($startmonth, $startday, $starttime) = @{$rule->[1]}; + $endtime = makePretty($endtime); + $starttime = makePretty($starttime); + my $savings = $rule->[2]; + if ($savings == 3600 * 1000) { + print <<EOF + tz = new SimpleTimeZone + ($rawoffset, \"$name\", + $startmonth, $startday, $starttime, + $endmonth, $endday, $endtime); +EOF + } else { + $savings = makePretty($savings); + print <<EOF + tz = new SimpleTimeZone + ($rawoffset, \"$name\", + $startmonth, $startday, $starttime, + $endmonth, $endday, $endtime, $savings); +EOF + } + } + for (@aliases) { + print <<EOF + timezones0.put(\"$_\", tz); +EOF + } +} + + |