From 66350869c3043fb626ddaa4ae0a39369408c5e84 Mon Sep 17 00:00:00 2001 From: Yoshito Umaoka Date: Tue, 17 Jan 2012 04:32:31 +0000 Subject: [PATCH] ICU-8998 Merging TimeZone#observesDaylightTime changes from my work branch to trunk. X-SVN-Rev: 31213 --- .../src/com/ibm/icu/impl/JavaTimeZone.java | 41 ++++--- .../src/com/ibm/icu/impl/OlsonTimeZone.java | 31 +++++- .../com/ibm/icu/util/RuleBasedTimeZone.java | 47 +++++++- .../src/com/ibm/icu/util/SimpleTimeZone.java | 11 +- .../core/src/com/ibm/icu/util/TimeZone.java | 31 +++++- .../core/src/com/ibm/icu/util/VTimeZone.java | 12 ++- .../ibm/icu/impl/icuadapter/TimeZoneJDK.java | 29 ++++- .../icu/dev/test/timezone/TimeZoneTest.java | 101 +++++++++++++++++- 8 files changed, 284 insertions(+), 19 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java index fe87bfe4f87..3eb5b0741a1 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/JavaTimeZone.java @@ -1,6 +1,6 @@ /* ******************************************************************************* - * Copyright (C) 2008-2011, International Business Machines Corporation and * + * Copyright (C) 2008-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -8,6 +8,8 @@ package com.ibm.icu.impl; import java.io.IOException; import java.io.ObjectInputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Date; import java.util.TreeSet; @@ -31,6 +33,7 @@ public class JavaTimeZone extends TimeZone { private java.util.TimeZone javatz; private transient java.util.Calendar javacal; + private static Method mObservesDaylightTime; static { AVAILABLESET = new TreeSet(); @@ -38,6 +41,14 @@ public class JavaTimeZone extends TimeZone { for (int i = 0; i < availableIds.length; i++) { AVAILABLESET.add(availableIds[i]); } + + try { + mObservesDaylightTime = java.util.TimeZone.class.getMethod("observesDaylightTime", (Class[]) null); + } catch (NoSuchMethodException e) { + // Java 6 or older + } catch (SecurityException e) { + // not visible + } } /** @@ -174,21 +185,27 @@ public class JavaTimeZone extends TimeZone { return javatz.useDaylightTime(); } + /* (non-Javadoc) + * @see com.ibm.icu.util.TimeZone#observesDaylightTime() + */ + public boolean observesDaylightTime() { + if (mObservesDaylightTime != null) { + // Java 7+ + try { + return (Boolean)mObservesDaylightTime.invoke(javatz, (Object[]) null); + } catch (IllegalAccessException e) { + } catch (IllegalArgumentException e) { + } catch (InvocationTargetException e) { + } + } + return super.observesDaylightTime(); + } + /* (non-Javadoc) * @see com.ibm.icu.util.TimeZone#getDSTSavings() */ public int getDSTSavings() { - int dstSavings = super.getDSTSavings(); - try { - // hack so test compiles and runs in both JDK 1.3 and JDK 1.4+ - final Object[] args = new Object[0]; - final Class[] argtypes = new Class[0]; - java.lang.reflect.Method m = javatz.getClass().getMethod("getDSTSavings", argtypes); - dstSavings = ((Integer) m.invoke(javatz, args)).intValue(); - } catch (Exception e) { - // just use the result returned by super.getDSTSavings() - } - return dstSavings; + return javatz.getDSTSavings(); } public java.util.TimeZone unwrap() { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java index 255d6126ac7..d3aca2160e6 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java @@ -1,6 +1,6 @@ /* ******************************************************************************* - * Copyright (C) 2005-2011, International Business Machines Corporation and * + * Copyright (C) 2005-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -334,6 +334,35 @@ public class OlsonTimeZone extends BasicTimeZone { return false; } + /* (non-Javadoc) + * @see com.ibm.icu.util.TimeZone#observesDaylightTime() + */ + @Override + public boolean observesDaylightTime() { + long current = System.currentTimeMillis(); + + if (finalZone != null && current >= finalStartMillis) { + if (finalZone.useDaylightTime()) { + return true; + } + } + + // Return TRUE if DST is observed at any future time + long currentSec = Grego.floorDivide(current, Grego.MILLIS_PER_SECOND); + int trsIdx = transitionCount - 1; + if (dstOffsetAt(trsIdx) != 0) { + return true; + } + while (trsIdx >= 0) { + if (transitionTimes64[trsIdx] <= currentSec) { + break; + } + if (dstOffsetAt(trsIdx - 1) != 0) { + return true; + } + } + return false; + } /** * TimeZone API * Returns the amount of time to be added to local standard time diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/RuleBasedTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/util/RuleBasedTimeZone.java index ccbe7a62baa..750a407cd11 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/RuleBasedTimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/RuleBasedTimeZone.java @@ -1,6 +1,6 @@ /* ******************************************************************************* - * Copyright (C) 2007-2011, International Business Machines Corporation and * + * Copyright (C) 2007-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -189,6 +189,51 @@ public class RuleBasedTimeZone extends BasicTimeZone { return false; } + /** + * {@inheritDoc} + * @draft ICU 49 + * @provisional This API might change or be removed in a future release. + */ + @Override + public boolean observesDaylightTime() { + long time = System.currentTimeMillis(); + + // Check if daylight saving time is observed now. + int[] offsets = new int[2]; + getOffset(time, false, offsets); + if (offsets[1] != 0) { + return true; + } + + // If DST is not used now, check if DST is used after each transition. + BitSet checkFinals = finalRules == null ? null : new BitSet(finalRules.length); + while (true) { + TimeZoneTransition tt = getNextTransition(time, false); + if (tt == null) { + // no more transition + break; + } + TimeZoneRule toRule = tt.getTo(); + if (toRule.getDSTSavings() != 0) { + return true; + } + if (checkFinals != null) { + // final rules exist - check if we saw all of them + for (int i = 0; i < finalRules.length; i++) { + if (finalRules[i].equals(toRule)) { + checkFinals.set(i); + } + } + if (checkFinals.cardinality() == finalRules.length) { + // already saw all final rules + break; + } + } + time = tt.getTime(); + } + return false; + } + /** * {@inheritDoc} * diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/SimpleTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/util/SimpleTimeZone.java index c2114a7f520..4384d51ff32 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/SimpleTimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/SimpleTimeZone.java @@ -1,5 +1,5 @@ /* -* Copyright (C) 1996-2011, International Business Machines +* Copyright (C) 1996-2012, International Business Machines * Corporation and others. All Rights Reserved. */ @@ -956,6 +956,15 @@ public class SimpleTimeZone extends BasicTimeZone { return useDaylight; } + /** + * {@inheritDoc} + * @draft ICU 49 + * @provisional This API might change or be removed in a future release. + */ + public boolean observesDaylightTime() { + return useDaylight; + } + /** * Overrides TimeZone * Queries if the give date is in Daylight Saving Time. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/TimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/util/TimeZone.java index 2567b30414e..a3d9cee230d 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/TimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/TimeZone.java @@ -1,7 +1,7 @@ /* * @(#)TimeZone.java 1.51 00/01/19 * - * Copyright (C) 1996-2011, International Business Machines + * Copyright (C) 1996-2012, International Business Machines * Corporation and others. All Rights Reserved. */ @@ -651,6 +651,35 @@ abstract public class TimeZone implements Serializable, Cloneable, FreezableThe default implementation in this class returns true if {@link #useDaylightTime()} + * or {@link #inDaylightTime(Date) inDaylightTime(new Date())} returns true. + *

