2 # Create the timezone tables for java/util/TimeZone from the
3 # standard timezone sources by Arthur David Olson (as used by glibc)
5 # This needs the files from the package tzdata2000h which may be found
6 # at ftp://ftp.cs.mu.oz.au/pub/.
8 $TIMEZONEDIR = "tzdata";
9 @TIMEZONEFILES = ("africa", "antarctica", "asia", "australasia",
10 "europe", "northamerica", "pacificnew", "southamerica",
15 # value is either "-" (no daylight savings) or a list of three elements:
16 # $value[0] = end savings rule (list containing MONTH, DAY and TIME)
17 # $value[1] = start savings rule (ditto)
18 # $value[2] = daylight offset in milliseconds
19 my %rules = ("-" => "-");
21 # timezones list, list of pairs:
22 # $timezones[$i][0] is a timezone name
23 # $timezones[$i][1] = raw offset in milliseconds
24 # $timezones[$i][2] = rule in the same format as the value of
25 # the rules table, but TIME in milliseconds
26 # $timezones[$i][3] = list of timezone names with this rule (aliases)
27 my @timezones = ( [ "GMT", 0, "-", [ "GMT", "UTC" ] ]);
30 # parse the offset of form +/-hh:mm:ss (:ss is optional) and return it
31 # in milliseconds against UTC
34 $offset =~ /^([+-]?)(\d+)(:(\d+)(:(\d+))?)?$/
35 or die "Can't parse offset $offset";
36 my $seconds = $2 * 3600;
37 $seconds += $4 * 60 if ($3);
38 $seconds += $6 if ($3 && $5);
40 $seconds = - $seconds;
42 return $seconds * 1000;
45 # parse the time of form +/-hh:mm:ss[swguz] (:ss is optional) and return it
46 # in milliseconds since midnight in local wall time
49 my ($rawoffset, $stdoffset, $time) = @_;
50 $time =~ /^([+-]?)(\d+):(\d+)(:(\d+))?([swguz]?)$/
51 or die "Can't parse time $time";
52 my ($hour, $min) = ($2, $3);
53 my $sec = ($4) ? $5 : 0;
54 my $millis = ((($hour * 60) + $min) * 60 + $sec) * 1000;
58 # Normally millis is in wall time, adjust for utc and standard time.
60 $millis += $rawoffset + $stdoffset;
62 $millis += $stdoffset;
81 my $month = $monthnames{"$_[0]"} or die "Unknown month $_[0]";
94 my $weekday = $weekdaynames{"$_[0]"} or die "Unknown weekday $_[0]";
98 my @weekdayjavanames =
102 "Calendar.WEDNESDAY",
105 "Calendar.SATURDAY" );
106 my @daysInMonths = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
108 my ($dayoffset, $month, $day) = @_;
109 if ($day =~ /^\d+$/) {
111 } elsif ($day =~ /^last([A-Z][a-z][a-z])$/) {
112 my $weekday = ( parseWeekday($1) + $dayoffset + 7 ) % 7;
114 my $day = $daysInMonths[$month - 1] + $dayoffset;
115 warn "Can only approximate $day with dayoffset in $file"
117 return "$day, -$weekdayjavanames[$weekday]";
119 return "-1, $weekdayjavanames[$weekday]";
121 } elsif ($day =~ /^([A-Z][a-z][a-z])>=(\d+)$/) {
122 my $start = $2 + $dayoffset;
123 my $weekday = ( parseWeekday($1) + $dayoffset + 7 ) % 7;
124 if (($start % 7) == 1) {
125 $start = ($start + 6) / 7;
126 return "$start, $weekdayjavanames[$weekday]";
128 return "$start, -$weekdayjavanames[$weekday]";
131 die "Unknown day $day";
136 ( "Calendar.JANUARY",
144 "Calendar.SEPTEMBER",
147 "Calendar.DECEMBER" );
150 my ($rawoffset, $stdoffset, $rule) = @_;
151 my $monthnr = parseMonth($rule->[0]);
152 my $time = parseTime($rawoffset, $stdoffset, $rule->[2]);
155 $time += 24*3600*1000;
158 while ($time > 24*3600*1000) {
159 $time -= 24*3600*1000;
162 $day = parseDay($dayoffset, $monthnr, $rule->[1]);
163 return [ $monthjavanames[$monthnr-1], $day, $time ];
168 my ($rule1, $rule2) = @_;
170 return (($rule1->[0] eq $rule2->[0])
171 && ($rule1->[1] eq $rule2->[1])
172 && ($rule1->[2] == $rule2->[2]));
176 my ($rawoffset, $rule) = @_;
177 foreach $tz (@timezones) {
178 my ($key, $tzoffset, $tzrule, $aliaslist) = @{$tz};
179 next if ($tzoffset != $rawoffset);
181 return $tz if ($tzrule eq "-");
182 } elsif ($tzrule ne "-") {
183 next if $rule->[2] != $tzrule->[2];
184 if (ruleEquals($rule->[0], $tzrule->[0])
185 && ruleEquals($rule->[1], $tzrule->[1])) {
195 if (($offset % 3600) == 0) {
197 return "$offset * 3600";
205 if (($a =~ /\//) != ($b =~ /\//)) {
206 return ($a =~ /\//) ? 1 : -1;
212 foreach $file (@TIMEZONEFILES) {
213 # print STDERR "$file\n";
214 open INPUT, "$TIMEZONEDIR/$file" or die "Can't open $TIMEZONEDIR/$file";
215 my $in_time_zone = 0;
217 $_ = $1 if /^([^\#]*)\#/;
220 # $, = ","; print "'$_' -> [",@entries,"]\n";
221 if (!$in_time_zone) {
222 if ($entries[0] eq "Rule") {
223 # check if rule still applies
224 # column 3 is TO entry.
225 if ($entries[3] eq "max") {
226 my $rulename = $entries[1];
227 my $month = $entries[5];
228 my $day = $entries[6];
229 my $time = $entries[7];
230 if ($entries[8] eq "0") {
231 # This is the end time rule
232 $rules{"$rulename"}[0] = [ $month, $day, $time ];
234 # This is the start time rule
235 $rules{"$rulename"}[1] = [ $month, $day, $time ];
236 $rules{"$rulename"}[2] = parseOffset($entries[8]);
239 } elsif ($entries[0] eq "Zone") {
242 $timezonename = shift @entries;
243 } elsif ($entries[0] eq "Remove") {
245 foreach $tz (@timezones) {
247 foreach $tzname (@{$tz->[3]}) {
248 if ($tzname eq $entries[1]) {
251 push @newaliases, $tzname;
255 if ($tz->[0] eq $entries[1]) {
256 $tz->[0] = $newaliases[0];
258 $tz->[3] = \@newaliases;
263 die "Unknown link $_" if ! $found;
264 } elsif ($entries[0] eq "Link") {
266 foreach $tz (@timezones) {
267 foreach $tzname (@{$tz->[3]}) {
268 if ($tzname eq $entries[1]) {
275 die "Unknown link $_" if ! $alias;
276 die "@entries" if $entries[1] =~ /^\d+$/;
277 push @{$alias->[3]}, $entries[2];
279 die "Unknown command: $_";
283 die "early end of Zone: $_" if ($entries[0] =~ /^[A-Za-z]+/);
285 # print "found ZONE $timezonename $entries[0] $entries[1] $entries[2]\n";
286 # This is the last line and the only we look at.
287 # other lines are for historic time zones.
288 my $rawoffset = parseOffset($entries[0]);
289 my $rule = $rules{"$entries[1]"} || "-";
291 if (!defined($rule->[2])) {
294 # now we can parse the time since we know raw offset.
295 my $savings = $rule->[2];
296 my $endrule = parseRule($rawoffset, $savings,
298 my $startrule = parseRule($rawoffset, $savings,
300 $rule = [ $endrule, $startrule, $savings ];
301 # print "start",@{$rule->[1]}, "end", @{$rule->[0]},
302 # "offset", $rule->[2],"\n";
305 my $alias = findAlias($rawoffset, $rule);
307 if (($alias->[0] =~ /\//)
308 && ($timezonename !~ /\//)) {
309 # alias is of Country/City form, timezonename not
310 # make timezonename the real zone name
311 $alias->[0] = $timezonename;
313 push @{$alias->[3]}, $timezonename;
315 push @timezones, [ $timezonename, $rawoffset, $rule,
325 @timezones = sort { if ($a->[1] != $b->[1]) { $a->[1] <=> $b->[1] }
326 else { $a->[0] cmp $b->[0] } } @timezones;
328 my ($name, $rawoffset, $rule, $aliaslist) = @{$_};
329 my @aliases = sort { tzcompare($a, $b); } @{$aliaslist};
331 $rawoffset = makePretty($rawoffset);
334 tz = new SimpleTimeZone($rawoffset, \"$name\");
337 my ($endmonth, $endday, $endtime) = @{$rule->[0]};
338 my ($startmonth, $startday, $starttime) = @{$rule->[1]};
339 $endtime = makePretty($endtime);
340 $starttime = makePretty($starttime);
341 my $savings = $rule->[2];
342 if ($savings == 3600 * 1000) {
344 tz = new SimpleTimeZone
345 ($rawoffset, \"$name\",
346 $startmonth, $startday, $starttime,
347 $endmonth, $endday, $endtime);
350 $savings = makePretty($savings);
352 tz = new SimpleTimeZone
353 ($rawoffset, \"$name\",
354 $startmonth, $startday, $starttime,
355 $endmonth, $endday, $endtime, $savings);
361 timezones0.put(\"$_\", tz);