KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > tz > ZoneInfoCompiler


1 /*
2  * Copyright 2001-2005 Stephen Colebourne
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.joda.time.tz;
17
18 import java.io.BufferedReader JavaDoc;
19 import java.io.DataOutputStream JavaDoc;
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.FileReader JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Locale JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34 import java.util.TreeMap JavaDoc;
35
36 import org.joda.time.Chronology;
37 import org.joda.time.DateTime;
38 import org.joda.time.DateTimeField;
39 import org.joda.time.DateTimeZone;
40 import org.joda.time.MutableDateTime;
41 import org.joda.time.chrono.ISOChronology;
42 import org.joda.time.chrono.LenientChronology;
43 import org.joda.time.format.DateTimeFormatter;
44 import org.joda.time.format.ISODateTimeFormat;
45
46 /**
47  * Compiles Olson ZoneInfo database files into binary files for each time zone
48  * in the database. {@link DateTimeZoneBuilder} is used to construct and encode
49  * compiled data files. {@link ZoneInfoProvider} loads the encoded files and
50  * converts them back into {@link DateTimeZone} objects.
51  * <p>
52  * Although this tool is similar to zic, the binary formats are not
53  * compatible. The latest Olson database files may be obtained
54  * <a HREF="http://www.twinsun.com/tz/tz-link.htm">here</a>.
55  * <p>
56  * ZoneInfoCompiler is mutable and not thread-safe, although the main method
57  * may be safely invoked by multiple threads.
58  *
59  * @author Brian S O'Neill
60  * @since 1.0
61  */

62 public class ZoneInfoCompiler {
63     static DateTimeOfYear cStartOfYear;
64
65     static Chronology cLenientISO;
66
67     /**
68      * Launches the ZoneInfoCompiler tool.
69      *
70      * <pre>
71      * Usage: java org.joda.time.tz.ZoneInfoCompiler &lt;options&gt; &lt;source files&gt;
72      * where possible options include:
73      * -src &lt;directory&gt; Specify where to read source files
74      * -dst &lt;directory&gt; Specify where to write generated files
75      * </pre>
76      */

77     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
78         if (args.length == 0) {
79             printUsage();
80             return;
81         }
82
83         File JavaDoc inputDir = null;
84         File JavaDoc outputDir = null;
85
86         int i;
87         for (i=0; i<args.length; i++) {
88             try {
89                 if ("-src".equals(args[i])) {
90                     inputDir = new File JavaDoc(args[++i]);
91                 } else if ("-dst".equals(args[i])) {
92                     outputDir = new File JavaDoc(args[++i]);
93                 } else if ("-?".equals(args[i])) {
94                     printUsage();
95                     return;
96                 } else {
97                     break;
98                 }
99             } catch (IndexOutOfBoundsException JavaDoc e) {
100                 printUsage();
101                 return;
102             }
103         }
104
105         if (i >= args.length) {
106             printUsage();
107             return;
108         }
109
110         File JavaDoc[] sources = new File JavaDoc[args.length - i];
111         for (int j=0; i<args.length; i++,j++) {
112             sources[j] = inputDir == null ? new File JavaDoc(args[i]) : new File JavaDoc(inputDir, args[i]);
113         }
114
115         ZoneInfoCompiler zic = new ZoneInfoCompiler();
116         zic.compile(outputDir, sources);
117     }
118
119     private static void printUsage() {
120         System.out.println("Usage: java org.joda.time.tz.ZoneInfoCompiler <options> <source files>");
121         System.out.println("where possible options include:");
122         System.out.println(" -src <directory> Specify where to read source files");
123         System.out.println(" -dst <directory> Specify where to write generated files");
124     }
125
126     static DateTimeOfYear getStartOfYear() {
127         if (cStartOfYear == null) {
128             cStartOfYear = new DateTimeOfYear();
129         }
130         return cStartOfYear;
131     }
132
133     static Chronology getLenientISOChronology() {
134         if (cLenientISO == null) {
135             cLenientISO = LenientChronology.getInstance(ISOChronology.getInstanceUTC());
136         }
137         return cLenientISO;
138     }
139
140     /**
141      * @param zimap maps string ids to DateTimeZone objects.
142      */

