OSDN Git Service

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