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