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