143     static void writeZoneInfoMap(DataOutputStream JavaDoc dout, Map JavaDoc zimap) throws IOException JavaDoc {
144         // Build the string pool.
145
Map JavaDoc idToIndex = new HashMap JavaDoc(zimap.size());
146         TreeMap JavaDoc indexToId = new TreeMap JavaDoc();
147
148         Iterator JavaDoc it = zimap.entrySet().iterator();
149         short count = 0;
150         while (it.hasNext()) {
151             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
152             String JavaDoc id = (String JavaDoc)entry.getKey();
153             if (!idToIndex.containsKey(id)) {
154                 Short JavaDoc index = new Short JavaDoc(count);
155                 idToIndex.put(id, index);
156                 indexToId.put(index, id);
157                 if (++count == 0) {
158                     throw new InternalError JavaDoc("Too many time zone ids");
159                 }
160             }
161             id = ((DateTimeZone)entry.getValue()).getID();
162             if (!idToIndex.containsKey(id)) {
163                 Short JavaDoc index = new Short JavaDoc(count);
164                 idToIndex.put(id, index);
165                 indexToId.put(index, id);
166                 if (++count == 0) {
167                     throw new InternalError JavaDoc("Too many time zone ids");
168                 }
169             }
170         }
171
172         // Write the string pool, ordered by index.
173
dout.writeShort(indexToId.size());
174         it = indexToId.values().iterator();
175         while (it.hasNext()) {
176             dout.writeUTF((String JavaDoc)it.next());
177         }
178
179         // Write the mappings.
180
dout.writeShort(zimap.size());
181         it = zimap.entrySet().iterator();
182         while (it.hasNext()) {
183             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
184             String JavaDoc id = (String JavaDoc)entry.getKey();
185             dout.writeShort(((Short JavaDoc)idToIndex.get(id)).shortValue());
186             id = ((DateTimeZone)entry.getValue()).getID();
187             dout.writeShort(((Short JavaDoc)idToIndex.get(id)).shortValue());
188         }
189     }
190
191     static int parseYear(String JavaDoc str, int def) {
192         str = str.toLowerCase();
193         if (str.equals("minimum") || str.equals("min")) {
194             return Integer.MIN_VALUE;
195         } else if (str.equals("maximum") || str.equals("max")) {
196             return Integer.MAX_VALUE;
197         } else if (str.equals("only")) {
198             return def;
199         }
200         return Integer.parseInt(str);
201     }
202
203     static int parseMonth(String JavaDoc str) {
204         DateTimeField field = ISOChronology.getInstanceUTC().monthOfYear();
205         return field.get(field.set(0, str, Locale.ENGLISH));
206     }
207
208     static int parseDayOfWeek(String JavaDoc str) {
209         DateTimeField field = ISOChronology.getInstanceUTC().dayOfWeek();
210         return field.get(field.set(0, str, Locale.ENGLISH));
211     }
212     
213     static String JavaDoc parseOptional(String JavaDoc str) {
214         return (str.equals("-")) ? null : str;
215     }
216
217     static int parseTime(String JavaDoc str) {
218         DateTimeFormatter p = ISODateTimeFormat.hourMinuteSecondFraction();
219         MutableDateTime mdt = new MutableDateTime(0, getLenientISOChronology());
220         int pos = 0;
221         if (str.startsWith("-")) {
222             pos = 1;
223         }
224         int newPos = p.parseInto(mdt, str, pos);
225         if (newPos == ~pos) {
226             throw new IllegalArgumentException JavaDoc(str);
227         }
228         int millis = (int)mdt.getMillis();
229         if (pos == 1) {
230             millis = -millis;
231         }
232         return millis;
233     }
234
235     static char parseZoneChar(char c) {
236         switch (c) {
237         case 's': case 'S':
238             // Standard time
239
return 's';
240         case 'u': case 'U': case 'g': case 'G': case 'z': case 'Z':
241             // UTC
242
return 'u';
243         case 'w': case 'W': default:
244             // Wall time
245
return 'w';
246         }
247     }
248
249     /**
250      * @return false if error.
251      */

