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