ICU-7518 Workaround for 24:00 on a transition day problem. Use 23:59:59.999 as the transition time. iCal VTIMEZONE is not capable to handle such rule. Also a minor bug fix in BasicTimeZone.hasEquivalentTransitions which was found while updating the test case.

X-SVN-Rev: 28879
This commit is contained in:
Yoshito Umaoka 2010-10-20 06:19:12 +00:00
parent a1f9c9d8f7
commit faca047508
3 changed files with 147 additions and 19 deletions

View File

@ -136,17 +136,27 @@ public abstract class BasicTimeZone extends TimeZone {
if (ignoreDstAmount) {
// Skip a transition which only differ the amount of DST savings
if (tr1 != null
&& (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
== tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
&& (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
tr1 = getNextTransition(tr1.getTime(), false);
while (true) {
if (tr1 != null
&& tr1.getTime() <= end
&& (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
== tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
&& (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
tr1 = getNextTransition(tr1.getTime(), false);
} else {
break;
}
}
if (tr2 != null
&& (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
== tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
&& (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
tr2 = getNextTransition(tr2.getTime(), false);
while (true) {
if (tr2 != null
&& tr2.getTime() <= end
&& (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
== tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
&& (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
tr2 = ((BasicTimeZone)tz).getNextTransition(tr2.getTime(), false);
} else {
break;
}
}
}

View File

@ -1624,6 +1624,18 @@ public class VTimeZone extends BasicTimeZone {
private static void writeFinalRule(Writer writer, boolean isDst, AnnualTimeZoneRule rule,
int fromRawOffset, int fromDSTSavings, long startTime) throws IOException{
DateTimeRule dtrule = toWallTimeRule(rule.getRule(), fromRawOffset, fromDSTSavings);
// If the rule's mills in a day is out of range, adjust start time.
// Olson tzdata supports 24:00 of a day, but VTIMEZONE does not.
// See ticket#7008/#7518
int timeInDay = dtrule.getRuleMillisInDay();
if (timeInDay < 0) {
startTime = startTime + (0 - timeInDay);
} else if (timeInDay >= Grego.MILLIS_PER_DAY) {
startTime = startTime - (timeInDay - (Grego.MILLIS_PER_DAY - 1));
}
int toOffset = rule.getRawOffset() + rule.getDSTSavings();
switch (dtrule.getDateRuleType()) {
case DateTimeRule.DOM:

View File

@ -436,9 +436,6 @@ public class TimeZoneRuleTest extends TestFmwk {
String[] tzids = getTestZIDs();
for (int i = 0; i < tzids.length; i++) {
if (skipIfBeforeICU(4,5,2) && tzids[i].equals("Asia/Amman")) { // ticket#7008
continue;
}
BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);
VTimeZone vtz_org = VTimeZone.create(tzids[i]);
vtz_org.setTZURL("http://source.icu-project.org/timezone");
@ -488,7 +485,12 @@ public class TimeZoneRuleTest extends TestFmwk {
TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);
if (tzt != null) {
if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {
errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding.");
int maxDelta = 1000;
if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) {
errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding.");
} else {
logln("VTimeZone for " + tzids[i] + " differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
}
}
if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, false)) {
logln("VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding in strict comparison mode.");
@ -513,9 +515,6 @@ public class TimeZoneRuleTest extends TestFmwk {
for (int n = 0; n < startTimes.length; n++) {
long startTime = startTimes[n];
for (int i = 0; i < tzids.length; i++) {
if (skipIfBeforeICU(4,5,2) && tzids[i].equals("Asia/Amman")) { // ticket#7008
continue;
}
BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);
VTimeZone vtz_org = VTimeZone.create(tzids[i]);
VTimeZone vtz_new = null;
@ -546,10 +545,15 @@ public class TimeZoneRuleTest extends TestFmwk {
TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);
if (tzt != null) {
if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {
errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding.");
int maxDelta = 1000;
if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) {
errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding.");
} else {
logln("VTimeZone for " + tzids[i] + "(>=" + startTime + ") differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
}
}
}
}
}
}
}
@ -1677,4 +1681,106 @@ public class TimeZoneRuleTest extends TestFmwk {
utcCal.set(year, month, dayOfMonth);
return utcCal.getTimeInMillis();
}
/*
* Slightly modified version of BasicTimeZone#hasEquivalentTransitions.
* This version returns true if transition time delta is within the given
* delta range.
*/
private static boolean hasEquivalentTransitions(BasicTimeZone tz1, BasicTimeZone tz2,
long start, long end,
boolean ignoreDstAmount, int maxTransitionTimeDelta) {
if (tz1.hasSameRules(tz2)) {
return true;
}
// Check the offsets at the start time
int[] offsets1 = new int[2];
int[] offsets2 = new int[2];
tz1.getOffset(start, false, offsets1);
tz2.getOffset(start, false, offsets2);
if (ignoreDstAmount) {
if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1])
|| (offsets1[1] != 0 && offsets2[1] == 0)
|| (offsets1[1] == 0 && offsets2[1] != 0)) {
return false;
}
} else {
if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
return false;
}
}
// Check transitions in the range
long time = start;
while (true) {
TimeZoneTransition tr1 = tz1.getNextTransition(time, false);
TimeZoneTransition tr2 = tz2.getNextTransition(time, false);
if (ignoreDstAmount) {
// Skip a transition which only differ the amount of DST savings
while (true) {
if (tr1 != null
&& tr1.getTime() <= end
&& (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
== tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
&& (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
tr1 = tz1.getNextTransition(tr1.getTime(), false);
} else {
break;
}
}
while (true) {
if (tr2 != null
&& tr2.getTime() <= end
&& (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
== tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
&& (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
tr2 = tz2.getNextTransition(tr2.getTime(), false);
} else {
break;
}
} }
boolean inRange1 = false;
boolean inRange2 = false;
if (tr1 != null) {
if (tr1.getTime() <= end) {
inRange1 = true;
}
}
if (tr2 != null) {
if (tr2.getTime() <= end) {
inRange2 = true;
}
}
if (!inRange1 && !inRange2) {
// No more transition in the range
break;
}
if (!inRange1 || !inRange2) {
return false;
}
if (Math.abs(tr1.getTime() - tr2.getTime()) > maxTransitionTimeDelta) {
return false;
}
if (ignoreDstAmount) {
if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()
!= tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()
|| tr1.getTo().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() == 0
|| tr1.getTo().getDSTSavings() == 0 && tr2.getTo().getDSTSavings() != 0) {
return false;
}
} else {
if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset() ||
tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) {
return false;
}
}
time = tr1.getTime() > tr2.getTime() ? tr1.getTime() : tr2.getTime();
}
return true;
}
}