+ * Note: This method was added for JDK compatibility support. + * The JDK's useDaylightTime() only checks the last known rule(s), therefore + * it may return false even the zone observes daylight saving time currently. JDK added + * observesDaylightTime() to resolve the issue. In ICU, {@link #useDaylightTime()} + * works differently. The ICU implementation checks if the zone uses daylight saving time + * in the current calendar year. Therefore, it will never return false if + * daylight saving time is currently used. + *

+ * ICU's TimeZone subclass implementations override this method to support the same behavior + * with JDK's observesDaylightSavingTime(). Unlike {@link #useDaylightTime()}, + * the implementation does not take past daylight saving time into account, so + * that this method may return false even when {@link #useDaylightTime()} returns + * true. + * + * @return true if this time zone is in daylight saving time or will observe + * daylight saving time at any future time. + * @see #useDaylightTime + * @stable ICU 49 + */ + public boolean observesDaylightTime() { + return useDaylightTime() || inDaylightTime(new Date()); + } + /** * Queries if the given date is in daylight savings time in * this time zone. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/VTimeZone.java b/icu4j/main/classes/core/src/com/ibm/icu/util/VTimeZone.java index 6d0b79d2887..a38b292383d 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/VTimeZone.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/VTimeZone.java @@ -1,6 +1,6 @@ /* ******************************************************************************* - * Copyright (C) 2007-2011, International Business Machines Corporation and * + * Copyright (C) 2007-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -140,6 +140,16 @@ public class VTimeZone extends BasicTimeZone { return tz.useDaylightTime(); } + /** + * {@inheritDoc} + * @draft ICU 49 + * @provisional This API might change or be removed in a future release. + */ + @Override + public boolean observesDaylightTime() { + return tz.observesDaylightTime(); + } + /** * {@inheritDoc} * @stable ICU 3.8 diff --git a/icu4j/main/classes/localespi/src/com/ibm/icu/impl/icuadapter/TimeZoneJDK.java b/icu4j/main/classes/localespi/src/com/ibm/icu/impl/icuadapter/TimeZoneJDK.java index 24025a0c11c..2abdde7f39e 100644 --- a/icu4j/main/classes/localespi/src/com/ibm/icu/impl/icuadapter/TimeZoneJDK.java +++ b/icu4j/main/classes/localespi/src/com/ibm/icu/impl/icuadapter/TimeZoneJDK.java @@ -1,11 +1,13 @@ /* ******************************************************************************* - * Copyright (C) 2008, International Business Machines Corporation and * + * Copyright (C) 2008-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.impl.icuadapter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -26,6 +28,17 @@ public class TimeZoneJDK extends com.ibm.icu.util.TimeZone { private TimeZone fJdkTz; private transient Calendar fJdkCal; + private static Method mObservesDaylightTime; + + static { + try { + mObservesDaylightTime = TimeZone.class.getMethod("observesDaylightTime", (Class[]) null); + } catch (NoSuchMethodException e) { + // Java 6 or older + } catch (SecurityException e) { + // not visible + } + } private TimeZoneJDK(TimeZone jdkTz) { fJdkTz = jdkTz; @@ -171,4 +184,18 @@ public class TimeZoneJDK extends com.ibm.icu.util.TimeZone { public boolean useDaylightTime() { return fJdkTz.useDaylightTime(); } + + @Override + public boolean observesDaylightTime() { + if (mObservesDaylightTime != null) { + // Java 7+ + try { + return (Boolean)mObservesDaylightTime.invoke(fJdkTz, (Object[]) null); + } catch (IllegalAccessException e) { + } catch (IllegalArgumentException e) { + } catch (InvocationTargetException e) { + } + } + return super.observesDaylightTime(); + } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java index 01ca6e38ab7..4431ad9f1ba 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java @@ -1,6 +1,6 @@ /** ******************************************************************************* - * Copyright (C) 2000-2011, International Business Machines Corporation and * + * Copyright (C) 2000-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -22,6 +22,7 @@ import java.util.Set; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.impl.ICUResourceBundle; import com.ibm.icu.text.SimpleDateFormat; +import com.ibm.icu.util.BasicTimeZone; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.DateTimeRule; import com.ibm.icu.util.GregorianCalendar; @@ -31,6 +32,8 @@ import com.ibm.icu.util.SimpleTimeZone; import com.ibm.icu.util.TimeArrayTimeZoneRule; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.TimeZone.SystemTimeZoneType; +import com.ibm.icu.util.TimeZoneRule; +import com.ibm.icu.util.TimeZoneTransition; import com.ibm.icu.util.ULocale; import com.ibm.icu.util.UResourceBundle; import com.ibm.icu.util.VTimeZone; @@ -2083,6 +2086,102 @@ public class TimeZoneTest extends TestFmwk } } } + + public void TestObservesDaylightTime() { + boolean observesDaylight; + long current = System.currentTimeMillis(); + + String[] tzids = TimeZone.getAvailableIDs(); + for (String tzid : tzids) { + // OlsonTimeZone + TimeZone tz = TimeZone.getTimeZone(tzid, TimeZone.TIMEZONE_ICU); + observesDaylight = tz.observesDaylightTime(); + if (observesDaylight != isDaylightTimeAvailable(tz, current)) { + errln("Fail: [OlsonTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid); + } + + // RuleBasedTimeZone + RuleBasedTimeZone rbtz = createRBTZ((BasicTimeZone)tz, current); + boolean observesDaylightRBTZ = rbtz.observesDaylightTime(); + if (observesDaylightRBTZ != isDaylightTimeAvailable(rbtz, current)) { + errln("Fail: [RuleBasedTimeZone] observesDaylightTime() returned " + observesDaylightRBTZ + " for " + rbtz.getID()); + } else if (observesDaylight != observesDaylightRBTZ) { + errln("Fail: RuleBasedTimeZone " + rbtz.getID() + " returns " + observesDaylightRBTZ + ", but different from match OlsonTimeZone"); + } + + // JavaTimeZone + tz = TimeZone.getTimeZone(tzid, TimeZone.TIMEZONE_JDK); + observesDaylight = tz.observesDaylightTime(); + if (observesDaylight != isDaylightTimeAvailable(tz, current)) { + errln("Fail: [JavaTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid); + } + + // VTimeZone + tz = VTimeZone.getTimeZone(tzid); + observesDaylight = tz.observesDaylightTime(); + if (observesDaylight != isDaylightTimeAvailable(tz, current)) { + errln("Fail: [VTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid); + } + } + + // SimpleTimeZone + SimpleTimeZone[] stzs = { + new SimpleTimeZone(0, "STZ0"), + new SimpleTimeZone(-5*60*60*1000, "STZ-5D", Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*60*60*1000), + }; + for (SimpleTimeZone stz : stzs) { + observesDaylight = stz.observesDaylightTime(); + if (observesDaylight != isDaylightTimeAvailable(stz, current)) { + errln("Fail: [SimpleTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + stz.getID()); + } + } + } + + private static boolean isDaylightTimeAvailable(TimeZone tz, long start) { + if (tz.inDaylightTime(new Date(start))) { + return true; + } + + long date; + if (tz instanceof BasicTimeZone) { + BasicTimeZone btz = (BasicTimeZone)tz; + // check future transitions, up to 100 + date = start; + for (int i = 0; i < 100; i++) { + TimeZoneTransition tzt = btz.getNextTransition(date, false); + if (tzt == null) { + // no more transitions + break; + } + if (tzt.getTo().getDSTSavings() != 0) { + return true; + } + date = tzt.getTime(); + } + } else { + // check future times by incrementing 30 days, up to 200 times (about 16 years) + final long inc = 30L * 24 * 60 * 60 * 1000; + int[] offsets = new int[2]; + date = start + inc; + for (int i = 0; i < 200; i++, date += inc) { + tz.getOffset(date, false, offsets); + if (offsets[1] != 0) { + return true; + } + } + } + return false; + } + + private static RuleBasedTimeZone createRBTZ(BasicTimeZone btz, long start) { + TimeZoneRule[] rules = btz.getTimeZoneRules(start); + RuleBasedTimeZone rbtz = new RuleBasedTimeZone("RBTZ:btz.getID()", (InitialTimeZoneRule)rules[0]); + for (int i = 1; i < rules.length; i++) { + rbtz.addTransitionRule(rules[i]); + } + return rbtz; + } } //eof -- 2.40.0