252     static boolean test(String JavaDoc id, DateTimeZone tz) {
253         if (!id.equals(tz.getID())) {
254             return true;
255         }
256
257         // Test to ensure that reported transitions are not duplicated.
258

259         long millis = ISOChronology.getInstanceUTC().year().set(0, 1850);
260         long end = ISOChronology.getInstanceUTC().year().set(0, 2050);
261
262         int offset = tz.getOffset(millis);
263         String JavaDoc key = tz.getNameKey(millis);
264
265         List JavaDoc transitions = new ArrayList JavaDoc();
266
267         while (true) {
268             long next = tz.nextTransition(millis);
269             if (next == millis || next > end) {
270                 break;
271             }
272
273             millis = next;
274
275             int nextOffset = tz.getOffset(millis);
276             String JavaDoc nextKey = tz.getNameKey(millis);
277
278             if (offset == nextOffset
279                 && key.equals(nextKey)) {
280                 System.out.println("*d* Error in " + tz.getID() + " "
281                                    + new DateTime(millis,
282                                                   ISOChronology.getInstanceUTC()));
283                 return false;
284             }
285
286             if (nextKey == null || (nextKey.length() < 3 && !"??".equals(nextKey))) {
287                 System.out.println("*s* Error in " + tz.getID() + " "
288                                    + new DateTime(millis,
289                                                   ISOChronology.getInstanceUTC())
290                                    + ", nameKey=" + nextKey);
291                 return false;
292             }
293
294             transitions.add(new Long JavaDoc(millis));
295
296             offset = nextOffset;
297             key = nextKey;
298         }
299
300         // Now verify that reverse transitions match up.
301

302         millis = ISOChronology.getInstanceUTC().year().set(0, 2050);
303         end = ISOChronology.getInstanceUTC().year().set(0, 1850);
304
305         for (int i=transitions.size(); --i>= 0; ) {
306             long prev = tz.previousTransition(millis);
307             if (prev == millis || prev < end) {
308                 break;
309             }
310
311             millis = prev;
312
313             long trans = ((Long JavaDoc)transitions.get(i)).longValue();
314             
315             if (trans - 1 != millis) {
316                 System.out.println("*r* Error in " + tz.getID() + " "
317                                    + new DateTime(millis,
318                                                   ISOChronology.getInstanceUTC()) + " != "
319                                    + new DateTime(trans - 1,
320                                                   ISOChronology.getInstanceUTC()));
321                                    
322                 return false;
323             }
324         }
325
326         return true;
327     }
328
329     // Maps names to RuleSets.
330
private Map JavaDoc iRuleSets;
331
332     // List of Zone objects.
333
private List JavaDoc iZones;
334
335     // List String pairs to link.
336
private List JavaDoc iLinks;
337
338     public ZoneInfoCompiler() {
339         iRuleSets = new HashMap JavaDoc();
340         iZones = new ArrayList JavaDoc();
341         iLinks = new ArrayList JavaDoc();
342     }
343
344     /**
345      * Returns a map of ids to DateTimeZones.
346      *
347      * @param outputDir optional directory to write compiled data files to
348      * @param sources optional list of source files to parse
349      */

