2 *******************************************************************************
3 * Copyright (C) 2008-2012, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.io.IOException;
10 import java.io.ObjectInputStream;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.util.Date;
14 import java.util.TreeSet;
16 import com.ibm.icu.util.TimeZone;
19 * JavaTimeZone inherits com.ibm.icu.util.TimeZone and wraps java.util.TimeZone.
20 * We used to have JDKTimeZone which wrapped Java TimeZone and used it as primary
21 * TimeZone implementation until ICU4J 3.4.1. This class works exactly like
22 * JDKTimeZone and allows ICU users who use ICU4J and JDK date/time/calendar
23 * services in mix to maintain only JDK timezone rules.
25 * This TimeZone subclass is returned by the TimeZone factory method getTimeZone(String)
26 * when the default timezone type in TimeZone class is TimeZone.TIMEZONE_JDK.
28 public class JavaTimeZone extends TimeZone {
30 private static final long serialVersionUID = 6977448185543929364L;
32 private static final TreeSet<String> AVAILABLESET;
34 private java.util.TimeZone javatz;
35 private transient java.util.Calendar javacal;
36 private static Method mObservesDaylightTime;
39 AVAILABLESET = new TreeSet<String>();
40 String[] availableIds = java.util.TimeZone.getAvailableIDs();
41 for (int i = 0; i < availableIds.length; i++) {
42 AVAILABLESET.add(availableIds[i]);
46 mObservesDaylightTime = java.util.TimeZone.class.getMethod("observesDaylightTime", (Class[]) null);
47 } catch (NoSuchMethodException e) {
49 } catch (SecurityException e) {
55 * Constructs a JavaTimeZone with the default Java TimeZone
57 public JavaTimeZone() {
58 this(java.util.TimeZone.getDefault(), null);
62 * Constructs a JavaTimeZone with the specified Java TimeZone and ID.
63 * @param jtz the Java TimeZone
64 * @param id the ID of the zone. if null, the zone ID is initialized
65 * by the given Java TimeZone's ID.
67 public JavaTimeZone(java.util.TimeZone jtz, String id) {
73 javacal = new java.util.GregorianCalendar(javatz);
77 * Creates an instance of JavaTimeZone with the given timezone ID.
78 * @param id A timezone ID, either a system ID or a custom ID.
79 * @return An instance of JavaTimeZone for the given ID, or null
80 * when the ID cannot be understood.
82 public static JavaTimeZone createTimeZone(String id) {
83 java.util.TimeZone jtz = null;
85 if (AVAILABLESET.contains(id)) {
86 jtz = java.util.TimeZone.getTimeZone(id);
90 // Use ICU's canonical ID mapping
91 boolean[] isSystemID = new boolean[1];
92 String canonicalID = TimeZone.getCanonicalID(id, isSystemID);
93 if (isSystemID[0] && AVAILABLESET.contains(canonicalID)) {
94 jtz = java.util.TimeZone.getTimeZone(canonicalID);
102 return new JavaTimeZone(jtz, id);
106 * @see com.ibm.icu.util.TimeZone#getOffset(int, int, int, int, int, int)
108 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
109 return javatz.getOffset(era, year, month, day, dayOfWeek, milliseconds);
113 * @see com.ibm.icu.util.TimeZone#getOffset(long, boolean, int[])
115 public void getOffset(long date, boolean local, int[] offsets) {
116 synchronized (javacal) {
118 int fields[] = new int[6];
119 Grego.timeToFields(date, fields);
120 int hour, min, sec, mil;
129 javacal.set(fields[0], fields[1], fields[2], hour, min, sec);
130 javacal.set(java.util.Calendar.MILLISECOND, mil);
132 int doy1, hour1, min1, sec1, mil1;
133 doy1 = javacal.get(java.util.Calendar.DAY_OF_YEAR);
134 hour1 = javacal.get(java.util.Calendar.HOUR_OF_DAY);
135 min1 = javacal.get(java.util.Calendar.MINUTE);
136 sec1 = javacal.get(java.util.Calendar.SECOND);
137 mil1 = javacal.get(java.util.Calendar.MILLISECOND);
139 if (fields[4] != doy1 || hour != hour1 || min != min1 || sec != sec1 || mil != mil1) {
140 // Calendar field(s) were changed due to the adjustment for non-existing time
141 // Note: This code does not support non-existing local time at year boundary properly.
142 // But, it should work fine for real timezones.
143 int dayDelta = Math.abs(doy1 - fields[4]) > 1 ? 1 : doy1 - fields[4];
144 int delta = ((((dayDelta * 24) + hour1 - hour) * 60 + min1 - min) * 60 + sec1 - sec) * 1000 + mil1 - mil;
146 // In this case, we use the offsets before the transition
147 javacal.setTimeInMillis(javacal.getTimeInMillis() - delta - 1);
150 javacal.setTimeInMillis(date);
152 offsets[0] = javacal.get(java.util.Calendar.ZONE_OFFSET);
153 offsets[1] = javacal.get(java.util.Calendar.DST_OFFSET);
158 * @see com.ibm.icu.util.TimeZone#getRawOffset()
160 public int getRawOffset() {
161 return javatz.getRawOffset();
165 * @see com.ibm.icu.util.TimeZone#inDaylightTime(java.util.Date)
167 public boolean inDaylightTime(Date date) {
168 return javatz.inDaylightTime(date);
172 * @see com.ibm.icu.util.TimeZone#setRawOffset(int)
174 public void setRawOffset(int offsetMillis) {
176 throw new UnsupportedOperationException("Attempt to modify a frozen JavaTimeZone instance.");
178 javatz.setRawOffset(offsetMillis);
182 * @see com.ibm.icu.util.TimeZone#useDaylightTime()
184 public boolean useDaylightTime() {
185 return javatz.useDaylightTime();
189 * @see com.ibm.icu.util.TimeZone#observesDaylightTime()
191 public boolean observesDaylightTime() {
192 if (mObservesDaylightTime != null) {
195 return (Boolean)mObservesDaylightTime.invoke(javatz, (Object[]) null);
196 } catch (IllegalAccessException e) {
197 } catch (IllegalArgumentException e) {
198 } catch (InvocationTargetException e) {
201 return super.observesDaylightTime();
205 * @see com.ibm.icu.util.TimeZone#getDSTSavings()
207 public int getDSTSavings() {
208 return javatz.getDSTSavings();
211 public java.util.TimeZone unwrap() {
216 * @see com.ibm.icu.util.TimeZone#clone()
218 public Object clone() {
222 return cloneAsThawed();
226 * @see com.ibm.icu.util.TimeZone#hashCode()
228 public int hashCode() {
229 return super.hashCode() + javatz.hashCode();
232 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
233 s.defaultReadObject();
234 javacal = new java.util.GregorianCalendar(javatz);
238 private transient boolean isFrozen = false;
241 * @see com.ibm.icu.util.TimeZone#isFrozen()
243 public boolean isFrozen() {
248 * @see com.ibm.icu.util.TimeZone#freeze()
250 public TimeZone freeze() {
256 * @see com.ibm.icu.util.TimeZone#cloneAsThawed()
258 public TimeZone cloneAsThawed() {
259 JavaTimeZone tz = (JavaTimeZone)super.cloneAsThawed();
260 tz.javatz = (java.util.TimeZone)javatz.clone();
261 tz.javacal = (java.util.GregorianCalendar)javacal.clone();