001/* java.util.TimeZone
002   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.util;
041
042import gnu.classpath.SystemProperties;
043import gnu.java.lang.CPStringBuilder;
044import gnu.java.util.ZoneInfo;
045
046import java.io.File;
047import java.security.AccessController;
048import java.security.PrivilegedAction;
049import 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 */
068public 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}