use of android.icu.util.TimeZoneTransition in project j2objc by google.
the class OlsonTimeZone method initTransitionRules.
private synchronized void initTransitionRules() {
if (transitionRulesInitialized) {
return;
}
initialRule = null;
firstTZTransition = null;
firstFinalTZTransition = null;
historicRules = null;
firstTZTransitionIdx = 0;
finalZoneWithStartYear = null;
String stdName = getID() + "(STD)";
String dstName = getID() + "(DST)";
int raw, dst;
// Create initial rule
raw = initialRawOffset() * Grego.MILLIS_PER_SECOND;
dst = initialDstOffset() * Grego.MILLIS_PER_SECOND;
initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
if (transitionCount > 0) {
int transitionIdx, typeIdx;
// For now, keeping this code for just in case. Feb 19, 2010 Yoshito
for (transitionIdx = 0; transitionIdx < transitionCount; transitionIdx++) {
if (getInt(typeMapData[transitionIdx]) != 0) {
// type 0 is the initial type
break;
}
firstTZTransitionIdx++;
}
if (transitionIdx == transitionCount) {
// Actually no transitions...
} else {
// Build historic rule array
long[] times = new long[transitionCount];
for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
// Gather all start times for each pair of offsets
int nTimes = 0;
for (transitionIdx = firstTZTransitionIdx; transitionIdx < transitionCount; transitionIdx++) {
if (typeIdx == getInt(typeMapData[transitionIdx])) {
long tt = transitionTimes64[transitionIdx] * Grego.MILLIS_PER_SECOND;
if (tt < finalStartMillis) {
// Exclude transitions after finalMillis
times[nTimes++] = tt;
}
}
}
if (nTimes > 0) {
long[] startTimes = new long[nTimes];
System.arraycopy(times, 0, startTimes, 0, nTimes);
// Create a TimeArrayTimeZoneRule
raw = typeOffsets[typeIdx * 2] * Grego.MILLIS_PER_SECOND;
dst = typeOffsets[typeIdx * 2 + 1] * Grego.MILLIS_PER_SECOND;
if (historicRules == null) {
historicRules = new TimeArrayTimeZoneRule[typeCount];
}
historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst, startTimes, DateTimeRule.UTC_TIME);
}
}
// Create initial transition
typeIdx = getInt(typeMapData[firstTZTransitionIdx]);
firstTZTransition = new TimeZoneTransition(transitionTimes64[firstTZTransitionIdx] * Grego.MILLIS_PER_SECOND, initialRule, historicRules[typeIdx]);
}
}
if (finalZone != null) {
// Get the first occurrence of final rule starts
long startTime = (long) finalStartMillis;
TimeZoneRule firstFinalRule;
if (finalZone.useDaylightTime()) {
/*
* Note: When an OlsonTimeZone is constructed, we should set the final year
* as the start year of finalZone. However, the boundary condition used for
* getting offset from finalZone has some problems. So setting the start year
* in the finalZone will cause a problem. For now, we do not set the valid
* start year when the construction time and create a clone and set the
* start year when extracting rules.
*/
finalZoneWithStartYear = (SimpleTimeZone) finalZone.clone();
finalZoneWithStartYear.setStartYear(finalStartYear);
TimeZoneTransition tzt = finalZoneWithStartYear.getNextTransition(startTime, false);
firstFinalRule = tzt.getTo();
startTime = tzt.getTime();
} else {
finalZoneWithStartYear = finalZone;
firstFinalRule = new TimeArrayTimeZoneRule(finalZone.getID(), finalZone.getRawOffset(), 0, new long[] { startTime }, DateTimeRule.UTC_TIME);
}
TimeZoneRule prevRule = null;
if (transitionCount > 0) {
prevRule = historicRules[getInt(typeMapData[transitionCount - 1])];
}
if (prevRule == null) {
// No historic transitions, but only finalZone available
prevRule = initialRule;
}
firstFinalTZTransition = new TimeZoneTransition(startTime, prevRule, firstFinalRule);
}
transitionRulesInitialized = true;
}
use of android.icu.util.TimeZoneTransition in project j2objc by google.
the class TimeZoneFormatTest method TestTimeRoundTrip.
/*
* Test case of round trip time and text. This test case detects every canonical TimeZone's
* rule transition since 1900 until 2020, then check if time around each transition can
* round trip as expected.
*/
@Test
public void TestTimeRoundTrip() {
boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false);
int startYear, endYear;
if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) {
startYear = 1900;
} else {
startYear = 1990;
}
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
endYear = cal.get(Calendar.YEAR) + 3;
cal.set(startYear, Calendar.JANUARY, 1);
final long START_TIME = cal.getTimeInMillis();
cal.set(endYear, Calendar.JANUARY, 1);
final long END_TIME = cal.getTimeInMillis();
// These patterns are ambiguous at DST->STD local time overlap
List<String> AMBIGUOUS_DST_DECESSION = Arrays.asList("v", "vvvv", "V", "VV", "VVV", "VVVV");
// These patterns are ambiguous at STD->STD/DST->DST local time overlap
List<String> AMBIGUOUS_NEGATIVE_SHIFT = Arrays.asList("z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV");
// These patterns only support integer minutes offset
List<String> MINUTES_OFFSET = Arrays.asList("X", "XX", "XXX", "x", "xx", "xxx");
// Regex pattern used for filtering zone IDs without exemplar location
final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]");
final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
ULocale[] LOCALES = null;
// timer for performance analysis
long[] times = new long[PATTERNS.length];
long timer;
if (TEST_ALL) {
// It may take about an hour for testing all locales
LOCALES = ULocale.getAvailableLocales();
} else if (TestFmwk.getExhaustiveness() > 5) {
LOCALES = new ULocale[] { new ULocale("ar_EG"), new ULocale("bg_BG"), new ULocale("ca_ES"), new ULocale("da_DK"), new ULocale("de"), new ULocale("de_DE"), new ULocale("el_GR"), new ULocale("en"), new ULocale("en_AU"), new ULocale("en_CA"), new ULocale("en_US"), new ULocale("es"), new ULocale("es_ES"), new ULocale("es_MX"), new ULocale("fi_FI"), new ULocale("fr"), new ULocale("fr_CA"), new ULocale("fr_FR"), new ULocale("he_IL"), new ULocale("hu_HU"), new ULocale("it"), new ULocale("it_IT"), new ULocale("ja"), new ULocale("ja_JP"), new ULocale("ko"), new ULocale("ko_KR"), new ULocale("nb_NO"), new ULocale("nl_NL"), new ULocale("nn_NO"), new ULocale("pl_PL"), new ULocale("pt"), new ULocale("pt_BR"), new ULocale("pt_PT"), new ULocale("ru_RU"), new ULocale("sv_SE"), new ULocale("th_TH"), new ULocale("tr_TR"), new ULocale("zh"), new ULocale("zh_Hans"), new ULocale("zh_Hans_CN"), new ULocale("zh_Hant"), new ULocale("zh_Hant_HK"), new ULocale("zh_Hant_TW") };
} else {
LOCALES = new ULocale[] { new ULocale("en") };
}
SimpleDateFormat sdfGMT = new SimpleDateFormat(BASEPATTERN);
sdfGMT.setTimeZone(TimeZone.getTimeZone("Etc/GMT"));
long testCounts = 0;
long[] testTimes = new long[4];
boolean[] expectedRoundTrip = new boolean[4];
int testLen = 0;
for (int locidx = 0; locidx < LOCALES.length; locidx++) {
logln("Locale: " + LOCALES[locidx].toString());
for (int patidx = 0; patidx < PATTERNS.length; patidx++) {
logln(" pattern: " + PATTERNS[patidx]);
String pattern = BASEPATTERN + " " + PATTERNS[patidx];
SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]);
boolean minutesOffset = MINUTES_OFFSET.contains(PATTERNS[patidx]);
Set<String> ids = null;
if (JDKTZ) {
ids = new TreeSet<String>();
String[] jdkIDs = java.util.TimeZone.getAvailableIDs();
for (String jdkID : jdkIDs) {
if (EXCL_TZ_PATTERN.matcher(jdkID).matches()) {
continue;
}
String tmpID = TimeZone.getCanonicalID(jdkID);
if (tmpID != null) {
ids.add(tmpID);
}
}
} else {
ids = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
}
for (String id : ids) {
if (PATTERNS[patidx].equals("V")) {
// Some zones do not have short ID assigned, such as Asia/Riyadh87.
// The time roundtrip will fail for such zones with pattern "V" (short zone ID).
// This is expected behavior.
String shortZoneID = ZoneMeta.getShortID(id);
if (shortZoneID == null) {
continue;
}
} else if (PATTERNS[patidx].equals("VVV")) {
// This is expected behavior.
if (id.indexOf('/') < 0 || LOC_EXCLUSION_PATTERN.matcher(id).matches()) {
continue;
}
}
if (id.equals("Pacific/Apia") && PATTERNS[patidx].equals("vvvv") && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
continue;
}
BasicTimeZone btz = (BasicTimeZone) TimeZone.getTimeZone(id, TimeZone.TIMEZONE_ICU);
TimeZone tz = TimeZone.getTimeZone(id);
sdf.setTimeZone(tz);
long t = START_TIME;
TimeZoneTransition tzt = null;
boolean middle = true;
boolean last = false;
while (t < END_TIME) {
if (tzt == null) {
testTimes[0] = t;
expectedRoundTrip[0] = true;
testLen = 1;
} else {
int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings();
int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings();
int delta = toOffset - fromOffset;
if (delta < 0) {
boolean isDstDecession = tzt.getFrom().getDSTSavings() > 0 && tzt.getTo().getDSTSavings() == 0;
testTimes[0] = t + delta - 1;
expectedRoundTrip[0] = true;
testTimes[1] = t + delta;
expectedRoundTrip[1] = isDstDecession ? !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
testTimes[2] = t - 1;
expectedRoundTrip[2] = isDstDecession ? !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
testTimes[3] = t;
expectedRoundTrip[3] = true;
testLen = 4;
} else {
testTimes[0] = t - 1;
expectedRoundTrip[0] = true;
testTimes[1] = t;
expectedRoundTrip[1] = true;
testLen = 2;
}
}
for (int testidx = 0; testidx < testLen; testidx++) {
testCounts++;
timer = System.currentTimeMillis();
String text = sdf.format(new Date(testTimes[testidx]));
try {
Date parsedDate = sdf.parse(text);
long restime = parsedDate.getTime();
long timeDiff = restime - testTimes[testidx];
boolean bTimeMatch = minutesOffset ? (timeDiff / 60000) * 60000 == 0 : timeDiff == 0;
if (!bTimeMatch) {
StringBuffer msg = new StringBuffer();
msg.append("Time round trip failed for ").append("tzid=").append(id).append(", locale=").append(LOCALES[locidx]).append(", pattern=").append(PATTERNS[patidx]).append(", text=").append(text).append(", gmt=").append(sdfGMT.format(new Date(testTimes[testidx]))).append(", time=").append(testTimes[testidx]).append(", restime=").append(restime).append(", diff=").append(timeDiff);
if (expectedRoundTrip[testidx] && !isSpecialTimeRoundTripCase(LOCALES[locidx], id, PATTERNS[patidx], testTimes[testidx])) {
errln("FAIL: " + msg.toString());
} else if (REALLY_VERBOSE_LOG) {
logln(msg.toString());
}
}
} catch (ParseException pe) {
errln("FAIL: " + pe.getMessage() + " tzid=" + id + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] + ", text=" + text);
}
times[patidx] += System.currentTimeMillis() - timer;
}
if (last) {
break;
}
tzt = btz.getNextTransition(t, false);
if (tzt == null) {
last = true;
t = END_TIME - 1;
} else if (middle) {
// Test the date in the middle of two transitions.
t += (tzt.getTime() - t) / 2;
middle = false;
tzt = null;
} else {
t = tzt.getTime();
}
}
}
}
}
long total = 0;
logln("### Elapsed time by patterns ###");
for (int i = 0; i < PATTERNS.length; i++) {
logln(times[i] + "ms (" + PATTERNS[i] + ")");
total += times[i];
}
logln("Total: " + total + "ms");
logln("Iteration: " + testCounts);
}
use of android.icu.util.TimeZoneTransition in project Resurrection_packages_apps_Settings by ResurrectionRemix.
the class TimeZoneInfoPreferenceController method findNextDstTransition.
private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
if (!(timeZone instanceof BasicTimeZone)) {
return null;
}
final BasicTimeZone basicTimeZone = (BasicTimeZone) timeZone;
TimeZoneTransition transition = basicTimeZone.getNextTransition(mDate.getTime(), /* inclusive */
false);
do {
if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
break;
}
transition = basicTimeZone.getNextTransition(transition.getTime(), /*inclusive */
false);
} while (transition != null);
return transition;
}
use of android.icu.util.TimeZoneTransition in project android_packages_apps_Settings by omnirom.
the class TimeZoneInfoPreferenceController method findNextDstTransition.
private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
if (!(timeZone instanceof BasicTimeZone)) {
return null;
}
final BasicTimeZone basicTimeZone = (BasicTimeZone) timeZone;
TimeZoneTransition transition = basicTimeZone.getNextTransition(mDate.getTime(), /* inclusive */
false);
do {
if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
break;
}
transition = basicTimeZone.getNextTransition(transition.getTime(), /*inclusive */
false);
} while (transition != null);
return transition;
}
use of android.icu.util.TimeZoneTransition in project j2objc by google.
the class SimpleDateFormat method parse.
/**
* Overrides DateFormat
* @see DateFormat
*/
@Override
public void parse(String text, Calendar cal, ParsePosition parsePos) {
TimeZone backupTZ = null;
Calendar resultCal = null;
if (cal != calendar && !cal.getType().equals(calendar.getType())) {
// Different calendar type
// We use the time/zone from the input calendar, but
// do not use the input calendar for field calculation.
calendar.setTimeInMillis(cal.getTimeInMillis());
backupTZ = calendar.getTimeZone();
calendar.setTimeZone(cal.getTimeZone());
resultCal = cal;
cal = calendar;
}
int pos = parsePos.getIndex();
if (pos < 0) {
parsePos.setErrorIndex(0);
return;
}
int start = pos;
// Hold the day period until everything else is parsed, because we need
// the hour to interpret time correctly.
// Using an one-element array for output parameter.
Output<DayPeriodRules.DayPeriod> dayPeriod = new Output<DayPeriodRules.DayPeriod>(null);
Output<TimeType> tzTimeType = new Output<TimeType>(TimeType.UNKNOWN);
boolean[] ambiguousYear = { false };
// item index for the first numeric field within a contiguous numeric run
int numericFieldStart = -1;
// item length for the first numeric field within a contiguous numeric run
int numericFieldLength = 0;
// start index of numeric text run in the input text
int numericStartPos = 0;
MessageFormat numericLeapMonthFormatter = null;
if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
}
Object[] items = getPatternItems();
int i = 0;
while (i < items.length) {
if (items[i] instanceof PatternItem) {
// Handle pattern field
PatternItem field = (PatternItem) items[i];
if (field.isNumeric) {
// try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
if (numericFieldStart == -1) {
// check if this field is followed by abutting another numeric field
if ((i + 1) < items.length && (items[i + 1] instanceof PatternItem) && ((PatternItem) items[i + 1]).isNumeric) {
// record the first numeric field within a numeric text run
numericFieldStart = i;
numericFieldLength = field.length;
numericStartPos = pos;
}
}
}
if (numericFieldStart != -1) {
// Handle a numeric field within abutting numeric fields
int len = field.length;
if (numericFieldStart == i) {
len = numericFieldLength;
}
// Parse a numeric field
pos = subParse(text, pos, field.type, len, true, false, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType);
if (pos < 0) {
// If the parse fails anywhere in the numeric run, back up to the
// start of the run and use shorter pattern length for the first
// numeric field.
--numericFieldLength;
if (numericFieldLength == 0) {
// can not make shorter any more
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
i = numericFieldStart;
pos = numericStartPos;
continue;
}
} else if (field.type != 'l') {
// (SMALL LETTER L) obsolete pattern char just gets ignored
// Handle a non-numeric field or a non-abutting numeric field
numericFieldStart = -1;
int s = pos;
pos = subParse(text, pos, field.type, field.length, false, true, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType, dayPeriod);
if (pos < 0) {
if (pos == ISOSpecialEra) {
// era not present, in special cases allow this to continue
pos = s;
if (i + 1 < items.length) {
String patl = null;
// if it will cause a class cast exception to String, we can't use it
try {
patl = (String) items[i + 1];
} catch (ClassCastException cce) {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// get next item in pattern
if (patl == null)
patl = (String) items[i + 1];
int plen = patl.length();
int idx = 0;
// Skip contiguous white spaces.
while (idx < plen) {
char pch = patl.charAt(idx);
if (PatternProps.isWhiteSpace(pch))
idx++;
else
break;
}
// if next item in pattern is all whitespace, skip it
if (idx == plen) {
i++;
}
}
} else {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
}
} else {
// Handle literal pattern text literal
numericFieldStart = -1;
boolean[] complete = new boolean[1];
pos = matchLiteral(text, pos, items, i, complete);
if (!complete[0]) {
// Set the position of mismatch
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
++i;
}
// Special hack for trailing "." after non-numeric field.
if (pos < text.length()) {
char extra = text.charAt(pos);
if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
// only do if the last field is not numeric
Object lastItem = items[items.length - 1];
if (lastItem instanceof PatternItem && !((PatternItem) lastItem).isNumeric) {
// skip the extra "."
pos++;
}
}
}
// If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
if (dayPeriod.value != null) {
DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());
if (!cal.isSet(Calendar.HOUR) && !cal.isSet(Calendar.HOUR_OF_DAY)) {
// If hour is not set, set time to the midpoint of current day period, overwriting
// minutes if it's set.
double midPoint = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
// Truncate midPoint toward zero to get the hour.
// Any leftover means it was a half-hour.
int midPointHour = (int) midPoint;
int midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
// No need to set am/pm because hour-of-day is set last therefore takes precedence.
cal.set(Calendar.HOUR_OF_DAY, midPointHour);
cal.set(Calendar.MINUTE, midPointMinute);
} else {
int hourOfDay;
if (cal.isSet(Calendar.HOUR_OF_DAY)) {
// Hour is parsed in 24-hour format.
hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
} else {
// Hour is parsed in 12-hour format.
hourOfDay = cal.get(Calendar.HOUR);
// so 0 unambiguously means a 24-hour time from above.
if (hourOfDay == 0) {
hourOfDay = 12;
}
}
assert (0 <= hourOfDay && hourOfDay <= 23);
// If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
// Make hour-of-day take precedence over (hour + am/pm) by setting it again.
cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
} else {
// - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
if (hourOfDay == 12) {
hourOfDay = 0;
}
double currentHour = hourOfDay + cal.get(Calendar.MINUTE) / 60.0;
double midPointHour = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
double hoursAheadMidPoint = currentHour - midPointHour;
// Assume current time is in the AM.
if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
// Assumption holds; set time as such.
cal.set(Calendar.AM_PM, 0);
} else {
cal.set(Calendar.AM_PM, 1);
}
}
}
}
// At this point the fields of Calendar have been set. Calendar
// will fill in default values for missing fields when the time
// is computed.
parsePos.setIndex(pos);
// the year correctly to start with in other cases -- see subParse().
try {
TimeType tztype = tzTimeType.value;
if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
Calendar copy;
if (ambiguousYear[0]) {
// the two-digit year == the default start year
copy = (Calendar) cal.clone();
Date parsedDate = copy.getTime();
if (parsedDate.before(getDefaultCenturyStart())) {
// We can't use add here because that does a complete() first.
cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
}
}
if (tztype != TimeType.UNKNOWN) {
copy = (Calendar) cal.clone();
TimeZone tz = copy.getTimeZone();
BasicTimeZone btz = null;
if (tz instanceof BasicTimeZone) {
btz = (BasicTimeZone) tz;
}
// Get local millis
copy.set(Calendar.ZONE_OFFSET, 0);
copy.set(Calendar.DST_OFFSET, 0);
long localMillis = copy.getTimeInMillis();
// Make sure parsed time zone type (Standard or Daylight)
// matches the rule used by the parsed time zone.
int[] offsets = new int[2];
if (btz != null) {
if (tztype == TimeType.STANDARD) {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
} else {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
}
} else {
// No good way to resolve ambiguous time at transition,
// but following code work in most case.
tz.getOffset(localMillis, true, offsets);
if (tztype == TimeType.STANDARD && offsets[1] != 0 || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
// Roll back one day and try it again.
// Note: This code assumes 1. timezone transition only happens
// once within 24 hours at max
// 2. the difference of local offsets at the transition is
// less than 24 hours.
tz.getOffset(localMillis - (24 * 60 * 60 * 1000), true, offsets);
}
}
// Now, compare the results with parsed type, either standard or
// daylight saving time
int resolvedSavings = offsets[1];
if (tztype == TimeType.STANDARD) {
if (offsets[1] != 0) {
// Override DST_OFFSET = 0 in the result calendar
resolvedSavings = 0;
}
} else {
// tztype == TZTYPE_DST
if (offsets[1] == 0) {
if (btz != null) {
long time = localMillis + offsets[0];
// We use the nearest daylight saving time rule.
TimeZoneTransition beforeTrs, afterTrs;
long beforeT = time, afterT = time;
int beforeSav = 0, afterSav = 0;
// Search for DST rule before or on the time
while (true) {
beforeTrs = btz.getPreviousTransition(beforeT, true);
if (beforeTrs == null) {
break;
}
beforeT = beforeTrs.getTime() - 1;
beforeSav = beforeTrs.getFrom().getDSTSavings();
if (beforeSav != 0) {
break;
}
}
// Search for DST rule after the time
while (true) {
afterTrs = btz.getNextTransition(afterT, false);
if (afterTrs == null) {
break;
}
afterT = afterTrs.getTime();
afterSav = afterTrs.getTo().getDSTSavings();
if (afterSav != 0) {
break;
}
}
if (beforeTrs != null && afterTrs != null) {
if (time - beforeT > afterT - time) {
resolvedSavings = afterSav;
} else {
resolvedSavings = beforeSav;
}
} else if (beforeTrs != null && beforeSav != 0) {
resolvedSavings = beforeSav;
} else if (afterTrs != null && afterSav != 0) {
resolvedSavings = afterSav;
} else {
resolvedSavings = btz.getDSTSavings();
}
} else {
resolvedSavings = tz.getDSTSavings();
}
if (resolvedSavings == 0) {
// Final fallback
resolvedSavings = millisPerHour;
}
}
}
cal.set(Calendar.ZONE_OFFSET, offsets[0]);
cal.set(Calendar.DST_OFFSET, resolvedSavings);
}
}
}// if any fields are out of range, e.g., MONTH == 17.
catch (IllegalArgumentException e) {
parsePos.setErrorIndex(pos);
parsePos.setIndex(start);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// instead of the input calendar
if (resultCal != null) {
resultCal.setTimeZone(cal.getTimeZone());
resultCal.setTimeInMillis(cal.getTimeInMillis());
}
// Restore the original time zone if required
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
}
Aggregations