OSDN Git Service

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