350     public Map JavaDoc compile(File JavaDoc outputDir, File JavaDoc[] sources) throws IOException JavaDoc {
351         if (sources != null) {
352             for (int i=0; i<sources.length; i++) {
353                 BufferedReader JavaDoc in = new BufferedReader JavaDoc(new FileReader JavaDoc(sources[i]));
354                 parseDataFile(in);
355                 in.close();
356             }
357         }
358
359         if (outputDir != null) {
360             if (!outputDir.exists()) {
361                 throw new IOException JavaDoc("Destination directory doesn't exist: " + outputDir);
362             }
363             if (!outputDir.isDirectory()) {
364                 throw new IOException JavaDoc("Destination is not a directory: " + outputDir);
365             }
366         }
367
368         Map JavaDoc map = new TreeMap JavaDoc();
369
370         for (int i=0; i<iZones.size(); i++) {
371             Zone zone = (Zone)iZones.get(i);
372             DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
373             zone.addToBuilder(builder, iRuleSets);
374             final DateTimeZone original = builder.toDateTimeZone(zone.iName);
375             DateTimeZone tz = original;
376             if (test(tz.getID(), tz)) {
377                 map.put(tz.getID(), tz);
378                 if (outputDir != null) {
379                     System.out.println("Writing " + tz.getID());
380                     File JavaDoc file = new File JavaDoc(outputDir, tz.getID());
381                     if (!file.getParentFile().exists()) {
382                         file.getParentFile().mkdirs();
383                     }
384                     OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
385                     builder.writeTo(out);
386                     out.close();
387
388                     // Test if it can be read back.
389
InputStream JavaDoc in = new FileInputStream JavaDoc(file);
390                     DateTimeZone tz2 = DateTimeZoneBuilder.readFrom(in, tz.getID());
391                     in.close();
392
393                     if (!original.equals(tz2)) {
394                         System.out.println("*e* Error in " + tz.getID() +
395                                            ": Didn't read properly from file");
396                     }
397                 }
398             }
399         }
400
401         for (int pass=0; pass<2; pass++) {
402             for (int i=0; i<iLinks.size(); i += 2) {
403                 String JavaDoc id = (String JavaDoc)iLinks.get(i);
404                 String JavaDoc alias = (String JavaDoc)iLinks.get(i + 1);
405                 DateTimeZone tz = (DateTimeZone)map.get(id);
406                 if (tz == null) {
407                     if (pass > 0) {
408                         System.out.println("Cannot find time zone '" + id +
409                                            "' to link alias '" + alias + "' to");
410                     }
411                 } else {
412                     map.put(alias, tz);
413                 }
414             }
415         }
416
417         if (outputDir != null) {
418             System.out.println("Writing ZoneInfoMap");
419             File JavaDoc file = new File JavaDoc(outputDir, "ZoneInfoMap");
420             if (!file.getParentFile().exists()) {
421                 file.getParentFile().mkdirs();
422             }
423
424             OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
425             DataOutputStream JavaDoc dout = new DataOutputStream JavaDoc(out);
426             // Sort and filter out any duplicates that match case.
427
Map JavaDoc zimap = new TreeMap JavaDoc(String.CASE_INSENSITIVE_ORDER);
428             zimap.putAll(map);
429             writeZoneInfoMap(dout, zimap);
430             dout.close();
431         }
432
433         return map;
434     }
435
436     public void parseDataFile(BufferedReader JavaDoc in) throws IOException JavaDoc {
437         Zone zone = null;
438         String JavaDoc line;
439         while ((line = in.readLine()) != null) {
440             String JavaDoc trimmed = line.trim();
441             if (trimmed.length() == 0 || trimmed.charAt(0) == '#') {
442                 continue;
443             }
444
445             int index = line.indexOf('#');
446             if (index >= 0) {
447                 line = line.substring(0, index);
448             }
449
450             //System.out.println(line);
451

452             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(line, " \t");
453
454             if (Character.isWhitespace(line.charAt(0)) && st.hasMoreTokens()) {
455                 if (zone != null) {
456                     // Zone continuation
457
zone.chain(st);
458                 }
459                 continue;
460             } else {
461                 if (zone != null) {
462                     iZones.add(zone);
463                 }
464                 zone = null;
465             }
466
467             if (st.hasMoreTokens()) {
468                 String JavaDoc token = st.nextToken();
469                 if (token.equalsIgnoreCase("Rule")) {
470                     Rule r = new Rule(st);
471                     RuleSet rs = (RuleSet)iRuleSets.get(r.iName);
472                     if (rs == null) {
473                         rs = new RuleSet(r);
474                         iRuleSets.put(r.iName, rs);
475                     } else {
476                         rs.addRule(r);
477                     }
478                 } else if (token.equalsIgnoreCase("Zone")) {
479                     zone = new Zone(st);
480                 } else if (token.equalsIgnoreCase("Link")) {
481                     iLinks.add(st.nextToken());
482                     iLinks.add(st.nextToken());
483                 } else {
484                     System.out.println("Unknown line: " + line);
485                 }
486             }
487         }
488
489         if (zone != null) {
490             iZones.add(zone);
491         }
492     }
493
494     private static class DateTimeOfYear {
495         public final int iMonthOfYear;
496         public final int iDayOfMonth;
497         public final int iDayOfWeek;
498         public final boolean iAdvanceDayOfWeek;
499         public final int iMillisOfDay;
500         public final char iZoneChar;
501
502         DateTimeOfYear() {
503             iMonthOfYear = 1;
504             iDayOfMonth = 1;
505             iDayOfWeek = 0;
506             iAdvanceDayOfWeek = false;
507             iMillisOfDay = 0;
508             iZoneChar = 'w';
509         }
510
511         DateTimeOfYear(StringTokenizer JavaDoc st) {
512             int month = 1;
513             int day = 1;
514             int dayOfWeek = 0;
515             int millis = 0;
516             boolean advance = false;
517             char zoneChar = 'w';
518
519             if (st.hasMoreTokens()) {
520                 month = parseMonth(st.nextToken());
521
522                 if (st.hasMoreTokens()) {
523                     String JavaDoc str = st.nextToken();
524                     if (str.startsWith("last")) {
525                         day = -1;
526                         dayOfWeek = parseDayOfWeek(str.substring(4));
527                         advance = false;
528                     } else {
529                         try {
530                             day = Integer.parseInt(str);
531                             dayOfWeek = 0;
532                             advance = false;
533                         } catch (NumberFormatException JavaDoc e) {
534                             int index = str.indexOf(">=");
535                             if (index > 0) {
536                                 day = Integer.parseInt(str.substring(index + 2));
537                                 dayOfWeek = parseDayOfWeek(str.substring(0, index));
538                                 advance = true;
539                             } else {
540                                 index = str.indexOf("<=");
541                                 if (index > 0) {
542                                     day = Integer.parseInt(str.substring(index + 2));
543                                     dayOfWeek = parseDayOfWeek(str.substring(0, index));
544                                     advance = false;
545                                 } else {
546                                     throw new IllegalArgumentException JavaDoc(str);
547                                 }
548                             }
549                         }
550                     }
551
552                     if (st.hasMoreTokens()) {
553                         str = st.nextToken();
554                         zoneChar = parseZoneChar(str.charAt(str.length() - 1));
555                         millis = parseTime(str);
556                     }
557                 }
558             }
559
560             iMonthOfYear = month;
561             iDayOfMonth = day;
562             iDayOfWeek = dayOfWeek;
563             iAdvanceDayOfWeek = advance;
564             iMillisOfDay = millis;
565             iZoneChar = zoneChar;
566         }
567
568         /**
569          * Adds a recurring savings rule to the builder.
570          */

571         public void addRecurring(DateTimeZoneBuilder builder, String JavaDoc nameKey,
572                                  int saveMillis, int fromYear, int toYear)
573         {
574             builder.addRecurringSavings(nameKey, saveMillis,
575                                         fromYear, toYear,
576                                         iZoneChar,
577                                         iMonthOfYear,
578                                         iDayOfMonth,
579                                         iDayOfWeek,
580                                         iAdvanceDayOfWeek,
581                                         iMillisOfDay);
582         }
583
584         /**
585          * Adds a cutover to the builder.
586          */

587         public void addCutover(DateTimeZoneBuilder builder, int year) {
588             builder.addCutover(year,
589                                iZoneChar,
590                                iMonthOfYear,
591                                iDayOfMonth,
592                                iDayOfWeek,
593                                iAdvanceDayOfWeek,
594                                iMillisOfDay);
595         }
596
597         public String JavaDoc toString() {
598             return
599                 "MonthOfYear: " + iMonthOfYear + "\n" +
600                 "DayOfMonth: " + iDayOfMonth + "\n" +
601                 "DayOfWeek: " + iDayOfWeek + "\n" +
602                 "AdvanceDayOfWeek: " + iAdvanceDayOfWeek + "\n" +
603                 "MillisOfDay: " + iMillisOfDay + "\n" +
604                 "ZoneChar: " + iZoneChar + "\n";
605         }
606     }
607
608     private static class Rule {
609         public final String JavaDoc iName;
610         public final int iFromYear;
611         public final int iToYear;
612         public final String JavaDoc iType;
613         public final DateTimeOfYear iDateTimeOfYear;
614         public final int iSaveMillis;
615         public final String JavaDoc iLetterS;
616
617         Rule(StringTokenizer JavaDoc st) {
618             iName = st.nextToken().intern();
619             iFromYear = parseYear(st.nextToken(), 0);
620             iToYear = parseYear(st.nextToken(), iFromYear);
621             if (iToYear < iFromYear) {
622                 throw new IllegalArgumentException JavaDoc();
623             }
624             iType = parseOptional(st.nextToken());
625             iDateTimeOfYear = new DateTimeOfYear(st);
626             iSaveMillis = parseTime(st.nextToken());
627             iLetterS = parseOptional(st.nextToken());
628         }
629
630         /**
631          * Adds a recurring savings rule to the builder.
632          */

633         public void addRecurring(DateTimeZoneBuilder builder, String JavaDoc nameFormat) {
634             String JavaDoc nameKey = formatName(nameFormat);
635             iDateTimeOfYear.addRecurring
636                 (builder, nameKey, iSaveMillis, iFromYear, iToYear);
637         }
638
639         private String JavaDoc formatName(String JavaDoc nameFormat) {
640             int index = nameFormat.indexOf('/');
641             if (index > 0) {
642                 if (iSaveMillis == 0) {
643                     // Extract standard name.
644
return nameFormat.substring(0, index).intern();
645                 } else {
646                     return nameFormat.substring(index + 1).intern();
647                 }
648             }
649             index = nameFormat.indexOf("%s");
650             if (index < 0) {
651                 return nameFormat;
652             }
653             String JavaDoc left = nameFormat.substring(0, index);
654             String JavaDoc right = nameFormat.substring(index + 2);
655             String JavaDoc name;
656             if (iLetterS == null) {
657                 name = left.concat(right);
658             } else {
659                 name = left + iLetterS + right;
660             }
661             return name.intern();
662         }
663
664         public String JavaDoc toString() {
665             return
666                 "[Rule]\n" +
667                 "Name: " + iName + "\n" +
668                 "FromYear: " + iFromYear + "\n" +
669                 "ToYear: " + iToYear + "\n" +
670                 "Type: " + iType + "\n" +
671                 iDateTimeOfYear +
672                 "SaveMillis: " + iSaveMillis + "\n" +
673                 "LetterS: " + iLetterS + "\n";
674         }
675     }
676
677     private static class RuleSet {
678         private List JavaDoc iRules;
679
680         RuleSet(Rule rule) {
681             iRules = new ArrayList JavaDoc();
682             iRules.add(rule);
683         }
684
685         void addRule(Rule rule) {
686             if (!(rule.iName.equals(((Rule)iRules.get(0)).iName))) {
687                 throw new IllegalArgumentException JavaDoc("Rule name mismatch");
688             }
689             iRules.add(rule);
690         }
691
692         /**
693          * Adds recurring savings rules to the builder.
694          */

695         public void addRecurring(DateTimeZoneBuilder builder, String JavaDoc nameFormat) {
696             for (int i=0; i<iRules.size(); i++) {
697                 Rule rule = (Rule)iRules.get(i);
698                 rule.addRecurring(builder, nameFormat);
699             }
700         }
701     }
702
703     private static class Zone {
704         public final String JavaDoc iName;
705         public final int iOffsetMillis;
706         public final String JavaDoc iRules;
707         public final String JavaDoc iFormat;
708         public final int iUntilYear;
709         public final DateTimeOfYear iUntilDateTimeOfYear;
710
711         private Zone iNext;
712
713         Zone(StringTokenizer JavaDoc st) {
714             this(st.nextToken(), st);
715         }
716
717         private Zone(String JavaDoc name, StringTokenizer JavaDoc st) {
718             iName = name.intern();
719             iOffsetMillis = parseTime(st.nextToken());
720             iRules = parseOptional(st.nextToken());
721             iFormat = st.nextToken().intern();
722
723             int year = Integer.MAX_VALUE;
724             DateTimeOfYear dtOfYear = getStartOfYear();
725
726             if (st.hasMoreTokens()) {
727                 year = Integer.parseInt(st.nextToken());
728                 if (st.hasMoreTokens()) {
729                     dtOfYear = new DateTimeOfYear(st);
730                 }
731             }
732
733             iUntilYear = year;
734             iUntilDateTimeOfYear = dtOfYear;
735         }
736
737         void chain(StringTokenizer JavaDoc st) {
738             if (iNext != null) {
739                 iNext.chain(st);
740             } else {
741                 iNext = new Zone(iName, st);
742             }
743         }
744
745         /*
746         public DateTimeZone buildDateTimeZone(Map ruleSets) {
747             DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
748             addToBuilder(builder, ruleSets);
749             return builder.toDateTimeZone(iName);
750         }
751         */

752
753         /**
754          * Adds zone info to the builder.
755          */

756         public void addToBuilder(DateTimeZoneBuilder builder, Map JavaDoc ruleSets) {
757             addToBuilder(this, builder, ruleSets);
758         }
759
760         private static void addToBuilder(Zone zone,
761                                          DateTimeZoneBuilder builder,
762                                          Map JavaDoc ruleSets)
763         {
764             for (; zone != null; zone = zone.iNext) {
765                 builder.setStandardOffset(zone.iOffsetMillis);
766
767                 if (zone.iRules == null) {
768                     builder.setFixedSavings(zone.iFormat, 0);
769                 } else {
770                     try {
771                         // Check if iRules actually just refers to a savings.
772
int saveMillis = parseTime(zone.iRules);
773                         builder.setFixedSavings(zone.iFormat, saveMillis);
774                     }
775                     catch (Exception JavaDoc e) {
776                         RuleSet rs = (RuleSet)ruleSets.get(zone.iRules);
777                         if (rs == null) {
778                             throw new IllegalArgumentException JavaDoc
779                                 ("Rules not found: " + zone.iRules);
780                         }
781                         rs.addRecurring(builder, zone.iFormat);
782                     }
783                 }
784
785                 if (zone.iUntilYear == Integer.MAX_VALUE) {
786                     break;
787                 }
788
789                 zone.iUntilDateTimeOfYear.addCutover(builder, zone.iUntilYear);
790             }
791         }
792
793         public String JavaDoc toString() {
794             String JavaDoc str =
795                 "[Zone]\n" +
796                 "Name: " + iName + "\n" +
797                 "OffsetMillis: " + iOffsetMillis + "\n" +
798                 "Rules: " + iRules + "\n" +
799                 "Format: " + iFormat + "\n" +
800                 "UntilYear: " + iUntilYear + "\n" +
801                 iUntilDateTimeOfYear;
802
803             if (iNext == null) {
804                 return str;
805             }
806
807             return str + "...\n" + iNext.toString();
808         }
809     }
810 }
811
812
Popular Tags