001: /*
002: * @(#)TimeZone.java 1.51 00/01/19
003: *
004: * Copyright (C) 1996-2006, International Business Machines
005: * Corporation and others. All Rights Reserved.
006: */
007:
008: package com.ibm.icu.util;
009:
010: import java.io.Serializable;
011: import java.lang.ref.SoftReference;
012: import java.util.Date;
013: import java.util.Hashtable;
014: import java.util.Locale;
015: import java.util.MissingResourceException;
016:
017: import com.ibm.icu.impl.ICUResourceBundle;
018: import com.ibm.icu.impl.TimeZoneAdapter;
019: import com.ibm.icu.impl.ZoneMeta;
020: import com.ibm.icu.text.SimpleDateFormat;
021:
022: /**
023: * <code>TimeZone</code> represents a time zone offset, and also figures out daylight
024: * savings.
025: *
026: * <p>
027: * Typically, you get a <code>TimeZone</code> using <code>getDefault</code>
028: * which creates a <code>TimeZone</code> based on the time zone where the program
029: * is running. For example, for a program running in Japan, <code>getDefault</code>
030: * creates a <code>TimeZone</code> object based on Japanese Standard Time.
031: *
032: * <p>
033: * You can also get a <code>TimeZone</code> using <code>getTimeZone</code>
034: * along with a time zone ID. For instance, the time zone ID for the
035: * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a
036: * U.S. Pacific Time <code>TimeZone</code> object with:
037: * <blockquote>
038: * <pre>
039: * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
040: * </pre>
041: * </blockquote>
042: * You can use <code>getAvailableIDs</code> method to iterate through
043: * all the supported time zone IDs. You can then choose a
044: * supported ID to get a <code>TimeZone</code>.
045: * If the time zone you want is not represented by one of the
046: * supported IDs, then you can create a custom time zone ID with
047: * the following syntax:
048: *
049: * <blockquote>
050: * <pre>
051: * GMT[+|-]hh[[:]mm]
052: * </pre>
053: * </blockquote>
054: *
055: * For example, you might specify GMT+14:00 as a custom
056: * time zone ID. The <code>TimeZone</code> that is returned
057: * when you specify a custom time zone ID does not include
058: * daylight savings time.
059: * <p>
060: * For compatibility with JDK 1.1.x, some other three-letter time zone IDs
061: * (such as "PST", "CTT", "AST") are also supported. However, <strong>their
062: * use is deprecated</strong> because the same abbreviation is often used
063: * for multiple time zones (for example, "CST" could be U.S. "Central Standard
064: * Time" and "China Standard Time"), and the Java platform can then only
065: * recognize one of them.
066: *
067: *
068: * @see Calendar
069: * @see GregorianCalendar
070: * @see SimpleTimeZone
071: * @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
072: * @stable ICU 2.0
073: */
074: abstract public class TimeZone implements Serializable, Cloneable {
075: // using serialver from jdk1.4.2_05
076: private static final long serialVersionUID = -744942128318337471L;
077:
078: /**
079: * Default constructor. (For invocation by subclass constructors,
080: * typically implicit.)
081: * @stable ICU 2.8
082: */
083: public TimeZone() {
084: }
085:
086: /**
087: * A style specifier for <code>getDisplayName()</code> indicating
088: * a short name, such as "PST."
089: * @see #LONG
090: * @stable ICU 2.0
091: */
092: public static final int SHORT = 0;
093:
094: /**
095: * A style specifier for <code>getDisplayName()</code> indicating
096: * a long name, such as "Pacific Standard Time."
097: * @see #SHORT
098: * @stable ICU 2.0
099: */
100: public static final int LONG = 1;
101:
102: /**
103: * @internal
104: * @deprecated This API is ICU internal only.
105: */
106: private static final int SHORT_GENERIC = 2;
107:
108: /**
109: * @internal
110: * @deprecated This API is ICU internal only.
111: */
112: private static final int LONG_GENERIC = 3;
113:
114: /**
115: * Cache to hold the SimpleDateFormat objects for a Locale.
116: */
117: private static Hashtable cachedLocaleData = new Hashtable(3);
118:
119: /**
120: * Gets the time zone offset, for current date, modified in case of
121: * daylight savings. This is the offset to add *to* UTC to get local time.
122: * @param era the era of the given date.
123: * @param year the year in the given date.
124: * @param month the month in the given date.
125: * Month is 0-based. e.g., 0 for January.
126: * @param day the day-in-month of the given date.
127: * @param dayOfWeek the day-of-week of the given date.
128: * @param milliseconds the millis in day in <em>standard</em> local time.
129: * @return the offset to add *to* GMT to get local time.
130: * @stable ICU 2.0
131: */
132: abstract public int getOffset(int era, int year, int month,
133: int day, int dayOfWeek, int milliseconds);
134:
135: /**
136: * Returns the offset of this time zone from UTC at the specified
137: * date. If Daylight Saving Time is in effect at the specified
138: * date, the offset value is adjusted with the amount of daylight
139: * saving.
140: *
141: * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT
142: * @return the amount of time in milliseconds to add to UTC to get local time.
143: *
144: * @see Calendar#ZONE_OFFSET
145: * @see Calendar#DST_OFFSET
146: * @see #getOffset(long, boolean, int[])
147: * @stable ICU 2.8
148: */
149: public int getOffset(long date) {
150: int[] result = new int[2];
151: getOffset(date, false, result);
152: return result[0] + result[1];
153: }
154:
155: /**
156: * Returns the time zone raw and GMT offset for the given moment
157: * in time. Upon return, local-millis = GMT-millis + rawOffset +
158: * dstOffset. All computations are performed in the proleptic
159: * Gregorian calendar. The default implementation in the TimeZone
160: * class delegates to the 8-argument getOffset().
161: *
162: * @param date moment in time for which to return offsets, in
163: * units of milliseconds from January 1, 1970 0:00 GMT, either GMT
164: * time or local wall time, depending on `local'.
165: * @param local if true, `date' is local wall time; otherwise it
166: * is in GMT time.
167: * @param offsets output parameter to receive the raw offset, that
168: * is, the offset not including DST adjustments, in offsets[0],
169: * and the DST offset, that is, the offset to be added to
170: * `rawOffset' to obtain the total offset between local and GMT
171: * time, in offsets[1]. If DST is not in effect, the DST offset is
172: * zero; otherwise it is a positive value, typically one hour.
173: *
174: * @stable ICU 2.8
175: */
176: public void getOffset(long date, boolean local, int[] offsets) {
177: offsets[0] = getRawOffset();
178:
179: // Convert to local wall millis if necessary
180: if (!local) {
181: date += offsets[0]; // now in local standard millis
182: }
183:
184: // When local==FALSE, we might have to recompute. This loop is
185: // executed once, unless a recomputation is required; then it is
186: // executed twice.
187: for (int pass = 0;; ++pass) {
188: int fields[] = new int[4];
189: long day = floorDivide(date, MILLIS_PER_DAY, fields);
190: int millis = fields[0];
191:
192: computeGregorianFields(day, fields);
193:
194: offsets[1] = getOffset(GregorianCalendar.AD, fields[0],
195: fields[1], fields[2], fields[3], millis)
196: - offsets[0];
197:
198: // Recompute if local==FALSE, dstOffset!=0, and addition of
199: // the dstOffset puts us in a different day.
200: if (pass != 0 || local || offsets[1] == 0) {
201: break;
202: }
203: date += offsets[1];
204: if (floorDivide(date, MILLIS_PER_DAY) == day) {
205: break;
206: }
207: }
208: }
209:
210: /**
211: * Divide two long integers, returning the floor of the quotient.
212: * <p>
213: * Unlike the built-in division, this is mathematically well-behaved.
214: * E.g., <code>-1/4</code> => 0
215: * but <code>floorDivide(-1,4)</code> => -1.
216: * TODO: This duplicates a method in Calendar; clean up and
217: * consolidate in ICU 3.0.
218: * @param numerator the numerator
219: * @param denominator a divisor which must be > 0
220: * @return the floor of the quotient.
221: */
222: static long floorDivide(long numerator, long denominator) {
223: // We do this computation in order to handle
224: // a numerator of Long.MIN_VALUE correctly
225: return (numerator >= 0) ? numerator / denominator
226: : ((numerator + 1) / denominator) - 1;
227: }
228:
229: /**
230: * Divide two integers, returning the floor of the quotient, and
231: * the modulus remainder.
232: * <p>
233: * Unlike the built-in division, this is mathematically well-behaved.
234: * E.g., <code>-1/4</code> => 0 and <code>-1%4</code> => -1,
235: * but <code>floorDivide(-1,4)</code> => -1 with <code>remainder[0]</code> => 3.
236: * TODO: This duplicates a method in Calendar; clean up and
237: * consolidate in ICU 3.0.
238: * @param numerator the numerator
239: * @param denominator a divisor which must be > 0
240: * @param remainder an array of at least one element in which the value
241: * <code>numerator mod denominator</code> is returned. Unlike <code>numerator
242: * % denominator</code>, this will always be non-negative.
243: * @return the floor of the quotient.
244: */
245: static int floorDivide(long numerator, int denominator,
246: int[] remainder) {
247: if (numerator >= 0) {
248: remainder[0] = (int) (numerator % denominator);
249: return (int) (numerator / denominator);
250: }
251: int quotient = (int) (((numerator + 1) / denominator) - 1);
252: remainder[0] = (int) (numerator - (quotient * denominator));
253: return quotient;
254: }
255:
256: /**
257: * Compute the Gregorian calendar year, month, and day of month
258: * from the epoch day, and return them in the given array.
259: * TODO: This duplicates a method in Calendar; clean up and
260: * consolidate in ICU 3.0.
261: */
262: static void computeGregorianFields(long day, int fields[]) {
263: int year, month, dayOfMonth, dayOfYear;
264:
265: // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
266: // JULIAN_1_CE = 1721426; // January 1, 1 CE Gregorian
267: // JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian
268: day += (2440588 - 1721426);
269:
270: // Here we convert from the day number to the multiple radix
271: // representation. We use 400-year, 100-year, and 4-year cycles.
272: // For example, the 4-year cycle has 4 years + 1 leap day; giving
273: // 1461 == 365*4 + 1 days.
274: int[] rem = new int[1];
275: int n400 = floorDivide(day, 146097, rem); // 400-year cycle length
276: int n100 = floorDivide(rem[0], 36524, rem); // 100-year cycle length
277: int n4 = floorDivide(rem[0], 1461, rem); // 4-year cycle length
278: int n1 = floorDivide(rem[0], 365, rem);
279: year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
280: dayOfYear = rem[0]; // zero-based day of year
281: if (n100 == 4 || n1 == 4) {
282: dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
283: } else {
284: ++year;
285: }
286:
287: boolean isLeap = ((year & 0x3) == 0) && // equiv. to (year%4 == 0)
288: (year % 100 != 0 || year % 400 == 0);
289:
290: int correction = 0;
291: int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
292: if (dayOfYear >= march1)
293: correction = isLeap ? 1 : 2;
294: month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
295: dayOfMonth = dayOfYear
296: - GREGORIAN_MONTH_COUNT[month][isLeap ? 1 : 0] + 1; // one-based DOM
297:
298: // Jan 1 1 CE is Monday
299: int dayOfWeek = (int) ((day + Calendar.MONDAY) % 7);
300: if (dayOfWeek < Calendar.SUNDAY) {
301: dayOfWeek += 7;
302: }
303:
304: fields[0] = year;
305: fields[1] = month; // 0-based already
306: fields[2] = dayOfMonth; // 1-based already
307: fields[3] = dayOfWeek; // 1-based already
308: //fields[4] = dayOfYear + 1; // Convert from 0-based to 1-based
309: }
310:
311: /**
312: * The number of milliseconds in an hour.
313: * @internal
314: * @deprecated This API is ICU internal only.
315: */
316: protected static final int MILLIS_PER_HOUR = 60 * 60 * 1000;
317:
318: /**
319: * The number of milliseconds in one day.
320: * @internal
321: * @deprecated This API is ICU internal only.
322: */
323: protected static final int MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
324:
325: /**
326: * For each month, the days in a non-leap year before the start
327: * the of month, and the days in a leap year before the start of
328: * the month.
329: * TODO: This duplicates data in Calendar.java; clean up and
330: * consolidate in ICU 3.0.
331: */
332: static final int[][] GREGORIAN_MONTH_COUNT = { { 0, 0 }, // Jan
333: { 31, 31 }, // Feb
334: { 59, 60 }, // Mar
335: { 90, 91 }, // Apr
336: { 120, 121 }, // May
337: { 151, 152 }, // Jun
338: { 181, 182 }, // Jul
339: { 212, 213 }, // Aug
340: { 243, 244 }, // Sep
341: { 273, 274 }, // Oct
342: { 304, 305 }, // Nov
343: { 334, 335 } // Dec
344: };
345:
346: /**
347: * Sets the base time zone offset to GMT.
348: * This is the offset to add *to* UTC to get local time.
349: * @param offsetMillis the given base time zone offset to GMT.
350: * @stable ICU 2.0
351: */
352: abstract public void setRawOffset(int offsetMillis);
353:
354: /**
355: * Gets unmodified offset, NOT modified in case of daylight savings.
356: * This is the offset to add *to* UTC to get local time.
357: * @return the unmodified offset to add *to* UTC to get local time.
358: * @stable ICU 2.0
359: */
360: abstract public int getRawOffset();
361:
362: /**
363: * Gets the ID of this time zone.
364: * @return the ID of this time zone.
365: * @stable ICU 2.0
366: */
367: public String getID() {
368: return ID;
369: }
370:
371: /**
372: * Sets the time zone ID. This does not change any other data in
373: * the time zone object.
374: * @param ID the new time zone ID.
375: * @stable ICU 2.0
376: */
377: public void setID(String ID) {
378: if (ID == null) {
379: throw new NullPointerException();
380: }
381: this .ID = ID;
382: }
383:
384: /**
385: * Returns a name of this time zone suitable for presentation to the user
386: * in the default locale.
387: * This method returns the long generic name.
388: * If the display name is not available for the locale,
389: * a fallback based on the country, city, or time zone id will be used.
390: * @return the human-readable name of this time zone in the default locale.
391: * @stable ICU 2.0
392: */
393: public final String getDisplayName() {
394: return _getDisplayName(false, LONG_GENERIC, ULocale
395: .getDefault());
396: }
397:
398: /**
399: * Returns a name of this time zone suitable for presentation to the user
400: * in the specified locale.
401: * This method returns the long generic name.
402: * If the display name is not available for the locale,
403: * a fallback based on the country, city, or time zone id will be used.
404: * @param locale the locale in which to supply the display name.
405: * @return the human-readable name of this time zone in the given locale
406: * or in the default locale if the given locale is not recognized.
407: * @stable ICU 2.0
408: */
409: public final String getDisplayName(Locale locale) {
410: return _getDisplayName(false, LONG_GENERIC, ULocale
411: .forLocale(locale));
412: }
413:
414: /**
415: * Returns a name of this time zone suitable for presentation to the user
416: * in the specified locale.
417: * This method returns the long name, not including daylight savings.
418: * If the display name is not available for the locale,
419: * a fallback based on the country, city, or time zone id will be used.
420: * @param locale the ulocale in which to supply the display name.
421: * @return the human-readable name of this time zone in the given locale
422: * or in the default ulocale if the given ulocale is not recognized.
423: * @draft ICU 3.2
424: * @provisional This API might change or be removed in a future release.
425: */
426: public final String getDisplayName(ULocale locale) {
427: return _getDisplayName(false, LONG_GENERIC, locale);
428: }
429:
430: /**
431: * Returns a name of this time zone suitable for presentation to the user
432: * in the default locale.
433: * If the display name is not available for the locale,
434: * then this method returns a string in the format
435: * <code>GMT[+-]hh:mm</code>.
436: * @param daylight if true, return the daylight savings name.
437: * @param style either <code>LONG</code> or <code>SHORT</code>
438: * @return the human-readable name of this time zone in the default locale.
439: * @stable ICU 2.0
440: */
441: public final String getDisplayName(boolean daylight, int style) {
442: return getDisplayName(daylight, style, ULocale.getDefault());
443: }
444:
445: /**
446: * Returns a name of this time zone suitable for presentation to the user
447: * in the specified locale.
448: * If the display name is not available for the locale,
449: * then this method returns a string in the format
450: * <code>GMT[+-]hh:mm</code>.
451: * @param daylight if true, return the daylight savings name.
452: * @param style either <code>LONG</code> or <code>SHORT</code>
453: * @param locale the locale in which to supply the display name.
454: * @return the human-readable name of this time zone in the given locale
455: * or in the default locale if the given locale is not recognized.
456: * @exception IllegalArgumentException style is invalid.
457: * @stable ICU 2.0
458: */
459: public String getDisplayName(boolean daylight, int style,
460: Locale locale) {
461: return getDisplayName(daylight, style, ULocale
462: .forLocale(locale));
463: }
464:
465: /**
466: * Returns a name of this time zone suitable for presentation to the user
467: * in the specified locale.
468: * If the display name is not available for the locale,
469: * then this method returns a string in the format
470: * <code>GMT[+-]hh:mm</code>.
471: * @param daylight if true, return the daylight savings name.
472: * @param style either <code>LONG</code> or <code>SHORT</code>
473: * @param locale the locale in which to supply the display name.
474: * @return the human-readable name of this time zone in the given locale
475: * or in the default locale if the given locale is not recognized.
476: * @exception IllegalArgumentException style is invalid.
477: * @draft ICU 3.2
478: * @provisional This API might change or be removed in a future release.
479: */
480: public String getDisplayName(boolean daylight, int style,
481: ULocale locale) {
482: if (style != SHORT && style != LONG) {
483: throw new IllegalArgumentException("Illegal style: "
484: + style);
485: }
486: return _getDisplayName(daylight, style, locale);
487: }
488:
489: /**
490: * The public version of this API only accepts LONG/SHORT, the
491: * internal version (which this calls) also accepts LONG_GENERIC/SHORT_GENERIC.
492: * @internal
493: * @deprecated This API is ICU internal only.
494: */
495: private String _getDisplayName(boolean daylight, int style,
496: ULocale locale) {
497: /* NOTES:
498: * (1) We use SimpleDateFormat for simplicity; we could do this
499: * more efficiently but it would duplicate the SimpleDateFormat code
500: * here, which is undesirable.
501: * (2) Attempts to move the code from SimpleDateFormat to here also run
502: * aground because this requires SimpleDateFormat to keep a Locale
503: * object around, which it currently doesn't; to synthesize such a
504: * locale upon resurrection; and to somehow handle the special case of
505: * construction from a DateFormatSymbols object.
506: */
507:
508: // We keep a cache, indexed by locale. The cache contains a
509: // SimpleDateFormat object, which we create on demand.
510: SoftReference data = (SoftReference) cachedLocaleData
511: .get(locale);
512: SimpleDateFormat format;
513: if (data == null
514: || (format = (SimpleDateFormat) data.get()) == null) {
515: format = new SimpleDateFormat(null, locale);
516: cachedLocaleData.put(locale, new SoftReference(format));
517: }
518: // Create a new SimpleTimeZone as a stand-in for this zone; the stand-in
519: // will have no DST, or DST during January, but the same ID and offset,
520: // and hence the same display name. We don't cache these because
521: // they're small and cheap to create.
522: SimpleTimeZone tz;
523: if (daylight && useDaylightTime()) {
524: int savings = getDSTSavings();
525: tz = new SimpleTimeZone(getRawOffset(), getID(),
526: Calendar.JANUARY, 1, 0, 0, Calendar.FEBRUARY, 1, 0,
527: 0, savings);
528: } else {
529: tz = new SimpleTimeZone(getRawOffset(), getID());
530: }
531: String[] patterns = { "z", "zzzz", "v", "vvvv" };
532: format.applyPattern(patterns[style]);
533: format.setTimeZone(tz);
534: // Format a date in January. We use the value 10*ONE_DAY == Jan 11 1970
535: // 0:00 GMT.
536: return format.format(new Date(864000000L));
537: }
538:
539: /**
540: * Returns the amount of time to be added to local standard time
541: * to get local wall clock time.
542: * <p>
543: * The default implementation always returns 3600000 milliseconds
544: * (i.e., one hour) if this time zone observes Daylight Saving
545: * Time. Otherwise, 0 (zero) is returned.
546: * <p>
547: * If an underlying TimeZone implementation subclass supports
548: * historical Daylight Saving Time changes, this method returns
549: * the known latest daylight saving value.
550: *
551: * @return the amount of saving time in milliseconds
552: * @stable ICU 2.8
553: */
554: public int getDSTSavings() {
555: if (useDaylightTime()) {
556: return 3600000;
557: }
558: return 0;
559: }
560:
561: /**
562: * Queries if this time zone uses daylight savings time.
563: * @return true if this time zone uses daylight savings time,
564: * false, otherwise.
565: * @stable ICU 2.0
566: */
567: abstract public boolean useDaylightTime();
568:
569: /**
570: * Queries if the given date is in daylight savings time in
571: * this time zone.
572: * @param date the given Date.
573: * @return true if the given date is in daylight savings time,
574: * false, otherwise.
575: * @stable ICU 2.0
576: */
577: abstract public boolean inDaylightTime(Date date);
578:
579: /**
580: * Gets the <code>TimeZone</code> for the given ID.
581: *
582: * @param ID the ID for a <code>TimeZone</code>, either an abbreviation
583: * such as "PST", a full name such as "America/Los_Angeles", or a custom
584: * ID such as "GMT-8:00". Note that the support of abbreviations is
585: * for JDK 1.1.x compatibility only and full names should be used.
586: *
587: * @return the specified <code>TimeZone</code>, or the GMT zone if the given ID
588: * cannot be understood.
589: * @stable ICU 2.0
590: */
591: public static synchronized TimeZone getTimeZone(String ID) {
592: /* We first try to lookup the zone ID in our system list. If this
593: * fails, we try to parse it as a custom string GMT[+-]hh:mm. If
594: * all else fails, we return GMT, which is probably not what the
595: * user wants, but at least is a functioning TimeZone object.
596: *
597: * We cannot return NULL, because that would break compatibility
598: * with the JDK.
599: */
600: if (ID == null) {
601: throw new NullPointerException();
602: }
603: TimeZone result = ZoneMeta.getSystemTimeZone(ID);
604:
605: if (result == null) {
606: result = ZoneMeta.getCustomTimeZone(ID);
607: }
608: if (result == null) {
609: result = ZoneMeta.getGMT();
610: }
611: return result;
612: }
613:
614: /**
615: * Return a new String array containing all system TimeZone IDs
616: * with the given raw offset from GMT. These IDs may be passed to
617: * <code>get()</code> to construct the corresponding TimeZone
618: * object.
619: * @param rawOffset the offset in milliseconds from GMT
620: * @return an array of IDs for system TimeZones with the given
621: * raw offset. If there are none, return a zero-length array.
622: * @stable ICU 2.0
623: */
624: public static String[] getAvailableIDs(int rawOffset) {
625: return ZoneMeta.getAvailableIDs(rawOffset);
626:
627: }
628:
629: /**
630: * Return a new String array containing all system TimeZone IDs
631: * associated with the given country. These IDs may be passed to
632: * <code>get()</code> to construct the corresponding TimeZone
633: * object.
634: * @param country a two-letter ISO 3166 country code, or <code>null</code>
635: * to return zones not associated with any country
636: * @return an array of IDs for system TimeZones in the given
637: * country. If there are none, return a zero-length array.
638: * @stable ICU 2.0
639: */
640: public static String[] getAvailableIDs(String country) {
641: return ZoneMeta.getAvailableIDs(country);
642: }
643:
644: /**
645: * Return a new String array containing all system TimeZone IDs.
646: * These IDs (and only these IDs) may be passed to
647: * <code>get()</code> to construct the corresponding TimeZone
648: * object.
649: * @return an array of all system TimeZone IDs
650: * @stable ICU 2.0
651: */
652: public static String[] getAvailableIDs() {
653: return ZoneMeta.getAvailableIDs();
654: }
655:
656: /**
657: * Returns the number of IDs in the equivalency group that
658: * includes the given ID. An equivalency group contains zones
659: * that have the same GMT offset and rules.
660: *
661: * <p>The returned count includes the given ID; it is always >= 1
662: * for valid IDs. The given ID must be a system time zone. If it
663: * is not, returns zero.
664: * @param id a system time zone ID
665: * @return the number of zones in the equivalency group containing
666: * 'id', or zero if 'id' is not a valid system ID
667: * @see #getEquivalentID
668: * @stable ICU 2.0
669: */
670: public static int countEquivalentIDs(String id) {
671: return ZoneMeta.countEquivalentIDs(id);
672: }
673:
674: /**
675: * Returns an ID in the equivalency group that
676: * includes the given ID. An equivalency group contains zones
677: * that have the same GMT offset and rules.
678: *
679: * <p>The given index must be in the range 0..n-1, where n is the
680: * value returned by <code>countEquivalentIDs(id)</code>. For
681: * some value of 'index', the returned value will be equal to the
682: * given id. If the given id is not a valid system time zone, or
683: * if 'index' is out of range, then returns an empty string.
684: * @param id a system time zone ID
685: * @param index a value from 0 to n-1, where n is the value
686: * returned by <code>countEquivalentIDs(id)</code>
687: * @return the ID of the index-th zone in the equivalency group
688: * containing 'id', or an empty string if 'id' is not a valid
689: * system ID or 'index' is out of range
690: * @see #countEquivalentIDs
691: * @stable ICU 2.0
692: */
693: public static String getEquivalentID(String id, int index) {
694: return ZoneMeta.getEquivalentID(id, index);
695: }
696:
697: /**
698: * Gets the default <code>TimeZone</code> for this host.
699: * The source of the default <code>TimeZone</code>
700: * may vary with implementation.
701: * @return a default <code>TimeZone</code>.
702: * @stable ICU 2.0
703: */
704: public static synchronized TimeZone getDefault() {
705: if (defaultZone == null) {
706: java.util.TimeZone temp = java.util.TimeZone.getDefault();
707: defaultZone = getTimeZone(temp.getID());
708: }
709: return (TimeZone) defaultZone.clone();
710: }
711:
712: /**
713: * Sets the <code>TimeZone</code> that is
714: * returned by the <code>getDefault</code> method. If <code>zone</code>
715: * is null, reset the default to the value it had originally when the
716: * VM first started.
717: * @param tz the new default time zone
718: * @stable ICU 2.0
719: */
720: public static synchronized void setDefault(TimeZone tz) {
721:
722: defaultZone = tz;
723: // Keep java.util.TimeZone default in sync so java.util.Date
724: // can interoperate with com.ibm.icu.util classes.
725: java.util.TimeZone jdkZone = null;
726: if (tz != null) {
727: jdkZone = TimeZoneAdapter.wrap(tz);
728: }
729: java.util.TimeZone.setDefault(jdkZone);
730: }
731:
732: /**
733: * Returns true if this zone has the same rule and offset as another zone.
734: * That is, if this zone differs only in ID, if at all. Returns false
735: * if the other zone is null.
736: * @param other the <code>TimeZone</code> object to be compared with
737: * @return true if the other zone is not null and is the same as this one,
738: * with the possible exception of the ID
739: * @stable ICU 2.0
740: */
741: public boolean hasSameRules(TimeZone other) {
742: return other != null && getRawOffset() == other.getRawOffset()
743: && useDaylightTime() == other.useDaylightTime();
744: }
745:
746: /**
747: * Overrides Cloneable
748: * @stable ICU 2.0
749: */
750: public Object clone() {
751: try {
752: TimeZone other = (TimeZone) super .clone();
753: other.ID = ID;
754: return other;
755: } catch (CloneNotSupportedException e) {
756: throw new IllegalStateException();
757: }
758: }
759:
760: /**
761: * Return true if obj is a TimeZone with the same class and ID as this.
762: * @return true if obj is a TimeZone with the same class and ID as this
763: * @param obj the object to compare against
764: * @draft ICU 3.4.2
765: * @provisional This API might change or be removed in a future release.
766: */
767: public boolean equals(Object obj) {
768: if (this == obj)
769: return true;
770: if (obj == null || getClass() != obj.getClass())
771: return false;
772: return (ID.equals(((TimeZone) obj).ID));
773: }
774:
775: /**
776: * Return the hash code.
777: * @return the hash code
778: * @draft ICU 3.4.2
779: * @provisional This API might change or be removed in a future release.
780: */
781: public int hashCode() {
782: return ID.hashCode();
783: }
784:
785: // =======================privates===============================
786:
787: /**
788: * The string identifier of this <code>TimeZone</code>. This is a
789: * programmatic identifier used internally to look up <code>TimeZone</code>
790: * objects from the system table and also to map them to their localized
791: * display names. <code>ID</code> values are unique in the system
792: * table but may not be for dynamically created zones.
793: * @serial
794: */
795: private String ID;
796:
797: /**
798: * The default time zone, or null if not set.
799: */
800: private static TimeZone defaultZone = null;
801:
802: }
803:
804: //eof
|