OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / util / TimeZone.java
1 /* java.util.TimeZone
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package java.util;
41
42 import java.security.AccessController;
43 import java.security.PrivilegedAction;
44 import java.text.DateFormatSymbols;
45
46 /**
47  * This class represents a time zone offset and handles daylight savings.
48  * 
49  * You can get the default time zone with <code>getDefault</code>.
50  * This represents the time zone where program is running.
51  *
52  * Another way to create a time zone is <code>getTimeZone</code>, where
53  * you can give an identifier as parameter.  For instance, the identifier
54  * of the Central European Time zone is "CET".
55  *
56  * With the <code>getAvailableIDs</code> method, you can get all the
57  * supported time zone identifiers.
58  *
59  * @see Calendar
60  * @see SimpleTimeZone
61  * @author Jochen Hoenicke
62  */
63 public abstract class TimeZone implements java.io.Serializable, Cloneable
64 {
65
66   /**
67    * Constant used to indicate that a short timezone abbreviation should
68    * be returned, such as "EST"
69    */
70   public static final int SHORT = 0;
71
72   /**
73    * Constant used to indicate that a long timezone name should be
74    * returned, such as "Eastern Standard Time".
75    */
76   public static final int LONG = 1;
77
78   /**
79    * The time zone identifier, e.g. PST.
80    */
81   private String ID;
82
83   /**
84    * The default time zone, as returned by getDefault.
85    */
86   private static TimeZone defaultZone0;
87
88   /**
89    * Tries to get the default TimeZone for this system if not already
90    * set.  It will call <code>getDefaultTimeZone(String)</code> with
91    * the result of <code>System.getProperty("user.timezone")</code>.
92    * If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>.
93    * If that also fails GMT is returned.
94    */
95   private static synchronized TimeZone defaultZone()
96   {
97     /* Look up default timezone */
98     if (defaultZone0 == null) 
99       {
100         defaultZone0 = (TimeZone) AccessController.doPrivileged
101           (new PrivilegedAction()
102             {
103               public Object run()
104               {
105                 TimeZone zone = null;
106                 
107                 // Prefer System property user.timezone.
108                 String tzid = System.getProperty("user.timezone");
109                 if (tzid != null && !tzid.equals(""))
110                   zone = getDefaultTimeZone(tzid);
111                 
112                 // Try platfom specific way.
113                 if (zone == null)
114                   zone = VMTimeZone.getDefaultTimeZoneId();
115                 
116                 // Fall back on GMT.
117                 if (zone == null)
118                   zone = (TimeZone) timezones().get("GMT");
119                 
120                 return zone;
121               }
122             });
123       }
124     
125     return defaultZone0; 
126   }
127   
128   private static final long serialVersionUID = 3581463369166924961L;
129
130   /**
131    * HashMap for timezones by ID.  
132    */
133   private static HashMap timezones0;
134   /* initialize this static field lazily to overhead if
135    * it is not needed: 
136    */
137   // Package-private to avoid a trampoline.
138   static synchronized HashMap timezones()
139   {
140     if (timezones0 == null) 
141       {
142         HashMap timezones = new HashMap();
143         timezones0 = timezones;
144
145         TimeZone tz;
146         // Automatically generated by scripts/timezones.pl
147         // XXX - Should we read this data from a file?
148         tz = new SimpleTimeZone(-11000 * 3600, "MIT");
149         timezones0.put("MIT", tz);
150         timezones0.put("Pacific/Apia", tz);
151         timezones0.put("Pacific/Midway", tz);
152         timezones0.put("Pacific/Niue", tz);
153         timezones0.put("Pacific/Pago_Pago", tz);
154         tz = new SimpleTimeZone
155           (-10000 * 3600, "America/Adak",
156            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
157            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
158         timezones0.put("America/Adak", tz);
159         tz = new SimpleTimeZone(-10000 * 3600, "HST");
160         timezones0.put("HST", tz);
161         timezones0.put("Pacific/Fakaofo", tz);
162         timezones0.put("Pacific/Honolulu", tz);
163         timezones0.put("Pacific/Johnston", tz);
164         timezones0.put("Pacific/Rarotonga", tz);
165         timezones0.put("Pacific/Tahiti", tz);
166         tz = new SimpleTimeZone(-9500 * 3600, "Pacific/Marquesas");
167         timezones0.put("Pacific/Marquesas", tz);
168         tz = new SimpleTimeZone
169           (-9000 * 3600, "AST",
170            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
171            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
172         timezones0.put("AST", tz);
173         timezones0.put("America/Anchorage", tz);
174         timezones0.put("America/Juneau", tz);
175         timezones0.put("America/Nome", tz);
176         timezones0.put("America/Yakutat", tz);
177         tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier");
178         timezones0.put("Pacific/Gambier", tz);
179         tz = new SimpleTimeZone
180           (-8000 * 3600, "PST",
181            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
182            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
183         timezones0.put("PST", tz);
184         timezones0.put("PST8PDT", tz);
185         timezones0.put("America/Dawson", tz);
186         timezones0.put("America/Los_Angeles", tz);
187         timezones0.put("America/Tijuana", tz);
188         timezones0.put("America/Vancouver", tz);
189         timezones0.put("America/Whitehorse", tz);
190         timezones0.put("US/Pacific-New", tz);
191         tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn");
192         timezones0.put("Pacific/Pitcairn", tz);
193         tz = new SimpleTimeZone
194           (-7000 * 3600, "MST",
195            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
196            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
197         timezones0.put("MST", tz);
198         timezones0.put("MST7MDT", tz);
199         timezones0.put("America/Boise", tz);
200         timezones0.put("America/Chihuahua", tz);
201         timezones0.put("America/Denver", tz);
202         timezones0.put("America/Edmonton", tz);
203         timezones0.put("America/Inuvik", tz);
204         timezones0.put("America/Mazatlan", tz);
205         timezones0.put("America/Shiprock", tz);
206         timezones0.put("America/Yellowknife", tz);
207         tz = new SimpleTimeZone(-7000 * 3600, "MST7");
208         timezones0.put("MST7", tz);
209         timezones0.put("PNT", tz);
210         timezones0.put("America/Dawson_Creek", tz);
211         timezones0.put("America/Hermosillo", tz);
212         timezones0.put("America/Phoenix", tz);
213         tz = new SimpleTimeZone
214           (-6000 * 3600, "CST",
215            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
216            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
217         timezones0.put("CST", tz);
218         timezones0.put("CST6CDT", tz);
219         timezones0.put("America/Cambridge_Bay", tz);
220         timezones0.put("America/Cancun", tz);
221         timezones0.put("America/Chicago", tz);
222         timezones0.put("America/Menominee", tz);
223         timezones0.put("America/Merida", tz);
224         timezones0.put("America/Mexico_City", tz);
225         timezones0.put("America/Monterrey", tz);
226         timezones0.put("America/Rainy_River", tz);
227         timezones0.put("America/Winnipeg", tz);
228         tz = new SimpleTimeZone(-6000 * 3600, "America/Belize");
229         timezones0.put("America/Belize", tz);
230         timezones0.put("America/Costa_Rica", tz);
231         timezones0.put("America/El_Salvador", tz);
232         timezones0.put("America/Guatemala", tz);
233         timezones0.put("America/Managua", tz);
234         timezones0.put("America/Regina", tz);
235         timezones0.put("America/Swift_Current", tz);
236         timezones0.put("America/Tegucigalpa", tz);
237         timezones0.put("Pacific/Galapagos", tz);
238         tz = new SimpleTimeZone
239           (-6000 * 3600, "Pacific/Easter",
240            Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
241            Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
242         timezones0.put("Pacific/Easter", tz);
243         tz = new SimpleTimeZone
244           (-5000 * 3600, "America/Grand_Turk",
245            Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
246            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
247         timezones0.put("America/Grand_Turk", tz);
248         timezones0.put("America/Havana", tz);
249         tz = new SimpleTimeZone(-5000 * 3600, "EST5");
250         timezones0.put("EST5", tz);
251         timezones0.put("IET", tz);
252         timezones0.put("America/Bogota", tz);
253         timezones0.put("America/Cayman", tz);
254         timezones0.put("America/Eirunepe", tz);
255         timezones0.put("America/Guayaquil", tz);
256         timezones0.put("America/Indiana/Indianapolis", tz);
257         timezones0.put("America/Indiana/Knox", tz);
258         timezones0.put("America/Indiana/Marengo", tz);
259         timezones0.put("America/Indiana/Vevay", tz);
260         timezones0.put("America/Indianapolis", tz);
261         timezones0.put("America/Iqaluit", tz);
262         timezones0.put("America/Jamaica", tz);
263         timezones0.put("America/Lima", tz);
264         timezones0.put("America/Panama", tz);
265         timezones0.put("America/Pangnirtung", tz);
266         timezones0.put("America/Port-au-Prince", tz);
267         timezones0.put("America/Porto_Acre", tz);
268         timezones0.put("America/Rankin_Inlet", tz);
269         tz = new SimpleTimeZone
270           (-5000 * 3600, "EST",
271            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
272            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
273         timezones0.put("EST", tz);
274         timezones0.put("EST5EDT", tz);
275         timezones0.put("America/Detroit", tz);
276         timezones0.put("America/Kentucky/Louisville", tz);
277         timezones0.put("America/Kentucky/Monticello", tz);
278         timezones0.put("America/Louisville", tz);
279         timezones0.put("America/Montreal", tz);
280         timezones0.put("America/Nassau", tz);
281         timezones0.put("America/New_York", tz);
282         timezones0.put("America/Nipigon", tz);
283         timezones0.put("America/Thunder_Bay", tz);
284         tz = new SimpleTimeZone(-4000 * 3600, "PRT");
285         timezones0.put("PRT", tz);
286         timezones0.put("America/Anguilla", tz);
287         timezones0.put("America/Antigua", tz);
288         timezones0.put("America/Aruba", tz);
289         timezones0.put("America/Barbados", tz);
290         timezones0.put("America/Boa_Vista", tz);
291         timezones0.put("America/Caracas", tz);
292         timezones0.put("America/Curacao", tz);
293         timezones0.put("America/Dominica", tz);
294         timezones0.put("America/Grenada", tz);
295         timezones0.put("America/Guadeloupe", tz);
296         timezones0.put("America/Guyana", tz);
297         timezones0.put("America/La_Paz", tz);
298         timezones0.put("America/Manaus", tz);
299         timezones0.put("America/Martinique", tz);
300         timezones0.put("America/Montserrat", tz);
301         timezones0.put("America/Port_of_Spain", tz);
302         timezones0.put("America/Porto_Velho", tz);
303         timezones0.put("America/Puerto_Rico", tz);
304         timezones0.put("America/Santo_Domingo", tz);
305         timezones0.put("America/St_Kitts", tz);
306         timezones0.put("America/St_Lucia", tz);
307         timezones0.put("America/St_Thomas", tz);
308         timezones0.put("America/St_Vincent", tz);
309         timezones0.put("America/Tortola", tz);
310         tz = new SimpleTimeZone
311           (-4000 * 3600, "America/Asuncion",
312            Calendar.OCTOBER, 1, Calendar.SUNDAY, 0 * 3600,
313            Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
314         timezones0.put("America/Asuncion", tz);
315         tz = new SimpleTimeZone
316           (-4000 * 3600, "America/Cuiaba",
317            Calendar.OCTOBER, 2, Calendar.SUNDAY, 0 * 3600,
318            Calendar.FEBRUARY, 3, Calendar.SUNDAY, 0 * 3600);
319         timezones0.put("America/Cuiaba", tz);
320         tz = new SimpleTimeZone
321           (-4000 * 3600, "America/Goose_Bay",
322            Calendar.APRIL, 1, Calendar.SUNDAY, 60000,
323            Calendar.OCTOBER, -1, Calendar.SUNDAY, 60000);
324         timezones0.put("America/Goose_Bay", tz);
325         tz = new SimpleTimeZone
326           (-4000 * 3600, "America/Glace_Bay",
327            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
328            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
329         timezones0.put("America/Glace_Bay", tz);
330         timezones0.put("America/Halifax", tz);
331         timezones0.put("America/Thule", tz);
332         timezones0.put("Atlantic/Bermuda", tz);
333         tz = new SimpleTimeZone
334           (-4000 * 3600, "America/Santiago",
335            Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
336            Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
337         timezones0.put("America/Santiago", tz);
338         timezones0.put("Antarctica/Palmer", tz);
339         tz = new SimpleTimeZone
340           (-4000 * 3600, "Atlantic/Stanley",
341            Calendar.SEPTEMBER, 2, Calendar.SUNDAY, 0 * 3600,
342            Calendar.APRIL, 16, -Calendar.SUNDAY, 0 * 3600);
343         timezones0.put("Atlantic/Stanley", tz);
344         tz = new SimpleTimeZone
345           (-3500 * 3600, "CNT",
346            Calendar.APRIL, 1, Calendar.SUNDAY, 60000,
347            Calendar.OCTOBER, -1, Calendar.SUNDAY, 60000);
348         timezones0.put("CNT", tz);
349         timezones0.put("America/St_Johns", tz);
350         tz = new SimpleTimeZone
351           (-3000 * 3600, "America/Araguaina",
352            Calendar.OCTOBER, 2, Calendar.SUNDAY, 0 * 3600,
353            Calendar.FEBRUARY, 3, Calendar.SUNDAY, 0 * 3600);
354         timezones0.put("America/Araguaina", tz);
355         timezones0.put("America/Sao_Paulo", tz);
356         tz = new SimpleTimeZone(-3000 * 3600, "AGT");
357         timezones0.put("AGT", tz);
358         timezones0.put("America/Belem", tz);
359         timezones0.put("America/Buenos_Aires", tz);
360         timezones0.put("America/Catamarca", tz);
361         timezones0.put("America/Cayenne", tz);
362         timezones0.put("America/Cordoba", tz);
363         timezones0.put("America/Fortaleza", tz);
364         timezones0.put("America/Jujuy", tz);
365         timezones0.put("America/Maceio", tz);
366         timezones0.put("America/Mendoza", tz);
367         timezones0.put("America/Montevideo", tz);
368         timezones0.put("America/Paramaribo", tz);
369         timezones0.put("America/Recife", tz);
370         timezones0.put("America/Rosario", tz);
371         tz = new SimpleTimeZone
372           (-3000 * 3600, "America/Godthab",
373            Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600,
374            Calendar.OCTOBER, 30, -Calendar.SATURDAY, 22000 * 3600);
375         timezones0.put("America/Godthab", tz);
376         tz = new SimpleTimeZone
377           (-3000 * 3600, "America/Miquelon",
378            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
379            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
380         timezones0.put("America/Miquelon", tz);
381         tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha");
382         timezones0.put("America/Noronha", tz);
383         timezones0.put("Atlantic/South_Georgia", tz);
384         tz = new SimpleTimeZone
385           (-1000 * 3600, "America/Scoresbysund",
386            Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
387            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
388         timezones0.put("America/Scoresbysund", tz);
389         timezones0.put("Atlantic/Azores", tz);
390         tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde");
391         timezones0.put("Atlantic/Cape_Verde", tz);
392         timezones0.put("Atlantic/Jan_Mayen", tz);
393         tz = new SimpleTimeZone(0 * 3600, "GMT");
394         timezones0.put("GMT", tz);
395         timezones0.put("UTC", tz);
396         timezones0.put("Africa/Abidjan", tz);
397         timezones0.put("Africa/Accra", tz);
398         timezones0.put("Africa/Bamako", tz);
399         timezones0.put("Africa/Banjul", tz);
400         timezones0.put("Africa/Bissau", tz);
401         timezones0.put("Africa/Casablanca", tz);
402         timezones0.put("Africa/Conakry", tz);
403         timezones0.put("Africa/Dakar", tz);
404         timezones0.put("Africa/El_Aaiun", tz);
405         timezones0.put("Africa/Freetown", tz);
406         timezones0.put("Africa/Lome", tz);
407         timezones0.put("Africa/Monrovia", tz);
408         timezones0.put("Africa/Nouakchott", tz);
409         timezones0.put("Africa/Ouagadougou", tz);
410         timezones0.put("Africa/Sao_Tome", tz);
411         timezones0.put("Africa/Timbuktu", tz);
412         timezones0.put("Atlantic/Reykjavik", tz);
413         timezones0.put("Atlantic/St_Helena", tz);
414         timezones0.put("Europe/Belfast", tz);
415         timezones0.put("Europe/Dublin", tz);
416         timezones0.put("Europe/London", tz);
417         tz = new SimpleTimeZone
418           (0 * 3600, "WET",
419            Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
420            Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
421         timezones0.put("WET", tz);
422         timezones0.put("Atlantic/Canary", tz);
423         timezones0.put("Atlantic/Faeroe", tz);
424         timezones0.put("Atlantic/Madeira", tz);
425         timezones0.put("Europe/Lisbon", tz);
426         tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers");
427         timezones0.put("Africa/Algiers", tz);
428         timezones0.put("Africa/Bangui", tz);
429         timezones0.put("Africa/Brazzaville", tz);
430         timezones0.put("Africa/Douala", tz);
431         timezones0.put("Africa/Kinshasa", tz);
432         timezones0.put("Africa/Lagos", tz);
433         timezones0.put("Africa/Libreville", tz);
434         timezones0.put("Africa/Luanda", tz);
435         timezones0.put("Africa/Malabo", tz);
436         timezones0.put("Africa/Ndjamena", tz);
437         timezones0.put("Africa/Niamey", tz);
438         timezones0.put("Africa/Porto-Novo", tz);
439         timezones0.put("Africa/Tunis", tz);
440         tz = new SimpleTimeZone
441           (1000 * 3600, "Africa/Windhoek",
442            Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
443            Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600);
444         timezones0.put("Africa/Windhoek", tz);
445         tz = new SimpleTimeZone
446           (1000 * 3600, "CET",
447            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
448            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
449         timezones0.put("CET", tz);
450         timezones0.put("CEST", tz);
451         timezones0.put("ECT", tz);
452         timezones0.put("MET", tz);
453         timezones0.put("Africa/Ceuta", tz);
454         timezones0.put("Arctic/Longyearbyen", tz);
455         timezones0.put("Europe/Amsterdam", tz);
456         timezones0.put("Europe/Andorra", tz);
457         timezones0.put("Europe/Belgrade", tz);
458         timezones0.put("Europe/Berlin", tz);
459         timezones0.put("Europe/Bratislava", tz);
460         timezones0.put("Europe/Brussels", tz);
461         timezones0.put("Europe/Budapest", tz);
462         timezones0.put("Europe/Copenhagen", tz);
463         timezones0.put("Europe/Gibraltar", tz);
464         timezones0.put("Europe/Ljubljana", tz);
465         timezones0.put("Europe/Luxembourg", tz);
466         timezones0.put("Europe/Madrid", tz);
467         timezones0.put("Europe/Malta", tz);
468         timezones0.put("Europe/Monaco", tz);
469         timezones0.put("Europe/Oslo", tz);
470         timezones0.put("Europe/Paris", tz);
471         timezones0.put("Europe/Prague", tz);
472         timezones0.put("Europe/Rome", tz);
473         timezones0.put("Europe/San_Marino", tz);
474         timezones0.put("Europe/Sarajevo", tz);
475         timezones0.put("Europe/Skopje", tz);
476         timezones0.put("Europe/Stockholm", tz);
477         timezones0.put("Europe/Tirane", tz);
478         timezones0.put("Europe/Vaduz", tz);
479         timezones0.put("Europe/Vatican", tz);
480         timezones0.put("Europe/Vienna", tz);
481         timezones0.put("Europe/Warsaw", tz);
482         timezones0.put("Europe/Zagreb", tz);
483         timezones0.put("Europe/Zurich", tz);
484         tz = new SimpleTimeZone
485           (2000 * 3600, "ART",
486            Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600,
487            Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 23000 * 3600);
488         timezones0.put("ART", tz);
489         timezones0.put("Africa/Cairo", tz);
490         tz = new SimpleTimeZone(2000 * 3600, "CAT");
491         timezones0.put("CAT", tz);
492         timezones0.put("Africa/Blantyre", tz);
493         timezones0.put("Africa/Bujumbura", tz);
494         timezones0.put("Africa/Gaborone", tz);
495         timezones0.put("Africa/Harare", tz);
496         timezones0.put("Africa/Johannesburg", tz);
497         timezones0.put("Africa/Kigali", tz);
498         timezones0.put("Africa/Lubumbashi", tz);
499         timezones0.put("Africa/Lusaka", tz);
500         timezones0.put("Africa/Maputo", tz);
501         timezones0.put("Africa/Maseru", tz);
502         timezones0.put("Africa/Mbabane", tz);
503         timezones0.put("Africa/Tripoli", tz);
504         timezones0.put("Europe/Riga", tz);
505         timezones0.put("Europe/Tallinn", tz);
506         timezones0.put("Europe/Vilnius", tz);
507         tz = new SimpleTimeZone
508           (2000 * 3600, "Asia/Amman",
509            Calendar.MARCH, -1, Calendar.THURSDAY, 0 * 3600,
510            Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 0 * 3600);
511         timezones0.put("Asia/Amman", tz);
512         tz = new SimpleTimeZone
513           (2000 * 3600, "Asia/Beirut",
514            Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
515            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
516         timezones0.put("Asia/Beirut", tz);
517         tz = new SimpleTimeZone
518           (2000 * 3600, "Asia/Damascus",
519            Calendar.APRIL, 1, 0, 0 * 3600,
520            Calendar.OCTOBER, 1, 0, 0 * 3600);
521         timezones0.put("Asia/Damascus", tz);
522         tz = new SimpleTimeZone
523           (2000 * 3600, "Asia/Gaza",
524            Calendar.APRIL, 3, Calendar.FRIDAY, 0 * 3600,
525            Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600);
526         timezones0.put("Asia/Gaza", tz);
527         tz = new SimpleTimeZone
528           (2000 * 3600, "Asia/Jerusalem",
529            Calendar.APRIL, 1, 0, 1000 * 3600,
530            Calendar.OCTOBER, 1, 0, 1000 * 3600);
531         timezones0.put("Asia/Jerusalem", tz);
532         tz = new SimpleTimeZone
533           (2000 * 3600, "EET",
534            Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600,
535            Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
536         timezones0.put("EET", tz);
537         timezones0.put("Asia/Istanbul", tz);
538         timezones0.put("Asia/Nicosia", tz);
539         timezones0.put("Europe/Athens", tz);
540         timezones0.put("Europe/Bucharest", tz);
541         timezones0.put("Europe/Chisinau", tz);
542         timezones0.put("Europe/Helsinki", tz);
543         timezones0.put("Europe/Istanbul", tz);
544         timezones0.put("Europe/Kiev", tz);
545         timezones0.put("Europe/Nicosia", tz);
546         timezones0.put("Europe/Simferopol", tz);
547         timezones0.put("Europe/Sofia", tz);
548         timezones0.put("Europe/Uzhgorod", tz);
549         timezones0.put("Europe/Zaporozhye", tz);
550         tz = new SimpleTimeZone
551           (2000 * 3600, "Europe/Kaliningrad",
552            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
553            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
554         timezones0.put("Europe/Kaliningrad", tz);
555         timezones0.put("Europe/Minsk", tz);
556         tz = new SimpleTimeZone
557           (3000 * 3600, "Asia/Baghdad",
558            Calendar.APRIL, 1, 0, 3000 * 3600,
559            Calendar.OCTOBER, 1, 0, 3000 * 3600);
560         timezones0.put("Asia/Baghdad", tz);
561         tz = new SimpleTimeZone
562           (3000 * 3600, "Europe/Moscow",
563            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
564            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
565         timezones0.put("Europe/Moscow", tz);
566         timezones0.put("Europe/Tiraspol", tz);
567         tz = new SimpleTimeZone(3000 * 3600, "EAT");
568         timezones0.put("EAT", tz);
569         timezones0.put("Africa/Addis_Ababa", tz);
570         timezones0.put("Africa/Asmera", tz);
571         timezones0.put("Africa/Dar_es_Salaam", tz);
572         timezones0.put("Africa/Djibouti", tz);
573         timezones0.put("Africa/Kampala", tz);
574         timezones0.put("Africa/Khartoum", tz);
575         timezones0.put("Africa/Mogadishu", tz);
576         timezones0.put("Africa/Nairobi", tz);
577         timezones0.put("Antarctica/Syowa", tz);
578         timezones0.put("Asia/Aden", tz);
579         timezones0.put("Asia/Bahrain", tz);
580         timezones0.put("Asia/Kuwait", tz);
581         timezones0.put("Asia/Qatar", tz);
582         timezones0.put("Asia/Riyadh", tz);
583         timezones0.put("Indian/Antananarivo", tz);
584         timezones0.put("Indian/Comoro", tz);
585         timezones0.put("Indian/Mayotte", tz);
586         tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran");
587         timezones0.put("Asia/Tehran", tz);
588         tz = new SimpleTimeZone
589           (4000 * 3600, "Asia/Baku",
590            Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
591            Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
592         timezones0.put("Asia/Baku", tz);
593         tz = new SimpleTimeZone
594           (4000 * 3600, "Asia/Aqtau",
595            Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
596            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
597         timezones0.put("Asia/Aqtau", tz);
598         timezones0.put("Asia/Tbilisi", tz);
599         tz = new SimpleTimeZone
600           (4000 * 3600, "Asia/Yerevan",
601            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
602            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
603         timezones0.put("Asia/Yerevan", tz);
604         timezones0.put("Europe/Samara", tz);
605         tz = new SimpleTimeZone(4000 * 3600, "NET");
606         timezones0.put("NET", tz);
607         timezones0.put("Asia/Dubai", tz);
608         timezones0.put("Asia/Muscat", tz);
609         timezones0.put("Indian/Mahe", tz);
610         timezones0.put("Indian/Mauritius", tz);
611         timezones0.put("Indian/Reunion", tz);
612         tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul");
613         timezones0.put("Asia/Kabul", tz);
614         tz = new SimpleTimeZone
615           (5000 * 3600, "Asia/Aqtobe",
616            Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
617            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
618         timezones0.put("Asia/Aqtobe", tz);
619         tz = new SimpleTimeZone
620           (5000 * 3600, "Asia/Bishkek",
621            Calendar.MARCH, -1, Calendar.SUNDAY, 2500 * 3600,
622            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2500 * 3600);
623         timezones0.put("Asia/Bishkek", tz);
624         tz = new SimpleTimeZone
625           (5000 * 3600, "Asia/Yekaterinburg",
626            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
627            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
628         timezones0.put("Asia/Yekaterinburg", tz);
629         tz = new SimpleTimeZone(5000 * 3600, "PLT");
630         timezones0.put("PLT", tz);
631         timezones0.put("Asia/Ashgabat", tz);
632         timezones0.put("Asia/Dushanbe", tz);
633         timezones0.put("Asia/Karachi", tz);
634         timezones0.put("Asia/Samarkand", tz);
635         timezones0.put("Asia/Tashkent", tz);
636         timezones0.put("Indian/Chagos", tz);
637         timezones0.put("Indian/Kerguelen", tz);
638         timezones0.put("Indian/Maldives", tz);
639         tz = new SimpleTimeZone(5500 * 3600, "IST");
640         timezones0.put("IST", tz);
641         timezones0.put("Asia/Calcutta", tz);
642         tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu");
643         timezones0.put("Asia/Katmandu", tz);
644         tz = new SimpleTimeZone(6000 * 3600, "BST");
645         timezones0.put("BST", tz);
646         timezones0.put("Antarctica/Mawson", tz);
647         timezones0.put("Asia/Colombo", tz);
648         timezones0.put("Asia/Dhaka", tz);
649         timezones0.put("Asia/Thimphu", tz);
650         tz = new SimpleTimeZone
651           (6000 * 3600, "Asia/Almaty",
652            Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
653            Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
654         timezones0.put("Asia/Almaty", tz);
655         tz = new SimpleTimeZone
656           (6000 * 3600, "Asia/Novosibirsk",
657            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
658            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
659         timezones0.put("Asia/Novosibirsk", tz);
660         timezones0.put("Asia/Omsk", tz);
661         tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon");
662         timezones0.put("Asia/Rangoon", tz);
663         timezones0.put("Indian/Cocos", tz);
664         tz = new SimpleTimeZone(7000 * 3600, "VST");
665         timezones0.put("VST", tz);
666         timezones0.put("Antarctica/Davis", tz);
667         timezones0.put("Asia/Bangkok", tz);
668         timezones0.put("Asia/Hovd", tz);
669         timezones0.put("Asia/Jakarta", tz);
670         timezones0.put("Asia/Phnom_Penh", tz);
671         timezones0.put("Asia/Saigon", tz);
672         timezones0.put("Asia/Vientiane", tz);
673         timezones0.put("Indian/Christmas", tz);
674         tz = new SimpleTimeZone
675           (7000 * 3600, "Asia/Krasnoyarsk",
676            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
677            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
678         timezones0.put("Asia/Krasnoyarsk", tz);
679         tz = new SimpleTimeZone(8000 * 3600, "CTT");
680         timezones0.put("CTT", tz);
681         timezones0.put("Antarctica/Casey", tz);
682         timezones0.put("Asia/Brunei", tz);
683         timezones0.put("Asia/Chungking", tz);
684         timezones0.put("Asia/Harbin", tz);
685         timezones0.put("Asia/Hong_Kong", tz);
686         timezones0.put("Asia/Kashgar", tz);
687         timezones0.put("Asia/Kuala_Lumpur", tz);
688         timezones0.put("Asia/Kuching", tz);
689         timezones0.put("Asia/Macao", tz);
690         timezones0.put("Asia/Manila", tz);
691         timezones0.put("Asia/Shanghai", tz);
692         timezones0.put("Asia/Singapore", tz);
693         timezones0.put("Asia/Taipei", tz);
694         timezones0.put("Asia/Ujung_Pandang", tz);
695         timezones0.put("Asia/Ulaanbaatar", tz);
696         timezones0.put("Asia/Urumqi", tz);
697         timezones0.put("Australia/Perth", tz);
698         tz = new SimpleTimeZone
699           (8000 * 3600, "Asia/Irkutsk",
700            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
701            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
702         timezones0.put("Asia/Irkutsk", tz);
703         tz = new SimpleTimeZone(9000 * 3600, "JST");
704         timezones0.put("JST", tz);
705         timezones0.put("Asia/Dili", tz);
706         timezones0.put("Asia/Jayapura", tz);
707         timezones0.put("Asia/Pyongyang", tz);
708         timezones0.put("Asia/Seoul", tz);
709         timezones0.put("Asia/Tokyo", tz);
710         timezones0.put("Pacific/Palau", tz);
711         tz = new SimpleTimeZone
712           (9000 * 3600, "Asia/Yakutsk",
713            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
714            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
715         timezones0.put("Asia/Yakutsk", tz);
716         tz = new SimpleTimeZone
717           (9500 * 3600, "Australia/Adelaide",
718            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
719            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
720         timezones0.put("Australia/Adelaide", tz);
721         timezones0.put("Australia/Broken_Hill", tz);
722         tz = new SimpleTimeZone(9500 * 3600, "ACT");
723         timezones0.put("ACT", tz);
724         timezones0.put("Australia/Darwin", tz);
725         tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville");
726         timezones0.put("Antarctica/DumontDUrville", tz);
727         timezones0.put("Australia/Brisbane", tz);
728         timezones0.put("Australia/Lindeman", tz);
729         timezones0.put("Pacific/Guam", tz);
730         timezones0.put("Pacific/Port_Moresby", tz);
731         timezones0.put("Pacific/Saipan", tz);
732         timezones0.put("Pacific/Truk", tz);
733         timezones0.put("Pacific/Yap", tz);
734         tz = new SimpleTimeZone
735           (10000 * 3600, "Asia/Vladivostok",
736            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
737            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
738         timezones0.put("Asia/Vladivostok", tz);
739         tz = new SimpleTimeZone
740           (10000 * 3600, "Australia/Hobart",
741            Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
742            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
743         timezones0.put("Australia/Hobart", tz);
744         tz = new SimpleTimeZone
745           (10000 * 3600, "AET",
746            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
747            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
748         timezones0.put("AET", tz);
749         timezones0.put("Australia/Melbourne", tz);
750         timezones0.put("Australia/Sydney", tz);
751         tz = new SimpleTimeZone
752           (10500 * 3600, "Australia/Lord_Howe",
753            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
754            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600);
755         timezones0.put("Australia/Lord_Howe", tz);
756         tz = new SimpleTimeZone
757           (11000 * 3600, "Asia/Magadan",
758            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
759            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
760         timezones0.put("Asia/Magadan", tz);
761         tz = new SimpleTimeZone(11000 * 3600, "SST");
762         timezones0.put("SST", tz);
763         timezones0.put("Pacific/Efate", tz);
764         timezones0.put("Pacific/Guadalcanal", tz);
765         timezones0.put("Pacific/Kosrae", tz);
766         timezones0.put("Pacific/Noumea", tz);
767         timezones0.put("Pacific/Ponape", tz);
768         tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk");
769         timezones0.put("Pacific/Norfolk", tz);
770         tz = new SimpleTimeZone
771           (12000 * 3600, "NST",
772            Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
773            Calendar.MARCH, 3, Calendar.SUNDAY, 2000 * 3600);
774         timezones0.put("NST", tz);
775         timezones0.put("Antarctica/McMurdo", tz);
776         timezones0.put("Antarctica/South_Pole", tz);
777         timezones0.put("Pacific/Auckland", tz);
778         tz = new SimpleTimeZone
779           (12000 * 3600, "Asia/Anadyr",
780            Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
781            Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
782         timezones0.put("Asia/Anadyr", tz);
783         timezones0.put("Asia/Kamchatka", tz);
784         tz = new SimpleTimeZone(12000 * 3600, "Pacific/Fiji");
785         timezones0.put("Pacific/Fiji", tz);
786         timezones0.put("Pacific/Funafuti", tz);
787         timezones0.put("Pacific/Kwajalein", tz);
788         timezones0.put("Pacific/Majuro", tz);
789         timezones0.put("Pacific/Nauru", tz);
790         timezones0.put("Pacific/Tarawa", tz);
791         timezones0.put("Pacific/Wake", tz);
792         timezones0.put("Pacific/Wallis", tz);
793         tz = new SimpleTimeZone
794           (12750 * 3600, "Pacific/Chatham",
795            Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600,
796            Calendar.MARCH, 3, Calendar.SUNDAY, 2750 * 3600);
797         timezones0.put("Pacific/Chatham", tz);
798         tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury");
799         timezones0.put("Pacific/Enderbury", tz);
800         timezones0.put("Pacific/Tongatapu", tz);
801         tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati");
802         timezones0.put("Pacific/Kiritimati", tz);
803       }
804     return timezones0;
805   }
806
807   /**
808    * Maps a time zone name (with optional GMT offset and daylight time
809    * zone name) to one of the known time zones.  This method called
810    * with the result of <code>System.getProperty("user.timezone")</code>
811    * or <code>getDefaultTimeZoneId()</code>.  Note that giving one of
812    * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is
813    * preferred.  
814    * The time zone name can be given as follows:
815    * <code>(standard zone name)[(GMT offset)[(DST zone name)[DST offset]]]
816    * </code>
817    * <p>
818    * If only a (standard zone name) is given (no numbers in the
819    * String) then it gets mapped directly to the TimeZone with that
820    * name, if that fails null is returned.
821    * <p>
822    * Alternately, a POSIX-style TZ string can be given, defining the time zone:
823    * <code>std offset dst offset,date/time,date/time</code>
824    * See the glibc manual, or the man page for <code>tzset</code> for details
825    * of this format.
826    * <p>
827    * A GMT offset is the offset to add to the local time to get GMT.
828    * If a (GMT offset) is included (either in seconds or hours) then
829    * an attempt is made to find a TimeZone name matching both the name
830    * and the offset (that doesn't observe daylight time, if the
831    * timezone observes daylight time then you must include a daylight
832    * time zone name after the offset), if that fails then a TimeZone
833    * with the given GMT offset is returned (whether or not the
834    * TimeZone observes daylight time is ignored), if that also fails
835    * the GMT TimeZone is returned.
836    * <p>
837    * If the String ends with (GMT offset)(daylight time zone name)
838    * then an attempt is made to find a TimeZone with the given name and
839    * GMT offset that also observes (the daylight time zone name is not
840    * currently used in any other way), if that fails a TimeZone with
841    * the given GMT offset that observes daylight time is returned, if
842    * that also fails the GMT TimeZone is returned.
843    * <p>
844    * Examples: In Chicago, the time zone id could be "CST6CDT", but
845    * the preferred name would be "America/Chicago".  In Indianapolis
846    * (which does not have Daylight Savings Time) the string could be
847    * "EST5", but the preferred name would be "America/Indianapolis".
848    * The standard time zone name for The Netherlands is "Europe/Amsterdam",
849    * but can also be given as "CET-1CEST".
850    */
851   static TimeZone getDefaultTimeZone(String sysTimeZoneId)
852   {
853     String stdName = null;
854     String dstName;
855     int stdOffs;
856     int dstOffs;
857     try
858       {
859         int idLength = sysTimeZoneId.length();
860
861         int index = 0;
862         int prevIndex;
863         char c;
864
865         // get std
866         do
867           c = sysTimeZoneId.charAt(index++);
868         while (c != '+' && c != '-' && c != ',' && c != ':'
869                && ! Character.isDigit(c) && c != '\0' && index < idLength);
870
871         if (index >= idLength)
872           return (TimeZone)timezones().get(sysTimeZoneId);
873
874         stdName = sysTimeZoneId.substring(0, --index);
875         prevIndex = index;
876
877         // get the std offset
878         do
879           c = sysTimeZoneId.charAt(index++);
880         while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
881                && index < idLength);
882         if (index < idLength)
883           index--;
884
885         { // convert the dst string to a millis number
886             String offset = sysTimeZoneId.substring(prevIndex, index);
887             prevIndex = index;
888
889             if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
890               stdOffs = parseTime(offset.substring(1));
891             else
892               stdOffs = parseTime(offset);
893
894             if (offset.charAt(0) == '-')
895               stdOffs = -stdOffs;
896
897             // TZ timezone offsets are positive when WEST of the meridian.
898             stdOffs = -stdOffs;
899         }
900
901         // Done yet? (Format: std offset)
902         if (index >= idLength)
903           {
904             // Do we have an existing timezone with that name and offset?
905             TimeZone tz = (TimeZone) timezones().get(stdName);
906             if (tz != null)
907               if (tz.getRawOffset() == stdOffs)
908                 return tz;
909
910             // Custom then.
911             return new SimpleTimeZone(stdOffs, stdName);
912           }
913
914         // get dst
915         do
916           c = sysTimeZoneId.charAt(index++);
917         while (c != '+' && c != '-' && c != ',' && c != ':'
918                && ! Character.isDigit(c) && c != '\0' && index < idLength);
919
920         // Done yet? (Format: std offset dst)
921         if (index >= idLength)
922           {
923             // Do we have an existing timezone with that name and offset 
924             // which has DST?
925             TimeZone tz = (TimeZone) timezones().get(stdName);
926             if (tz != null)
927               if (tz.getRawOffset() == stdOffs && tz.useDaylightTime())
928                 return tz;
929
930             // Custom then.
931             return new SimpleTimeZone(stdOffs, stdName);
932           }
933
934         // get the dst offset
935         dstName = sysTimeZoneId.substring(prevIndex, --index);
936         prevIndex = index;
937         do
938           c = sysTimeZoneId.charAt(index++);
939         while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
940                && index < idLength);
941         if (index < idLength)
942           index--;
943
944         { // convert the dst string to a millis number
945             String offset = sysTimeZoneId.substring(prevIndex, index);
946             prevIndex = index;
947
948             if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
949               dstOffs = parseTime(offset.substring(1));
950             else
951               dstOffs = parseTime(offset);
952
953             if (offset.charAt(0) == '-')
954               dstOffs = -dstOffs;
955
956             // TZ timezone offsets are positive when WEST of the meridian.
957             dstOffs = -dstOffs;
958         }
959
960         // Done yet? (Format: std offset dst offset)
961         // FIXME: We don't support DST without a rule given. Should we?
962         if (index >= idLength)
963           {
964             // Time Zone existing with same name, dst and offsets?
965             TimeZone tz = (TimeZone) timezones().get(stdName);
966             if (tz != null)
967               if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
968                   && tz.getDSTSavings() == (dstOffs - stdOffs))
969                 return tz;
970
971             return new SimpleTimeZone(stdOffs, stdName);
972           }
973
974         // get the DST rule
975         if (sysTimeZoneId.charAt(index) == ','
976             || sysTimeZoneId.charAt(index) == ';')
977           {
978             index++;
979             int offs = index;
980             while (sysTimeZoneId.charAt(index) != ','
981                    && sysTimeZoneId.charAt(index) != ';')
982               index++;
983             String startTime = sysTimeZoneId.substring(offs, index);
984             index++;
985             String endTime = sysTimeZoneId.substring(index);
986
987             index = startTime.indexOf('/');
988             int startMillis;
989             int endMillis;
990             String startDate;
991             String endDate;
992             if (index != -1)
993               {
994                 startDate = startTime.substring(0, index);
995                 startMillis = parseTime(startTime.substring(index + 1));
996               }
997             else
998               {
999                 startDate = startTime;
1000                 // if time isn't given, default to 2:00:00 AM.
1001                 startMillis = 2 * 60 * 60 * 1000;
1002               }
1003             index = endTime.indexOf('/');
1004             if (index != -1)
1005               {
1006                 endDate = endTime.substring(0, index);
1007                 endMillis = parseTime(endTime.substring(index + 1));
1008               }
1009             else
1010               {
1011                 endDate = endTime;
1012                 // if time isn't given, default to 2:00:00 AM.
1013                 endMillis = 2 * 60 * 60 * 1000;
1014               }
1015
1016             int[] start = getDateParams(startDate);
1017             int[] end = getDateParams(endDate);
1018             return new SimpleTimeZone(stdOffs, stdName, start[0], start[1],
1019                                       start[2], startMillis, end[0], end[1],
1020                                       end[2], endMillis, (dstOffs - stdOffs));
1021           }
1022       }
1023
1024     // FIXME: Produce a warning here?
1025     catch (IndexOutOfBoundsException _)
1026       {
1027       }
1028     catch (NumberFormatException _)
1029       {
1030       }
1031
1032     return null;
1033   }
1034
1035   /**
1036    * Parses and returns the params for a POSIX TZ date field,
1037    * in the format int[]{ month, day, dayOfWeek }, following the
1038    * SimpleTimeZone constructor rules.
1039    */
1040   private static int[] getDateParams(String date)
1041   {
1042     int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
1043     int month;
1044
1045     if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
1046       {
1047         int day;
1048
1049         // Month, week of month, day of week
1050         month = Integer.parseInt(date.substring(1, date.indexOf('.')));
1051         int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
1052                                                    date.lastIndexOf('.')));
1053         int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
1054                                                         + 1));
1055         if (week == 5)
1056           day = -1; // last day of month is -1 in java, 5 in TZ
1057         else
1058           // first day of week starting on or after.
1059           day = (week - 1) * 7 + 1;
1060
1061         dayOfWeek++; // Java day of week is one-based, Sunday is first day.
1062         month--; // Java month is zero-based.
1063         return new int[] { month, day, dayOfWeek };
1064       }
1065
1066     // julian day, either zero-based 0<=n<=365 (incl feb 29)
1067     // or one-based 1<=n<=365 (no feb 29)
1068     int julianDay; // Julian day, 
1069
1070     if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
1071       {
1072         julianDay = Integer.parseInt(date.substring(1));
1073         julianDay++; // make 1-based
1074         // Adjust day count to include feb 29.
1075         dayCount = new int[]
1076                    {
1077                      0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
1078                    };
1079       }
1080     else
1081       // 1-based julian day
1082       julianDay = Integer.parseInt(date);
1083
1084     int i = 11;
1085     while (i > 0)
1086       if (dayCount[i] < julianDay)
1087         break;
1088       else
1089         i--;
1090     julianDay -= dayCount[i];
1091     month = i;
1092     return new int[] { month, julianDay, 0 };
1093   }
1094
1095   /**
1096    * Parses a time field hh[:mm[:ss]], returning the result
1097    * in milliseconds. No leading sign.
1098    */
1099   private static int parseTime(String time)
1100   {
1101     int millis = 0;
1102     int i = 0;
1103
1104     while (i < time.length())
1105       if (time.charAt(i) == ':')
1106         break;
1107       else
1108         i++;
1109     millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
1110     if (i >= time.length())
1111       return millis;
1112
1113     int iprev = ++i;
1114     while (i < time.length())
1115       if (time.charAt(i) == ':')
1116         break;
1117       else
1118         i++;
1119     if (i >= time.length())
1120       return millis;
1121
1122     millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
1123     millis += 1000 * Integer.parseInt(time.substring(++i));
1124     return millis;
1125   }
1126
1127   /**
1128    * Gets the time zone offset, for current date, modified in case of 
1129    * daylight savings.  This is the offset to add to UTC to get the local
1130    * time.
1131    * @param era the era of the given date
1132    * @param year the year of the given date
1133    * @param month the month of the given date, 0 for January.
1134    * @param day the day of month
1135    * @param dayOfWeek the day of week
1136    * @param milliseconds the millis in the day (in local standard time)
1137    * @return the time zone offset in milliseconds.
1138    */
1139   public abstract int getOffset(int era, int year, int month,
1140                                 int day, int dayOfWeek, int milliseconds);
1141
1142   /**
1143    * Get the time zone offset for the specified date, modified in case of
1144    * daylight savings.  This is the offset to add to UTC to get the local
1145    * time.
1146    * @param date the date represented in millisecends
1147    * since January 1, 1970 00:00:00 GMT.
1148    * @since 1.4
1149    */
1150   public int getOffset(long date)
1151   {
1152     return (inDaylightTime(new Date(date))
1153             ? getRawOffset() + getDSTSavings()
1154             : getRawOffset());
1155   }
1156   
1157   /**
1158    * Gets the time zone offset, ignoring daylight savings.  This is
1159    * the offset to add to UTC to get the local time.
1160    * @return the time zone offset in milliseconds.  
1161    */
1162   public abstract int getRawOffset();
1163
1164   /**
1165    * Sets the time zone offset, ignoring daylight savings.  This is
1166    * the offset to add to UTC to get the local time.
1167    * @param offsetMillis the time zone offset to GMT.
1168    */
1169   public abstract void setRawOffset(int offsetMillis);
1170
1171   /**
1172    * Gets the identifier of this time zone. For instance, PST for
1173    * Pacific Standard Time.
1174    * @returns the ID of this time zone.  
1175    */
1176   public String getID()
1177   {
1178     return ID;
1179   }
1180
1181   /**
1182    * Sets the identifier of this time zone. For instance, PST for
1183    * Pacific Standard Time.
1184    * @param id the new time zone ID.
1185    * @throws NullPointerException if <code>id</code> is <code>null</code>
1186    */
1187   public void setID(String id)
1188   {
1189     if (id == null)
1190       throw new NullPointerException();
1191     
1192     this.ID = id;
1193   }
1194
1195   /**
1196    * This method returns a string name of the time zone suitable
1197    * for displaying to the user.  The string returned will be the long
1198    * description of the timezone in the current locale.  The name
1199    * displayed will assume daylight savings time is not in effect.
1200    *
1201    * @return The name of the time zone.
1202    */
1203   public final String getDisplayName()
1204   {
1205     return (getDisplayName(false, LONG, Locale.getDefault()));
1206   }
1207
1208   /**
1209    * This method returns a string name of the time zone suitable
1210    * for displaying to the user.  The string returned will be the long
1211    * description of the timezone in the specified locale. The name
1212    * displayed will assume daylight savings time is not in effect.
1213    *
1214    * @param locale The locale for this timezone name.
1215    *
1216    * @return The name of the time zone.
1217    */
1218   public final String getDisplayName(Locale locale)
1219   {
1220     return (getDisplayName(false, LONG, locale));
1221   }
1222
1223   /**
1224    * This method returns a string name of the time zone suitable
1225    * for displaying to the user.  The string returned will be of the
1226    * specified type in the current locale. 
1227    *
1228    * @param dst Whether or not daylight savings time is in effect.
1229    * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
1230    * a short abbreviation.
1231    *
1232    * @return The name of the time zone.
1233    */
1234   public final String getDisplayName(boolean dst, int style)
1235   {
1236     return (getDisplayName(dst, style, Locale.getDefault()));
1237   }
1238
1239
1240   /**
1241    * This method returns a string name of the time zone suitable
1242    * for displaying to the user.  The string returned will be of the
1243    * specified type in the specified locale. 
1244    *
1245    * @param dst Whether or not daylight savings time is in effect.
1246    * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
1247    * a short abbreviation.
1248    * @param locale The locale for this timezone name.
1249    *
1250    * @return The name of the time zone.
1251    */
1252   public String getDisplayName(boolean dst, int style, Locale locale)
1253   {
1254     DateFormatSymbols dfs;
1255     try
1256       {
1257         dfs = new DateFormatSymbols(locale);
1258
1259         // The format of the value returned is defined by us.
1260         String[][]zoneinfo = dfs.getZoneStrings();
1261         for (int i = 0; i < zoneinfo.length; i++)
1262           {
1263             if (zoneinfo[i][0].equals(getID()))
1264               {
1265                 if (!dst)
1266                   {
1267                     if (style == SHORT)
1268                       return (zoneinfo[i][2]);
1269                     else
1270                       return (zoneinfo[i][1]);
1271                   }
1272                 else
1273                   {
1274                     if (style == SHORT)
1275                       return (zoneinfo[i][4]);
1276                     else
1277                       return (zoneinfo[i][3]);
1278                   }
1279               }
1280           }
1281       }
1282     catch (MissingResourceException e)
1283       {
1284       }
1285
1286     return getDefaultDisplayName(dst);
1287   }
1288
1289   private String getDefaultDisplayName(boolean dst)
1290   {
1291     int offset = getRawOffset();
1292     if (dst && this instanceof SimpleTimeZone)
1293       {
1294         // ugly, but this is a design failure of the API:
1295         // getDisplayName takes a dst parameter even though
1296         // TimeZone knows nothing about daylight saving offsets.
1297         offset += ((SimpleTimeZone) this).getDSTSavings();
1298       }
1299
1300     StringBuffer sb = new StringBuffer(9);
1301     sb.append("GMT");
1302
1303     offset = offset / (1000 * 60);
1304     int hours = Math.abs(offset) / 60;
1305     int minutes = Math.abs(offset) % 60;
1306
1307     if (minutes != 0 || hours != 0)
1308       {
1309         sb.append(offset >= 0 ? '+' : '-');
1310         sb.append((char) ('0' + hours / 10));
1311         sb.append((char) ('0' + hours % 10));
1312         sb.append(':');
1313         sb.append((char) ('0' + minutes / 10));
1314         sb.append((char) ('0' + minutes % 10));
1315       }
1316
1317     return sb.toString();
1318   }
1319
1320   /** 
1321    * Returns true, if this time zone uses Daylight Savings Time.
1322    */
1323   public abstract boolean useDaylightTime();
1324
1325   /**
1326    * Returns true, if the given date is in Daylight Savings Time in this
1327    * time zone.
1328    * @param date the given Date.
1329    */
1330   public abstract boolean inDaylightTime(Date date);
1331
1332   /**
1333    * Gets the daylight savings offset.  This is a positive offset in
1334    * milliseconds with respect to standard time.  Typically this
1335    * is one hour, but for some time zones this may be half an our.
1336    * <p>The default implementation returns 3600000 milliseconds
1337    * (one hour) if the time zone uses daylight savings time
1338    * (as specified by {@link #useDaylightTime()}), otherwise
1339    * it returns 0.
1340    * @return the daylight savings offset in milliseconds.
1341    * @since 1.4
1342    */
1343   public int getDSTSavings ()
1344   {
1345     return useDaylightTime () ? 3600000 : 0;
1346   }
1347
1348   /**
1349    * Gets the TimeZone for the given ID.
1350    * @param ID the time zone identifier.
1351    * @return The time zone for the identifier or GMT, if no such time
1352    * zone exists.
1353    */
1354   // FIXME: XXX: JCL indicates this and other methods are synchronized.
1355   public static TimeZone getTimeZone(String ID)
1356   {
1357     // First check timezones hash
1358     TimeZone tz = (TimeZone) timezones().get(ID);
1359     if (tz != null)
1360       {
1361         if (tz.getID().equals(ID))
1362           return tz;
1363
1364         // We always return a timezone with the requested ID.
1365         // This is the same behaviour as with JDK1.2.
1366         tz = (TimeZone) tz.clone();
1367         tz.setID(ID);
1368         // We also save the alias, so that we return the same
1369         // object again if getTimeZone is called with the same
1370         // alias.
1371         timezones().put(ID, tz);
1372         return tz;
1373       }
1374
1375     // See if the ID is really a GMT offset form.
1376     // Note that GMT is in the table so we know it is different.
1377     if (ID.startsWith("GMT"))
1378       {
1379         int pos = 3;
1380         int offset_direction = 1;
1381
1382         if (ID.charAt(pos) == '-')
1383           {
1384             offset_direction = -1;
1385             pos++;
1386           }
1387         else if (ID.charAt(pos) == '+')
1388           {
1389             pos++;
1390           }
1391
1392         try
1393           {
1394             int hour, minute;
1395
1396             String offset_str = ID.substring(pos);
1397             int idx = offset_str.indexOf(":");
1398             if (idx != -1)
1399               {
1400                 hour = Integer.parseInt(offset_str.substring(0, idx));
1401                 minute = Integer.parseInt(offset_str.substring(idx + 1));
1402               }
1403             else
1404               {
1405                 int offset_length = offset_str.length();
1406                 if (offset_length <= 2)
1407                   {
1408                     // Only hour
1409                     hour = Integer.parseInt(offset_str);
1410                     minute = 0;
1411                   }
1412                 else
1413                   {
1414                     // hour and minute, not separated by colon
1415                     hour = Integer.parseInt
1416                       (offset_str.substring(0, offset_length - 2));
1417                     minute = Integer.parseInt
1418                       (offset_str.substring(offset_length - 2));
1419                   }
1420               }
1421
1422             return new SimpleTimeZone((hour * (60 * 60 * 1000) +
1423                                        minute * (60 * 1000))
1424                                       * offset_direction, ID);
1425           }
1426         catch (NumberFormatException e)
1427           {
1428           }
1429       }
1430
1431     // Finally, return GMT per spec
1432     return getTimeZone("GMT");
1433   }
1434
1435   /**
1436    * Gets the available IDs according to the given time zone
1437    * offset.  
1438    * @param rawOffset the given time zone GMT offset.
1439    * @return An array of IDs, where the time zone has the specified GMT
1440    * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have
1441    * GMT-07:00, but differ in daylight savings behaviour.
1442    */
1443   public static String[] getAvailableIDs(int rawOffset)
1444   {
1445     int count = 0;
1446     Iterator iter = timezones().entrySet().iterator();
1447     while (iter.hasNext())
1448       {
1449         // Don't iterate the values, since we want to count 
1450         // doubled values (aliases)
1451         Map.Entry entry = (Map.Entry) iter.next();
1452         if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
1453           count++;
1454       }
1455
1456     String[] ids = new String[count];
1457     count = 0;
1458     iter = timezones().entrySet().iterator();
1459     while (iter.hasNext())
1460       {
1461         Map.Entry entry = (Map.Entry) iter.next();
1462         if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
1463           ids[count++] = (String) entry.getKey();
1464       }
1465     return ids;
1466   }
1467
1468   /**
1469    * Gets all available IDs.
1470    * @return An array of all supported IDs.
1471    */
1472   public static String[] getAvailableIDs()
1473   {
1474     return (String[])
1475       timezones().keySet().toArray(new String[timezones().size()]);
1476   }
1477
1478   /**
1479    * Returns the time zone under which the host is running.  This
1480    * can be changed with setDefault.
1481    *
1482    * @return A clone of the current default time zone for this host.
1483    * @see #setDefault
1484    */
1485   public static TimeZone getDefault()
1486   {
1487     return (TimeZone) defaultZone().clone();
1488   }
1489
1490   public static void setDefault(TimeZone zone)
1491   {
1492     // Hmmmm. No Security checks?
1493     defaultZone0 = zone;
1494   }
1495
1496   /**
1497    * Test if the other time zone uses the same rule and only
1498    * possibly differs in ID.  This implementation for this particular
1499    * class will return true if the raw offsets are identical.  Subclasses
1500    * should override this method if they use daylight savings.
1501    * @return true if this zone has the same raw offset
1502    */
1503   public boolean hasSameRules(TimeZone other)
1504   {
1505     return other.getRawOffset() == getRawOffset();
1506   }
1507
1508   /**
1509    * Returns a clone of this object.  I can't imagine, why this is
1510    * useful for a time zone.
1511    */
1512   public Object clone()
1513   {
1514     try
1515       {
1516         return super.clone();
1517       }
1518     catch (CloneNotSupportedException ex)
1519       {
1520         return null;
1521       }
1522   }
1523 }