OSDN Git Service

f349b031e95959f11c41467c39811b8d688c8686
[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         if (index == prevIndex && (c == ',' || c == ';'))
981           {
982             // Missing dst offset defaults to one hour ahead of standard
983             // time.  
984             dstOffs = stdOffs + 60 * 60 * 1000;
985           }
986         else
987           { // convert the dst string to a millis number
988             String offset = sysTimeZoneId.substring(prevIndex, index);
989             prevIndex = index;
990
991             if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
992               dstOffs = parseTime(offset.substring(1));
993             else
994               dstOffs = parseTime(offset);
995
996             if (offset.charAt(0) == '-')
997               dstOffs = -dstOffs;
998
999             // TZ timezone offsets are positive when WEST of the meridian.
1000             dstOffs = -dstOffs;
1001           }
1002
1003         // Done yet? (Format: std offset dst offset)
1004         // FIXME: We don't support DST without a rule given. Should we?
1005         if (index >= idLength)
1006           {
1007             // Time Zone existing with same name, dst and offsets?
1008             TimeZone tz = (TimeZone) timezones().get(stdName);
1009             if (tz != null)
1010               if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
1011                   && tz.getDSTSavings() == (dstOffs - stdOffs))
1012                 return tz;
1013
1014             return new SimpleTimeZone(stdOffs, stdName);
1015           }
1016
1017         // get the DST rule
1018         if (sysTimeZoneId.charAt(index) == ','
1019             || sysTimeZoneId.charAt(index) == ';')
1020           {
1021             index++;
1022             int offs = index;
1023             while (sysTimeZoneId.charAt(index) != ','
1024                    && sysTimeZoneId.charAt(index) != ';')
1025               index++;
1026             String startTime = sysTimeZoneId.substring(offs, index);
1027             index++;
1028             String endTime = sysTimeZoneId.substring(index);
1029
1030             index = startTime.indexOf('/');
1031             int startMillis;
1032             int endMillis;
1033             String startDate;
1034             String endDate;
1035             if (index != -1)
1036               {
1037                 startDate = startTime.substring(0, index);
1038                 startMillis = parseTime(startTime.substring(index + 1));
1039               }
1040             else
1041               {
1042                 startDate = startTime;
1043                 // if time isn't given, default to 2:00:00 AM.
1044                 startMillis = 2 * 60 * 60 * 1000;
1045               }
1046             index = endTime.indexOf('/');
1047             if (index != -1)
1048               {
1049                 endDate = endTime.substring(0, index);
1050                 endMillis = parseTime(endTime.substring(index + 1));
1051               }
1052             else
1053               {
1054                 endDate = endTime;
1055                 // if time isn't given, default to 2:00:00 AM.
1056                 endMillis = 2 * 60 * 60 * 1000;
1057               }
1058
1059             int[] start = getDateParams(startDate);
1060             int[] end = getDateParams(endDate);
1061             return new SimpleTimeZone(stdOffs, stdName, start[0], start[1],
1062                                       start[2], startMillis, end[0], end[1],
1063                                       end[2], endMillis, (dstOffs - stdOffs));
1064           }
1065       }
1066
1067     // FIXME: Produce a warning here?
1068     catch (IndexOutOfBoundsException _)
1069       {
1070       }
1071     catch (NumberFormatException _)
1072       {
1073       }
1074
1075     return null;
1076   }
1077
1078   /**
1079    * Parses and returns the params for a POSIX TZ date field,
1080    * in the format int[]{ month, day, dayOfWeek }, following the
1081    * SimpleTimeZone constructor rules.
1082    */
1083   private static int[] getDateParams(String date)
1084   {
1085     int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
1086     int month;
1087
1088     if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
1089       {
1090         int day;
1091
1092         // Month, week of month, day of week
1093         month = Integer.parseInt(date.substring(1, date.indexOf('.')));
1094         int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
1095                                                    date.lastIndexOf('.')));
1096         int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
1097                                                         + 1));
1098         if (week == 5)
1099           day = -1; // last day of month is -1 in java, 5 in TZ
1100         else
1101           // first day of week starting on or after.
1102           day = (week - 1) * 7 + 1;
1103
1104         dayOfWeek++; // Java day of week is one-based, Sunday is first day.
1105         month--; // Java month is zero-based.
1106         return new int[] { month, day, dayOfWeek };
1107       }
1108
1109     // julian day, either zero-based 0<=n<=365 (incl feb 29)
1110     // or one-based 1<=n<=365 (no feb 29)
1111     int julianDay; // Julian day, 
1112
1113     if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
1114       {
1115         julianDay = Integer.parseInt(date.substring(1));
1116         julianDay++; // make 1-based
1117         // Adjust day count to include feb 29.
1118         dayCount = new int[]
1119                    {
1120                      0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
1121                    };
1122       }
1123     else
1124       // 1-based julian day
1125       julianDay = Integer.parseInt(date);
1126
1127     int i = 11;
1128     while (i > 0)
1129       if (dayCount[i] < julianDay)
1130         break;
1131       else
1132         i--;
1133     julianDay -= dayCount[i];
1134     month = i;
1135     return new int[] { month, julianDay, 0 };
1136   }
1137
1138   /**
1139    * Parses a time field hh[:mm[:ss]], returning the result
1140    * in milliseconds. No leading sign.
1141    */
1142   private static int parseTime(String time)
1143   {
1144     int millis = 0;
1145     int i = 0;
1146
1147     while (i < time.length())
1148       if (time.charAt(i) == ':')
1149         break;
1150       else
1151         i++;
1152     millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
1153     if (i >= time.length())
1154       return millis;
1155
1156     int iprev = ++i;
1157     while (i < time.length())
1158       if (time.charAt(i) == ':')
1159         break;
1160       else
1161         i++;
1162     if (i >= time.length())
1163       return millis;
1164
1165     millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
1166     millis += 1000 * Integer.parseInt(time.substring(++i));
1167     return millis;
1168   }
1169
1170   /**
1171    * Gets the time zone offset, for current date, modified in case of 
1172    * daylight savings.  This is the offset to add to UTC to get the local
1173    * time.
1174    * @param era the era of the given date
1175    * @param year the year of the given date
1176    * @param month the month of the given date, 0 for January.
1177    * @param day the day of month
1178    * @param dayOfWeek the day of week
1179    * @param milliseconds the millis in the day (in local standard time)
1180    * @return the time zone offset in milliseconds.
1181    */
1182   public abstract int getOffset(int era, int year, int month,
1183                                 int day, int dayOfWeek, int milliseconds);
1184
1185   /**
1186    * Get the time zone offset for the specified date, modified in case of
1187    * daylight savings.  This is the offset to add to UTC to get the local
1188    * time.
1189    * @param date the date represented in millisecends
1190    * since January 1, 1970 00:00:00 GMT.
1191    * @since 1.4
1192    */
1193   public int getOffset(long date)
1194   {
1195     return (inDaylightTime(new Date(date))
1196             ? getRawOffset() + getDSTSavings()
1197             : getRawOffset());
1198   }
1199   
1200   /**
1201    * Gets the time zone offset, ignoring daylight savings.  This is
1202    * the offset to add to UTC to get the local time.
1203    * @return the time zone offset in milliseconds.  
1204    */
1205   public abstract int getRawOffset();
1206
1207   /**
1208    * Sets the time zone offset, ignoring daylight savings.  This is
1209    * the offset to add to UTC to get the local time.
1210    * @param offsetMillis the time zone offset to GMT.
1211    */
1212   public abstract void setRawOffset(int offsetMillis);
1213
1214   /**
1215    * Gets the identifier of this time zone. For instance, PST for
1216    * Pacific Standard Time.
1217    * @returns the ID of this time zone.  
1218    */
1219   public String getID()
1220   {
1221     return ID;
1222   }
1223
1224   /**
1225    * Sets the identifier of this time zone. For instance, PST for
1226    * Pacific Standard Time.
1227    * @param id the new time zone ID.
1228    * @throws NullPointerException if <code>id</code> is <code>null</code>
1229    */
1230   public void setID(String id)
1231   {
1232     if (id == null)
1233       throw new NullPointerException();
1234     
1235     this.ID = id;
1236   }
1237
1238   /**
1239    * This method returns a string name of the time zone suitable
1240    * for displaying to the user.  The string returned will be the long
1241    * description of the timezone in the current locale.  The name
1242    * displayed will assume daylight savings time is not in effect.
1243    *
1244    * @return The name of the time zone.
1245    */
1246   public final String getDisplayName()
1247   {
1248     return (getDisplayName(false, LONG, Locale.getDefault()));
1249   }
1250
1251   /**
1252    * This method returns a string name of the time zone suitable
1253    * for displaying to the user.  The string returned will be the long
1254    * description of the timezone in the specified locale. The name
1255    * displayed will assume daylight savings time is not in effect.
1256    *
1257    * @param locale The locale for this timezone name.
1258    *
1259    * @return The name of the time zone.
1260    */
1261   public final String getDisplayName(Locale locale)
1262   {
1263     return (getDisplayName(false, LONG, locale));
1264   }
1265
1266   /**
1267    * This method returns a string name of the time zone suitable
1268    * for displaying to the user.  The string returned will be of the
1269    * specified type in the current locale. 
1270    *
1271    * @param dst Whether or not daylight savings time is in effect.
1272    * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
1273    * a short abbreviation.
1274    *
1275    * @return The name of the time zone.
1276    */
1277   public final String getDisplayName(boolean dst, int style)
1278   {
1279     return (getDisplayName(dst, style, Locale.getDefault()));
1280   }
1281
1282
1283   /**
1284    * This method returns a string name of the time zone suitable
1285    * for displaying to the user.  The string returned will be of the
1286    * specified type in the specified locale. 
1287    *
1288    * @param dst Whether or not daylight savings time is in effect.
1289    * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
1290    * a short abbreviation.
1291    * @param locale The locale for this timezone name.
1292    *
1293    * @return The name of the time zone.
1294    */
1295   public String getDisplayName(boolean dst, int style, Locale locale)
1296   {
1297     DateFormatSymbols dfs;
1298     try
1299       {
1300         dfs = new DateFormatSymbols(locale);
1301
1302         // The format of the value returned is defined by us.
1303         String[][]zoneinfo = dfs.getZoneStrings();
1304         for (int i = 0; i < zoneinfo.length; i++)
1305           {
1306             if (zoneinfo[i][0].equals(getID()))
1307               {
1308                 if (!dst)
1309                   {
1310                     if (style == SHORT)
1311                       return (zoneinfo[i][2]);
1312                     else
1313                       return (zoneinfo[i][1]);
1314                   }
1315                 else
1316                   {
1317                     if (style == SHORT)
1318                       return (zoneinfo[i][4]);
1319                     else
1320                       return (zoneinfo[i][3]);
1321                   }
1322               }
1323           }
1324       }
1325     catch (MissingResourceException e)
1326       {
1327       }
1328
1329     return getDefaultDisplayName(dst);
1330   }
1331
1332   private String getDefaultDisplayName(boolean dst)
1333   {
1334     int offset = getRawOffset();
1335     if (dst && this instanceof SimpleTimeZone)
1336       {
1337         // ugly, but this is a design failure of the API:
1338         // getDisplayName takes a dst parameter even though
1339         // TimeZone knows nothing about daylight saving offsets.
1340         offset += ((SimpleTimeZone) this).getDSTSavings();
1341       }
1342
1343     StringBuffer sb = new StringBuffer(9);
1344     sb.append("GMT");
1345
1346     offset = offset / (1000 * 60);
1347     int hours = Math.abs(offset) / 60;
1348     int minutes = Math.abs(offset) % 60;
1349
1350     if (minutes != 0 || hours != 0)
1351       {
1352         sb.append(offset >= 0 ? '+' : '-');
1353         sb.append((char) ('0' + hours / 10));
1354         sb.append((char) ('0' + hours % 10));
1355         sb.append(':');
1356         sb.append((char) ('0' + minutes / 10));
1357         sb.append((char) ('0' + minutes % 10));
1358       }
1359
1360     return sb.toString();
1361   }
1362
1363   /** 
1364    * Returns true, if this time zone uses Daylight Savings Time.
1365    */
1366   public abstract boolean useDaylightTime();
1367
1368   /**
1369    * Returns true, if the given date is in Daylight Savings Time in this
1370    * time zone.
1371    * @param date the given Date.
1372    */
1373   public abstract boolean inDaylightTime(Date date);
1374
1375   /**
1376    * Gets the daylight savings offset.  This is a positive offset in
1377    * milliseconds with respect to standard time.  Typically this
1378    * is one hour, but for some time zones this may be half an our.
1379    * <p>The default implementation returns 3600000 milliseconds
1380    * (one hour) if the time zone uses daylight savings time
1381    * (as specified by {@link #useDaylightTime()}), otherwise
1382    * it returns 0.
1383    * @return the daylight savings offset in milliseconds.
1384    * @since 1.4
1385    */
1386   public int getDSTSavings ()
1387   {
1388     return useDaylightTime () ? 3600000 : 0;
1389   }
1390
1391   /**
1392    * Gets the TimeZone for the given ID.
1393    * @param ID the time zone identifier.
1394    * @return The time zone for the identifier or GMT, if no such time
1395    * zone exists.
1396    */
1397   // FIXME: XXX: JCL indicates this and other methods are synchronized.
1398   public static TimeZone getTimeZone(String ID)
1399   {
1400     // First check timezones hash
1401     TimeZone tz = (TimeZone) timezones().get(ID);
1402     if (tz != null)
1403       {
1404         if (tz.getID().equals(ID))
1405           return tz;
1406
1407         // We always return a timezone with the requested ID.
1408         // This is the same behaviour as with JDK1.2.
1409         tz = (TimeZone) tz.clone();
1410         tz.setID(ID);
1411         // We also save the alias, so that we return the same
1412         // object again if getTimeZone is called with the same
1413         // alias.
1414         timezones().put(ID, tz);
1415         return tz;
1416       }
1417
1418     // See if the ID is really a GMT offset form.
1419     // Note that GMT is in the table so we know it is different.
1420     if (ID.startsWith("GMT"))
1421       {
1422         int pos = 3;
1423         int offset_direction = 1;
1424
1425         if (ID.charAt(pos) == '-')
1426           {
1427             offset_direction = -1;
1428             pos++;
1429           }
1430         else if (ID.charAt(pos) == '+')
1431           {
1432             pos++;
1433           }
1434
1435         try
1436           {
1437             int hour, minute;
1438
1439             String offset_str = ID.substring(pos);
1440             int idx = offset_str.indexOf(":");
1441             if (idx != -1)
1442               {
1443                 hour = Integer.parseInt(offset_str.substring(0, idx));
1444                 minute = Integer.parseInt(offset_str.substring(idx + 1));
1445               }
1446             else
1447               {
1448                 int offset_length = offset_str.length();
1449                 if (offset_length <= 2)
1450                   {
1451                     // Only hour
1452                     hour = Integer.parseInt(offset_str);
1453                     minute = 0;
1454                   }
1455                 else
1456                   {
1457                     // hour and minute, not separated by colon
1458                     hour = Integer.parseInt
1459                       (offset_str.substring(0, offset_length - 2));
1460                     minute = Integer.parseInt
1461                       (offset_str.substring(offset_length - 2));
1462                   }
1463               }
1464
1465             return new SimpleTimeZone((hour * (60 * 60 * 1000) +
1466                                        minute * (60 * 1000))
1467                                       * offset_direction, ID);
1468           }
1469         catch (NumberFormatException e)
1470           {
1471           }
1472       }
1473
1474     // Finally, return GMT per spec
1475     return getTimeZone("GMT");
1476   }
1477
1478   /**
1479    * Gets the available IDs according to the given time zone
1480    * offset.  
1481    * @param rawOffset the given time zone GMT offset.
1482    * @return An array of IDs, where the time zone has the specified GMT
1483    * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have
1484    * GMT-07:00, but differ in daylight savings behaviour.
1485    */
1486   public static String[] getAvailableIDs(int rawOffset)
1487   {
1488     int count = 0;
1489     Iterator iter = timezones().entrySet().iterator();
1490     while (iter.hasNext())
1491       {
1492         // Don't iterate the values, since we want to count 
1493         // doubled values (aliases)
1494         Map.Entry entry = (Map.Entry) iter.next();
1495         if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
1496           count++;
1497       }
1498
1499     String[] ids = new String[count];
1500     count = 0;
1501     iter = timezones().entrySet().iterator();
1502     while (iter.hasNext())
1503       {
1504         Map.Entry entry = (Map.Entry) iter.next();
1505         if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
1506           ids[count++] = (String) entry.getKey();
1507       }
1508     return ids;
1509   }
1510
1511   /**
1512    * Gets all available IDs.
1513    * @return An array of all supported IDs.
1514    */
1515   public static String[] getAvailableIDs()
1516   {
1517     return (String[])
1518       timezones().keySet().toArray(new String[timezones().size()]);
1519   }
1520
1521   /**
1522    * Returns the time zone under which the host is running.  This
1523    * can be changed with setDefault.
1524    *
1525    * @return A clone of the current default time zone for this host.
1526    * @see #setDefault
1527    */
1528   public static TimeZone getDefault()
1529   {
1530     return (TimeZone) defaultZone().clone();
1531   }
1532
1533   public static void setDefault(TimeZone zone)
1534   {
1535     // Hmmmm. No Security checks?
1536     defaultZone0 = zone;
1537   }
1538
1539   /**
1540    * Test if the other time zone uses the same rule and only
1541    * possibly differs in ID.  This implementation for this particular
1542    * class will return true if the raw offsets are identical.  Subclasses
1543    * should override this method if they use daylight savings.
1544    * @return true if this zone has the same raw offset
1545    */
1546   public boolean hasSameRules(TimeZone other)
1547   {
1548     return other.getRawOffset() == getRawOffset();
1549   }
1550
1551   /**
1552    * Returns a clone of this object.  I can't imagine, why this is
1553    * useful for a time zone.
1554    */
1555   public Object clone()
1556   {
1557     try
1558       {
1559         return super.clone();
1560       }
1561     catch (CloneNotSupportedException ex)
1562       {
1563         return null;
1564       }
1565   }
1566 }