ICU-21490 Exposing getOffsetFromLocal for ICU4J

See #1609
This commit is contained in:
Yoshito Umaoka 2021-03-01 21:59:37 +00:00
parent e8dfea9bb6
commit 86d06eca8b
9 changed files with 251 additions and 163 deletions

View file

@ -280,11 +280,11 @@ public class OlsonTimeZone extends BasicTimeZone {
*/
@Override
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets) {
if (finalZone != null && date >= finalStartMillis) {
finalZone.getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
} else {
getHistoricalOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
getHistoricalOffset(date, true, getLocalOptionValue(nonExistingTimeOpt), getLocalOptionValue(duplicatedTimeOpt), offsets);
}
}
@ -1296,4 +1296,4 @@ public class OlsonTimeZone extends BasicTimeZone {
tz.isFrozen = false;
return tz;
}
}
}

View file

@ -37,6 +37,7 @@ import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.TimeZoneFormat.Style;
import com.ibm.icu.text.TimeZoneFormat.TimeType;
import com.ibm.icu.util.BasicTimeZone;
import com.ibm.icu.util.BasicTimeZone.LocalOption;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.Output;
@ -2710,10 +2711,10 @@ public class SimpleDateFormat extends DateFormat {
if (btz != null) {
if (tztype == TimeType.STANDARD) {
btz.getOffsetFromLocal(localMillis,
BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
LocalOption.STANDARD_FORMER, LocalOption.STANDARD_LATTER, offsets);
} else {
btz.getOffsetFromLocal(localMillis,
BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
LocalOption.DAYLIGHT_FORMER, LocalOption.DAYLIGHT_LATTER, offsets);
}
} else {
// No good way to resolve ambiguous time at transition,

View file

@ -36,7 +36,7 @@ public abstract class BasicTimeZone extends TimeZone {
/**
* {@icu} Returns the first time zone transition after the base time.
* <p>Example code:{@.jcite com.ibm.icu.samples.util.timezone.BasicTimeZoneExample:---getNextTransitionExample}
*
*
* @param base The base time.
* @param inclusive Whether the base time is inclusive or not.
*
@ -105,7 +105,7 @@ public abstract class BasicTimeZone extends TimeZone {
*
* @stable ICU 3.8
*/
public boolean hasEquivalentTransitions(TimeZone tz, long start, long end,
public boolean hasEquivalentTransitions(TimeZone tz, long start, long end,
boolean ignoreDstAmount) {
if (this == tz) {
return true;
@ -244,7 +244,7 @@ public abstract class BasicTimeZone extends TimeZone {
}
BitSet isProcessed = new BitSet(all.length);
List<TimeZoneRule> filteredRules = new LinkedList<TimeZoneRule>();
List<TimeZoneRule> filteredRules = new LinkedList<>();
// Create initial rule
TimeZoneRule initial = new InitialTimeZoneRule(tzt.getTo().getName(),
@ -434,16 +434,16 @@ public abstract class BasicTimeZone extends TimeZone {
// Check if the next next transition is either DST->STD or STD->DST
// and within roughly 1 year from the next transition
if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
|| (tr.getFrom().getDSTSavings() != 0
|| (tr.getFrom().getDSTSavings() != 0
&& tr.getTo().getDSTSavings() == 0))
&& nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
// Generate another DOW rule
dtfields = Grego.timeToFields(tr.getTime()
+ tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
+ tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
dtfields);
weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1],
weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1],
dtfields[2]);
dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3],
dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3],
dtfields[5], DateTimeRule.WALL_TIME);
secondRule = new AnnualTimeZoneRule(tr.getTo().getName(),
tr.getTo().getRawOffset(), tr.getTo().getDSTSavings(),
@ -472,22 +472,22 @@ public abstract class BasicTimeZone extends TimeZone {
&& tr.getTo().getDSTSavings() == 0)) {
// Generate another DOW rule
dtfields = Grego.timeToFields(tr.getTime()
+ tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
+ tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
dtfields);
weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1],
weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1],
dtfields[2]);
dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3],
dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3],
dtfields[5], DateTimeRule.WALL_TIME);
// second rule raw/dst offsets should match raw/dst offsets
// at the given time
secondRule = new AnnualTimeZoneRule(
tr.getTo().getName(), initialRaw, initialDst, dtr,
tr.getTo().getName(), initialRaw, initialDst, dtr,
annualRules[0].getStartYear() - 1, AnnualTimeZoneRule.MAX_YEAR);
// Check if this rule start after the first rule after the
// specified date
Date d = secondRule.getNextStart(date, tr.getFrom().getRawOffset(),
Date d = secondRule.getNextStart(date, tr.getFrom().getRawOffset(),
tr.getFrom().getDSTSavings(), false);
if (d.getTime() > nextTransitionTime) {
// We can use this rule as the second transition rule
@ -536,44 +536,123 @@ public abstract class BasicTimeZone extends TimeZone {
}
/**
* {@icu} The time type option for standard time used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* {@icu} Options used by {@link #getOffsetFromLocal(long, LocalOption, LocalOption, int[])}
* to specify how to interpret an input time when it does not exist, or when it is ambiguous,
* around a time zone transition.
*
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
public static enum LocalOption {
/**
* An input time is always interpreted as local time before
* a time zone transition.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
FORMER(0x04),
/**
* An input time is always interpreted as local time after
* a time zone transition.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
LATTER(0x0C),
/**
* An input time is interpreted as standard time when local
* time is switched to/from daylight saving time. When both
* sides of a time zone transition are standard time,
* or daylight saving time, the local time before the
* transition is used.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
STANDARD_FORMER(0x05),
/**
* An input time is interpreted as standard time when local
* time is switched to/from daylight saving time. When both
* sides of a time zone transition are standard time,
* or daylight saving time, the local time after the
* transition is used.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
STANDARD_LATTER(0x0D),
/**
* An input time is interpreted as daylight saving time when
* local time is switched to/from standard time. When both
* sides of a time zone transition are standard time,
* or daylight saving time, the local time before the
* transition is used.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
DAYLIGHT_FORMER(0x07),
/**
* An input time is interpreted as daylight saving time when
* local time is switched to/from standard time. When both
* sides of a time zone transition are standard time,
* or daylight saving time, the local time after the
* transition is used.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
DAYLIGHT_LATTER(0x0F);
private int flagVal;
LocalOption(int flagVal) {
this.flagVal = flagVal;
}
}
/**
* Get {@link LocalOption}'s internal flag value. This is used by ICU internal
* implementation only.
* @param locOpt A LocalOption
* @return LocalOption's internal flag value.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int LOCAL_STD = 0x01;
protected static int getLocalOptionValue(LocalOption locOpt) {
return locOpt.flagVal;
}
/**
* {@icu} The time type option for daylight saving time used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* The time type option for standard time used by internal implementation.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int LOCAL_DST = 0x03;
protected static final int LOCAL_STD = 0x01;
/**
* {@icu} The option designate former time to be used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* The time type option for daylight saving time used internally.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int LOCAL_FORMER = 0x04;
protected static final int LOCAL_DST = 0x03;
/**
* {@icu} The option designate latter time to be used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* The option designate former time used by internal implementation.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int LOCAL_LATTER = 0x0C;
protected static final int LOCAL_FORMER = 0x04;
/**
* {@icu} The bit mask for the time type option used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* The option designate latter time used by internal implementation.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
protected static final int LOCAL_LATTER = 0x0C;
/**
* The bit mask for the time type option used by internal implementation.
* @internal
* @deprecated This API is ICU internal only.
*/
@ -581,8 +660,7 @@ public abstract class BasicTimeZone extends TimeZone {
protected static final int STD_DST_MASK = 0x03;
/**
* {@icu} The bit mask for the former/latter option used by
* {@link #getOffsetFromLocal(long, int, int, int[])}
* The bit mask for the former/latter option used by internal implementation.
* @internal
* @deprecated This API is ICU internal only.
*/
@ -591,12 +669,11 @@ public abstract class BasicTimeZone extends TimeZone {
/**
* {@icu} Returns time zone offsets from local wall time.
* @internal
* @deprecated This API is ICU internal only.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
@Deprecated
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets) {
throw new IllegalStateException("Not implemented");
}

View file

@ -29,6 +29,7 @@ import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.DateTimePatternGenerator;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.BasicTimeZone.LocalOption;
import com.ibm.icu.util.ULocale.Category;
/**
@ -5725,8 +5726,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
int[] offsets = new int[2];
long wall = millis + millisInDay;
if (zone instanceof BasicTimeZone) {
int duplicatedTimeOpt = (repeatedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_FORMER : BasicTimeZone.LOCAL_LATTER;
int nonExistingTimeOpt = (skippedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_LATTER : BasicTimeZone.LOCAL_FORMER;
LocalOption nonExistingTimeOpt = (skippedWallTime == WALLTIME_FIRST) ? LocalOption.LATTER : LocalOption.FORMER;
LocalOption duplicatedTimeOpt = (repeatedWallTime == WALLTIME_FIRST) ? LocalOption.FORMER : LocalOption.LATTER;
((BasicTimeZone)zone).getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
} else {
// By default, TimeZone#getOffset behaves WALLTIME_LAST for both.
@ -5778,8 +5779,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
int[] offsets = new int[2];
long wall = millis + millisInDay;
if (zone instanceof BasicTimeZone) {
int duplicatedTimeOpt = (repeatedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_FORMER : BasicTimeZone.LOCAL_LATTER;
int nonExistingTimeOpt = (skippedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_LATTER : BasicTimeZone.LOCAL_FORMER;
LocalOption nonExistingTimeOpt = (skippedWallTime == WALLTIME_FIRST) ? LocalOption.LATTER : LocalOption.FORMER;
LocalOption duplicatedTimeOpt = (repeatedWallTime == WALLTIME_FIRST) ? LocalOption.FORMER : LocalOption.LATTER;
((BasicTimeZone)zone).getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
} else {
// By default, TimeZone#getOffset behaves WALLTIME_LAST for both.

View file

@ -17,9 +17,9 @@ import com.ibm.icu.impl.Grego;
/**
* <code>RuleBasedTimeZone</code> is a concrete subclass of <code>TimeZone</code> that allows users to define
* custom historic time transition rules.
*
*
* @see com.ibm.icu.util.TimeZoneRule
*
*
* @stable ICU 3.8
*/
public class RuleBasedTimeZone extends BasicTimeZone {
@ -36,10 +36,10 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* Constructs a <code>RuleBasedTimeZone</code> object with the ID and the
* <code>InitialTimeZoneRule</code>
*
*
* @param id The time zone ID.
* @param initialRule The initial time zone rule.
*
*
* @stable ICU 3.8
*/
public RuleBasedTimeZone(String id, InitialTimeZoneRule initialRule) {
@ -52,9 +52,9 @@ public class RuleBasedTimeZone extends BasicTimeZone {
* The <code>TimeZoneRule</code> must have start times, that is, the result
* of {@link com.ibm.icu.util.TimeZoneRule#isTransitionRule()} must be true.
* Otherwise, <code>IllegalArgumentException</code> is thrown.
*
*
* @param rule The <code>TimeZoneRule</code>.
*
*
* @stable ICU 3.8
*/
public void addTransitionRule(TimeZoneRule rule) {
@ -77,9 +77,9 @@ public class RuleBasedTimeZone extends BasicTimeZone {
throw new IllegalStateException("Too many final rules");
}
} else {
// If this is not a final rule, add it to the historic rule list
// If this is not a final rule, add it to the historic rule list
if (historicRules == null) {
historicRules = new ArrayList<TimeZoneRule>();
historicRules = new ArrayList<>();
}
historicRules.add(rule);
}
@ -90,7 +90,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -108,7 +108,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -118,19 +118,20 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
@Deprecated
@Override
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
getOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets) {
int nonExistingTimeOptVal = getLocalOptionValue(nonExistingTimeOpt);
int duplicatedTimeOptVal = getLocalOptionValue(duplicatedTimeOpt);
getOffset(date, true, nonExistingTimeOptVal, duplicatedTimeOptVal, offsets);
}
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -145,7 +146,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -157,7 +158,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -170,7 +171,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -238,7 +239,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -267,7 +268,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
if (finalRules[i] != null && otherRBTZ.finalRules[i] != null
&& finalRules[i].isEquivalentTo(otherRBTZ.finalRules[i])) {
continue;
}
return false;
}
@ -302,7 +303,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -321,7 +322,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
}
TimeZoneRule[] rules = new TimeZoneRule[size];
rules[0] = initialRule;
int idx = 1;
if (historicRules != null) {
for (; idx < historicRules.size() + 1; idx++) {
@ -339,7 +340,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -355,7 +356,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
if (tt > base || (inclusive && tt == base)) {
result = tzt;
} else {
int idx = historicTransitions.size() - 1;
int idx = historicTransitions.size() - 1;
tzt = historicTransitions.get(idx);
tt = tzt.getTime();
if (inclusive && tt == base) {
@ -411,7 +412,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
*
*
* @stable ICU 3.8
*/
@Override
@ -428,7 +429,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
} else if (tt >= base) {
return null;
} else {
int idx = historicTransitions.size() - 1;
int idx = historicTransitions.size() - 1;
tzt = historicTransitions.get(idx);
tt = tzt.getTime();
if (inclusive && tt == base) {
@ -459,7 +460,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
}
idx--;
}
result = tzt;
result = tzt;
}
}
// For now, this implementation ignore transitions with only zone name changes.
@ -472,7 +473,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
}
return result;
}
/**
* {@inheritDoc}
* @stable ICU 3.8
@ -582,7 +583,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
}
if (historicTransitions == null) {
historicTransitions = new ArrayList<TimeZoneTransition>();
historicTransitions = new ArrayList<>();
}
historicTransitions.add(new TimeZoneTransition(nextTransitionTime, curRule, nextRule));
lastTransitionTime = nextTransitionTime;
@ -591,7 +592,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
}
if (finalRules != null) {
if (historicTransitions == null) {
historicTransitions = new ArrayList<TimeZoneTransition>();
historicTransitions = new ArrayList<>();
}
// Append the first transition for each
Date d0 = finalRules[0].getNextStart(lastTransitionTime, curRule.getRawOffset(), curRule.getDSTSavings(), false);
@ -652,7 +653,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
offsets[0] = rule.getRawOffset();
offsets[1] = rule.getDSTSavings();
}
/*
* Find a time zone rule applicable to the specified time
*/
@ -764,6 +765,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public boolean isFrozen() {
return isFrozen;
}
@ -772,6 +774,7 @@ public class RuleBasedTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public TimeZone freeze() {
complete();
isFrozen = true;
@ -782,10 +785,11 @@ public class RuleBasedTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public TimeZone cloneAsThawed() {
RuleBasedTimeZone tz = (RuleBasedTimeZone)super.cloneAsThawed();
if (historicRules != null) {
tz.historicRules = new ArrayList<TimeZoneRule>(historicRules); // rules are immutable
tz.historicRules = new ArrayList<>(historicRules); // rules are immutable
}
if (finalRules != null) {
tz.finalRules = finalRules.clone();
@ -794,4 +798,3 @@ public class RuleBasedTimeZone extends BasicTimeZone {
return tz;
}
}

View file

@ -790,13 +790,15 @@ public class SimpleTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
@Override
@Deprecated
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets) {
int nonExistingTimeOptVal = getLocalOptionValue(nonExistingTimeOpt);
int duplicatedTimeOptVal = getLocalOptionValue(duplicatedTimeOpt);
offsets[0] = getRawOffset();
int fields[] = new int[6];
Grego.timeToFields(date, fields);
@ -808,16 +810,16 @@ public class SimpleTimeZone extends BasicTimeZone {
// Now, we need some adjustment
if (offsets[1] > 0) {
if ((nonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD
|| (nonExistingTimeOpt & STD_DST_MASK) != LOCAL_DST
&& (nonExistingTimeOpt & FORMER_LATTER_MASK) != LOCAL_LATTER) {
if ((nonExistingTimeOptVal & STD_DST_MASK) == LOCAL_STD
|| (nonExistingTimeOptVal & STD_DST_MASK) != LOCAL_DST
&& (nonExistingTimeOptVal & FORMER_LATTER_MASK) != LOCAL_LATTER) {
date -= getDSTSavings();
recalc = true;
}
} else {
if ((duplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST
|| (duplicatedTimeOpt & STD_DST_MASK) != LOCAL_STD
&& (duplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
if ((duplicatedTimeOptVal & STD_DST_MASK) == LOCAL_DST
|| (duplicatedTimeOptVal & STD_DST_MASK) != LOCAL_STD
&& (duplicatedTimeOptVal & FORMER_LATTER_MASK) == LOCAL_FORMER) {
date -= getDSTSavings();
recalc = true;
}
@ -1448,4 +1450,4 @@ public class SimpleTimeZone extends BasicTimeZone {
tz.isFrozen = false;
return tz;
}
}
}

View file

@ -28,11 +28,11 @@ import com.ibm.icu.impl.Grego;
* in RFC2445 VTIMEZONE format. Also, you can create a <code>VTimeZone</code> instance
* from RFC2445 VTIMEZONE data stream, which allows you to calculate time
* zone offset by the rules defined by the data.<br><br>
*
*
* Note: The consumer of this class reading or writing VTIMEZONE data is responsible to
* decode or encode Non-ASCII text. Methods reading/writing VTIMEZONE data in this class
* do nothing with MIME encoding.
*
*
* @stable ICU 3.8
*/
public class VTimeZone extends BasicTimeZone {
@ -41,11 +41,11 @@ public class VTimeZone extends BasicTimeZone {
/**
* Create a <code>VTimeZone</code> instance by the time zone ID.
*
*
* @param tzid The time zone ID, such as America/New_York
* @return A <code>VTimeZone</code> initialized by the time zone ID, or null
* when the ID is unknown.
*
*
* @stable ICU 3.8
*/
public static VTimeZone create(String tzid) {
@ -59,14 +59,14 @@ public class VTimeZone extends BasicTimeZone {
return vtz;
}
/**
* Create a <code>VTimeZone</code> instance by RFC2445 VTIMEZONE data.
*
*
* @param reader The Reader for VTIMEZONE data input stream
* @return A <code>VTimeZone</code> initialized by the VTIMEZONE data or
* null if failed to load the rule from the VTIMEZONE data.
*
*
* @stable ICU 3.8
*/
public static VTimeZone create(Reader reader) {
@ -98,13 +98,12 @@ public class VTimeZone extends BasicTimeZone {
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
* @draft ICU 69
* @provisional This API might change or be removed in a future release.
*/
@Deprecated
@Override
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets) {
tz.getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
}
@ -175,9 +174,9 @@ public class VTimeZone extends BasicTimeZone {
* Gets the RFC2445 TZURL property value. When a <code>VTimeZone</code> instance was created from
* VTIMEZONE data, the value is set by the TZURL property value in the data. Otherwise,
* the initial value is null.
*
*
* @return The RFC2445 TZURL property value
*
*
* @stable ICU 3.8
*/
public String getTZURL() {
@ -186,9 +185,9 @@ public class VTimeZone extends BasicTimeZone {
/**
* Sets the RFC2445 TZURL property value.
*
*
* @param url The TZURL property value.
*
*
* @stable ICU 3.8
*/
public void setTZURL(String url) {
@ -202,9 +201,9 @@ public class VTimeZone extends BasicTimeZone {
* Gets the RFC2445 LAST-MODIFIED property value. When a <code>VTimeZone</code> instance was created
* from VTIMEZONE data, the value is set by the LAST-MODIFIED property value in the data.
* Otherwise, the initial value is null.
*
*
* @return The Date represents the RFC2445 LAST-MODIFIED date.
*
*
* @stable ICU 3.8
*/
public Date getLastModified() {
@ -213,9 +212,9 @@ public class VTimeZone extends BasicTimeZone {
/**
* Sets the date used for RFC2445 LAST-MODIFIED property value.
*
*
* @param date The <code>Date</code> object represents the date for RFC2445 LAST-MODIFIED property value.
*
*
* @stable ICU 3.8
*/
public void setLastModified(Date date) {
@ -227,10 +226,10 @@ public class VTimeZone extends BasicTimeZone {
/**
* Writes RFC2445 VTIMEZONE data for this time zone
*
*
* @param writer A <code>Writer</code> used for the output
* @throws IOException If there were problems creating a buffered writer or writing to it.
*
*
* @stable ICU 3.8
*/
public void write(Writer writer) throws IOException {
@ -270,12 +269,12 @@ public class VTimeZone extends BasicTimeZone {
/**
* Writes RFC2445 VTIMEZONE data applicable for dates after
* the specified start time.
*
*
* @param writer The <code>Writer</code> used for the output
* @param start The start time
*
*
* @throws IOException If there were problems reading and writing to the writer.
*
*
* @stable ICU 3.8
*/
public void write(Writer writer, long start) throws IOException {
@ -290,7 +289,7 @@ public class VTimeZone extends BasicTimeZone {
String[] customProperties = null;
if (olsonzid != null && ICU_TZVERSION != null) {
customProperties = new String[1];
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
"/Partial@" + start + "]";
}
writeZone(writer, rbtz, customProperties);
@ -304,12 +303,12 @@ public class VTimeZone extends BasicTimeZone {
* the VTIMEZONE data which can be handled these implementations. The rules
* produced by this method can be used only for calculating time zone offset
* around the specified date.
*
*
* @param writer The <code>Writer</code> used for the output
* @param time The date
*
*
* @throws IOException If there were problems reading or writing to the writer.
*
*
* @stable ICU 3.8
*/
public void writeSimple(Writer writer, long time) throws IOException {
@ -324,7 +323,7 @@ public class VTimeZone extends BasicTimeZone {
String[] customProperties = null;
if (olsonzid != null && ICU_TZVERSION != null) {
customProperties = new String[1];
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
"/Simple@" + time + "]";
}
writeZone(writer, rbtz, customProperties);
@ -405,7 +404,7 @@ public class VTimeZone extends BasicTimeZone {
// Default DST savings
private static final int DEF_DSTSAVINGS = 60*60*1000; // 1 hour
// Default time start
private static final long DEF_TZSTARTTIME = 0;
@ -445,7 +444,7 @@ public class VTimeZone extends BasicTimeZone {
private static final String ICAL_BYDAY = "BYDAY";
private static final String ICAL_BYMONTHDAY = "BYMONTHDAY";
private static final String[] ICAL_DOW_NAMES =
private static final String[] ICAL_DOW_NAMES =
{"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
// Month length in regular year
@ -461,7 +460,7 @@ public class VTimeZone extends BasicTimeZone {
///CLOVER:ON
}
}
/* Hide the constructor */
private VTimeZone() {
}
@ -483,7 +482,7 @@ public class VTimeZone extends BasicTimeZone {
private boolean load(Reader reader) {
// Read VTIMEZONE block into string array
try {
vtzlines = new LinkedList<String>();
vtzlines = new LinkedList<>();
boolean eol = false;
boolean start = false;
boolean success = false;
@ -578,7 +577,7 @@ public class VTimeZone extends BasicTimeZone {
String dtstart = null; // current zone starts
boolean isRRULE = false; // true if the rule is described by RRULE
List<String> dates = null; // list of RDATE or RRULE strings
List<TimeZoneRule> rules = new ArrayList<TimeZoneRule>(); // rule list
List<TimeZoneRule> rules = new ArrayList<>(); // rule list
int initialRawOffset = 0; // initial offset
int initialDSTSavings = 0; // initial offset
long firstStart = MAX_TIME; // the earliest rule start time
@ -649,7 +648,7 @@ public class VTimeZone extends BasicTimeZone {
break;
}
if (dates == null) {
dates = new LinkedList<String>();
dates = new LinkedList<>();
}
// RDATE value may contain multiple date delimited
// by comma
@ -664,7 +663,7 @@ public class VTimeZone extends BasicTimeZone {
state = ERR;
break;
} else if (dates == null) {
dates = new LinkedList<String>();
dates = new LinkedList<>();
}
isRRULE = true;
dates.add(value);
@ -699,7 +698,7 @@ public class VTimeZone extends BasicTimeZone {
} else {
// This is rare case.. just use 1 hour DST savings
rawOffset = toOffset - DEF_DSTSAVINGS;
dstSavings = DEF_DSTSAVINGS;
dstSavings = DEF_DSTSAVINGS;
}
} else {
rawOffset = toOffset;
@ -797,7 +796,7 @@ public class VTimeZone extends BasicTimeZone {
AnnualTimeZoneRule finalRule = (AnnualTimeZoneRule)rules.get(finalRuleIdx);
int tmpRaw = finalRule.getRawOffset();
int tmpDST = finalRule.getDSTSavings();
// Find the last non-final rule
Date finalStart = finalRule.getFirstStart(initialRawOffset, initialDSTSavings);
Date start = finalStart;
@ -953,7 +952,7 @@ public class VTimeZone extends BasicTimeZone {
if (unt[0] > until[0]) {
until = unt;
}
// Check if BYMONTH + BYMONTHDAY + BYDAY rule
if (fields[0] == -1 || fields[1] == 0 || fields[3] == 0) {
return null;
@ -1050,18 +1049,18 @@ public class VTimeZone extends BasicTimeZone {
/*
* Parse individual RRULE
*
*
* On return -
*
*
* int[0] month calculated by BYMONTH - 1, or -1 when not found
* int[1] day of week in BYDAY, or 0 when not found
* int[2] day of week ordinal number in BYDAY, or 0 when not found
* int[i >= 3] day of month, which could be multiple values, or 0 when not found
*
*
* or
*
*
* null on any error cases, for exmaple, FREQ=YEARLY is not available
*
*
* When UNTIL attribute is available, the time will be set to until[0],
* otherwise, MIN_TIME
*/
@ -1094,7 +1093,7 @@ public class VTimeZone extends BasicTimeZone {
yearly = true;
} else {
parseError = true;
break;
break;
}
} else if (attr.equals(ICAL_UNTIL)) {
// ISO8601 UTC format, for example, "20060315T020000Z"
@ -1214,7 +1213,7 @@ public class VTimeZone extends BasicTimeZone {
results[2] = nthDayOfWeek;
return results;
}
/*
* Create a TimeZoneRule by the RDATE definition
*/
@ -1335,7 +1334,7 @@ public class VTimeZone extends BasicTimeZone {
dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime);
}
}
}
}
if (!sameRule) {
// Reset this DST information
dstName = name;
@ -1407,7 +1406,7 @@ public class VTimeZone extends BasicTimeZone {
int offset = basictz.getOffset(0 /* any time */);
boolean isDst = (offset != basictz.getRawOffset());
writeZonePropsByTime(w, isDst, getDefaultTZName(basictz.getID(), isDst),
offset, offset, DEF_TZSTARTTIME - offset, false);
offset, offset, DEF_TZSTARTTIME - offset, false);
} else {
if (dstCount > 0) {
if (finalDstRule == null) {
@ -1474,12 +1473,12 @@ public class VTimeZone extends BasicTimeZone {
if (nextStart != null) {
writeFinalRule(w, false, finalStdRule,
stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, nextStart.getTime());
}
}
}
}
}
}
}
writeFooter(w);
}
@ -1599,7 +1598,7 @@ public class VTimeZone extends BasicTimeZone {
// Check if all days are in the same month
int startDay = dayOfMonth;
int currentMonthDays = 7;
if (dayOfMonth <= 0) {
// The start day is in previous month
int prevMonthDays = 1 - dayOfMonth;
@ -1621,14 +1620,14 @@ public class VTimeZone extends BasicTimeZone {
currentMonthDays -= nextMonthDays;
int nextMonth = (month + 1) > 11 ? 0 : month + 1;
writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays, MAX_TIME /* Do not use UNTIL */, fromOffset);
}
writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays, untilTime, fromOffset);
endZoneProps(writer, isDst);
}
}
/*
* Called from writeZonePropsByDOW_GEQ_DOM
*/
@ -1829,12 +1828,12 @@ public class VTimeZone extends BasicTimeZone {
writer.write(COLON);
writer.write(tzname);
writer.write(NEWLINE);
// DTSTART
writer.write(ICAL_DTSTART);
writer.write(COLON);
writer.write(getDateTimeString(startTime + fromOffset));
writer.write(NEWLINE);
writer.write(NEWLINE);
}
/*
@ -1933,7 +1932,7 @@ public class VTimeZone extends BasicTimeZone {
int min = t / Grego.MILLIS_PER_MINUTE;
t %= Grego.MILLIS_PER_MINUTE;
int sec = t / Grego.MILLIS_PER_SECOND;
sb.append(numToString(hour, 2));
sb.append(numToString(min, 2));
sb.append(numToString(sec, 2));
@ -2107,6 +2106,7 @@ public class VTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public boolean isFrozen() {
return isFrozen;
}
@ -2115,6 +2115,7 @@ public class VTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public TimeZone freeze() {
isFrozen = true;
return this;
@ -2124,6 +2125,7 @@ public class VTimeZone extends BasicTimeZone {
* {@inheritDoc}
* @stable ICU 49
*/
@Override
public TimeZone cloneAsThawed() {
VTimeZone vtz = (VTimeZone)super.cloneAsThawed();
vtz.tz = (BasicTimeZone)tz.cloneAsThawed();

View file

@ -18,6 +18,7 @@ import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.util.AnnualTimeZoneRule;
import com.ibm.icu.util.BasicTimeZone;
import com.ibm.icu.util.BasicTimeZone.LocalOption;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.DateTimeRule;
import com.ibm.icu.util.GregorianCalendar;
@ -71,8 +72,8 @@ public class TimeZoneOffsetLocalTest extends TestFmwk {
// Expected offsets by getOffset(long time, boolean local, int[] offsets) with local = true
// or getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_STD/duplicatedTimeOpt = LOCAL_STD
// or getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = STANDARD_*/duplicatedTimeOpt = STANDARD_*
int[][] OFFSETS2 = {
// April 2, 2006
{-8*HOUR, 0},
@ -89,8 +90,8 @@ public class TimeZoneOffsetLocalTest extends TestFmwk {
{-8*HOUR, 0},
};
// Expected offsets by getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_DST/duplicatedTimeOpt = LOCAL_DST
// Expected offsets by getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = DAYLIGHT_*/duplicatedTimeOpt = DAYLIGHT_*
int[][] OFFSETS3 = {
// April 2, 2006
{-8*HOUR, 0},
@ -184,39 +185,39 @@ public class TimeZoneOffsetLocalTest extends TestFmwk {
}
}
// Test getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_STD/duplicatedTimeOpt = LOCAL_STD
// Test getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = STANDARD_*/duplicatedTimeOpt = STANDARD_*
for (int i = 0; i < TESTZONES.length; i++) {
for (int m = 0; m < MILLIS.length; m++) {
TESTZONES[i].getOffsetFromLocal(MILLIS[m], BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
TESTZONES[i].getOffsetFromLocal(MILLIS[m], LocalOption.STANDARD_FORMER, LocalOption.STANDARD_LATTER, offsets);
if (offsets[0] != OFFSETS2[m][0] || offsets[1] != OFFSETS2[m][1]) {
errln("Bad offset returned by " + TESTZONES[i].getID() + " at "
+ df.format(new Date(MILLIS[m])) + "(wall/STD/STD) - Got: "
+ df.format(new Date(MILLIS[m])) + "(wall/STANDARD_FORMER/STANDARD_LATTER) - Got: "
+ offsets[0] + "/" + offsets[1]
+ " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
}
}
}
// Test getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_DST/duplicatedTimeOpt = LOCAL_DST
// Test getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = DAYLIGHT_*/duplicatedTimeOpt = DAYLIGHT_*
for (int i = 0; i < TESTZONES.length; i++) {
for (int m = 0; m < MILLIS.length; m++) {
TESTZONES[i].getOffsetFromLocal(MILLIS[m], BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
TESTZONES[i].getOffsetFromLocal(MILLIS[m], LocalOption.DAYLIGHT_LATTER, LocalOption.DAYLIGHT_FORMER, offsets);
if (offsets[0] != OFFSETS3[m][0] || offsets[1] != OFFSETS3[m][1]) {
errln("Bad offset returned by " + TESTZONES[i].getID() + " at "
+ df.format(new Date(MILLIS[m])) + "(wall/DST/DST) - Got: "
+ df.format(new Date(MILLIS[m])) + "(wall/DAYLIGHT_LATTER/DAYLIGHT_FORMER) - Got: "
+ offsets[0] + "/" + offsets[1]
+ " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
}
}
}
// Test getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_FORMER/duplicatedTimeOpt = LOCAL_LATTER
// Test getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = FORMER/duplicatedTimeOpt = LATTER
for (int i = 0; i < TESTZONES.length; i++) {
for (int m = 0; m < MILLIS.length; m++) {
TESTZONES[i].getOffsetFromLocal(MILLIS[m], BasicTimeZone.LOCAL_FORMER, BasicTimeZone.LOCAL_LATTER, offsets);
TESTZONES[i].getOffsetFromLocal(MILLIS[m], LocalOption.FORMER, LocalOption.LATTER, offsets);
if (offsets[0] != OFFSETS2[m][0] || offsets[1] != OFFSETS2[m][1]) {
errln("Bad offset returned by " + TESTZONES[i].getID() + " at "
+ df.format(new Date(MILLIS[m])) + "(wall/FORMER/LATTER) - Got: "
@ -226,11 +227,11 @@ public class TimeZoneOffsetLocalTest extends TestFmwk {
}
}
// Test getOffsetFromLocal(long time, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LOCAL_LATTER/duplicatedTimeOpt = LOCAL_FORMER
// Test getOffsetFromLocal(long time, LocalOption nonExistingTimeOpt, LocalOption duplicatedTimeOpt, int[] offsets)
// with nonExistingTimeOpt = LATTER/duplicatedTimeOpt = FORMER
for (int i = 0; i < TESTZONES.length; i++) {
for (int m = 0; m < MILLIS.length; m++) {
TESTZONES[i].getOffsetFromLocal(MILLIS[m], BasicTimeZone.LOCAL_LATTER, BasicTimeZone.LOCAL_FORMER, offsets);
TESTZONES[i].getOffsetFromLocal(MILLIS[m], LocalOption.LATTER, LocalOption.FORMER, offsets);
if (offsets[0] != OFFSETS3[m][0] || offsets[1] != OFFSETS3[m][1]) {
errln("Bad offset returned by " + TESTZONES[i].getID() + " at "
+ df.format(new Date(MILLIS[m])) + "(wall/LATTER/FORMER) - Got: "

View file

@ -24,6 +24,7 @@ import org.junit.runners.JUnit4;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.util.AnnualTimeZoneRule;
import com.ibm.icu.util.BasicTimeZone;
import com.ibm.icu.util.BasicTimeZone.LocalOption;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.DateTimeRule;
import com.ibm.icu.util.GregorianCalendar;
@ -1235,7 +1236,7 @@ public class TimeZoneRuleTest extends TestFmwk {
int[] offsets_vtzc = new int[2];
VTimeZone vtzc = VTimeZone.create("PST");
vtzc.getOffsetFromLocal(Calendar.getInstance(vtzc).getTimeInMillis(), VTimeZone.LOCAL_STD, VTimeZone.LOCAL_STD, offsets_vtzc);
vtzc.getOffsetFromLocal(Calendar.getInstance(vtzc).getTimeInMillis(), LocalOption.STANDARD_FORMER, LocalOption.STANDARD_LATTER, offsets_vtzc);
if (offsets_vtzc[0] > offsets_vtzc[1]) {
errln("Error getOffsetFromLocal()");
}
@ -1469,7 +1470,7 @@ public class TimeZoneRuleTest extends TestFmwk {
BasicTimeZone btz = (BasicTimeZone)tz;
int []offsets = new int[2];
btz.getOffsetFromLocal(Calendar.getInstance().getTimeInMillis(), BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
btz.getOffsetFromLocal(Calendar.getInstance().getTimeInMillis(), LocalOption.STANDARD_FORMER, LocalOption.STANDARD_LATTER, offsets);
if (offsets[0] > offsets[1]) {
errln("Error calling getOffsetFromLocal().");
}