1/* 2 ******************************************************************************* 3 * Copyright (C) 2008-2014, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7package com.ibm.icu.impl.icuadapter; 8 9import java.lang.reflect.InvocationTargetException; 10import java.lang.reflect.Method; 11import java.util.Calendar; 12import java.util.Date; 13import java.util.GregorianCalendar; 14import java.util.Locale; 15import java.util.TimeZone; 16 17import com.ibm.icu.impl.Grego; 18import com.ibm.icu.impl.jdkadapter.TimeZoneICU; 19import com.ibm.icu.util.ULocale; 20 21/** 22 * TimeZoneJDK is an adapter class which wraps java.util.TimeZone and 23 * implements ICU4J TimeZone APIs. 24 */ 25public class TimeZoneJDK extends com.ibm.icu.util.TimeZone { 26 27 private static final long serialVersionUID = -1137052823551791933L; 28 29 private TimeZone fJdkTz; 30 private transient Calendar fJdkCal; 31 private static Method mObservesDaylightTime; 32 33 static { 34 try { 35 mObservesDaylightTime = TimeZone.class.getMethod("observesDaylightTime", (Class[]) null); 36 } catch (NoSuchMethodException e) { 37 // Java 6 or older 38 } catch (SecurityException e) { 39 // not visible 40 } 41 } 42 43 private TimeZoneJDK(TimeZone jdkTz) { 44 fJdkTz = (TimeZone)jdkTz.clone(); 45 } 46 47 public static com.ibm.icu.util.TimeZone wrap(TimeZone jdkTz) { 48 if (jdkTz instanceof TimeZoneICU) { 49 return ((TimeZoneICU)jdkTz).unwrap(); 50 } 51 return new TimeZoneJDK(jdkTz); 52 } 53 54 public TimeZone unwrap() { 55 return (TimeZone)fJdkTz.clone(); 56 } 57 58 @Override 59 public Object clone() { 60 if (isFrozen()) { 61 return this; 62 } 63 return cloneAsThawed(); 64 } 65 66 @Override 67 public boolean equals(Object obj) { 68 if (obj instanceof TimeZoneJDK) { 69 return (((TimeZoneJDK)obj).fJdkTz).equals(fJdkTz); 70 } 71 return false; 72 } 73 74 //public String getDisplayName() 75 //public String getDisplayName(boolean daylight, int style) 76 //public String getDisplayName(Locale locale) 77 //public String getDisplayName(ULocale locale) 78 @Override 79 public String getDisplayName(boolean daylight, int style, Locale locale) { 80 return fJdkTz.getDisplayName(daylight, style, locale); 81 } 82 83 @Override 84 public String getDisplayName(boolean daylight, int style, ULocale locale) { 85 return fJdkTz.getDisplayName(daylight, style, locale.toLocale()); 86 } 87 88 @Override 89 public int getDSTSavings() { 90 return fJdkTz.getDSTSavings(); 91 } 92 93 @Override 94 public String getID() { 95 return fJdkTz.getID(); 96 } 97 98 @Override 99 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) { 100 return fJdkTz.getOffset(era, year, month, day, dayOfWeek, milliseconds); 101 } 102 103 @Override 104 public int getOffset(long date) { 105 return fJdkTz.getOffset(date); 106 } 107 108 @Override 109 public void getOffset(long date, boolean local, int[] offsets) { 110 synchronized(this) { 111 if (fJdkCal == null) { 112 fJdkCal = new GregorianCalendar(fJdkTz); 113 } 114 if (local) { 115 int fields[] = new int[6]; 116 Grego.timeToFields(date, fields); 117 int hour, min, sec, mil; 118 int tmp = fields[5]; 119 mil = tmp % 1000; 120 tmp /= 1000; 121 sec = tmp % 60; 122 tmp /= 60; 123 min = tmp % 60; 124 hour = tmp / 60; 125 fJdkCal.clear(); 126 fJdkCal.set(fields[0], fields[1], fields[2], hour, min, sec); 127 fJdkCal.set(java.util.Calendar.MILLISECOND, mil); 128 129 int doy1, hour1, min1, sec1, mil1; 130 doy1 = fJdkCal.get(java.util.Calendar.DAY_OF_YEAR); 131 hour1 = fJdkCal.get(java.util.Calendar.HOUR_OF_DAY); 132 min1 = fJdkCal.get(java.util.Calendar.MINUTE); 133 sec1 = fJdkCal.get(java.util.Calendar.SECOND); 134 mil1 = fJdkCal.get(java.util.Calendar.MILLISECOND); 135 136 if (fields[4] != doy1 || hour != hour1 || min != min1 || sec != sec1 || mil != mil1) { 137 // Calendar field(s) were changed due to the adjustment for non-existing time 138 // Note: This code does not support non-existing local time at year boundary properly. 139 // But, it should work fine for real timezones. 140 int dayDelta = Math.abs(doy1 - fields[4]) > 1 ? 1 : doy1 - fields[4]; 141 int delta = ((((dayDelta * 24) + hour1 - hour) * 60 + min1 - min) * 60 + sec1 - sec) * 1000 + mil1 - mil; 142 143 // In this case, we use the offsets before the transition 144 fJdkCal.setTimeInMillis(fJdkCal.getTimeInMillis() - delta - 1); 145 } 146 } else { 147 fJdkCal.setTimeInMillis(date); 148 } 149 offsets[0] = fJdkCal.get(java.util.Calendar.ZONE_OFFSET); 150 offsets[1] = fJdkCal.get(java.util.Calendar.DST_OFFSET); 151 } 152 } 153 154 @Override 155 public int getRawOffset() { 156 return fJdkTz.getRawOffset(); 157 } 158 159 @Override 160 public int hashCode() { 161 return fJdkTz.hashCode(); 162 } 163 164 @Override 165 public boolean hasSameRules(com.ibm.icu.util.TimeZone other) { 166 return other.hasSameRules(TimeZoneJDK.wrap(fJdkTz)); 167 } 168 169 @Override 170 public boolean inDaylightTime(Date date) { 171 return fJdkTz.inDaylightTime(date); 172 } 173 174 @Override 175 public void setID(String ID) { 176 if (isFrozen()) { 177 throw new UnsupportedOperationException("Attempt to modify a frozen TimeZoneJDK instance."); 178 } 179 fJdkTz.setID(ID); 180 } 181 182 @Override 183 public void setRawOffset(int offsetMillis) { 184 if (isFrozen()) { 185 throw new UnsupportedOperationException("Attempt to modify a frozen TimeZoneJDK instance."); 186 } 187 fJdkTz.setRawOffset(offsetMillis); 188 } 189 190 @Override 191 public boolean useDaylightTime() { 192 return fJdkTz.useDaylightTime(); 193 } 194 195 @Override 196 public boolean observesDaylightTime() { 197 if (mObservesDaylightTime != null) { 198 // Java 7+ 199 try { 200 return (Boolean)mObservesDaylightTime.invoke(fJdkTz, (Object[]) null); 201 } catch (IllegalAccessException e) { 202 } catch (IllegalArgumentException e) { 203 } catch (InvocationTargetException e) { 204 } 205 } 206 return super.observesDaylightTime(); 207 } 208 209 // Freezable stuffs 210 private volatile transient boolean fIsFrozen = false; 211 212 @Override 213 public boolean isFrozen() { 214 return fIsFrozen; 215 } 216 217 @Override 218 public com.ibm.icu.util.TimeZone freeze() { 219 fIsFrozen = true; 220 return this; 221 } 222 223 @Override 224 public com.ibm.icu.util.TimeZone cloneAsThawed() { 225 TimeZoneJDK tz = (TimeZoneJDK)super.cloneAsThawed(); 226 tz.fJdkTz = (TimeZone)fJdkTz.clone(); 227 tz.fJdkCal = null; // To be instantiated when necessary 228 tz.fIsFrozen = false; 229 return tz; 230 } 231 232} 233