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. This fix is equivalent to the ICU4J fix r28879.

X-SVN-Rev: 28880
This commit is contained in:
Yoshito Umaoka 2010-10-20 07:46:11 +00:00
parent faca047508
commit 4e8f6e1ed2
3 changed files with 155 additions and 26 deletions

View file

@ -72,17 +72,27 @@ BasicTimeZone::hasEquivalentTransitions(/*const*/ BasicTimeZone& tz, UDate start
if (ignoreDstAmount) {
// Skip a transition which only differ the amount of DST savings
if (avail1
&& (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
== tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
&& (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
getNextTransition(tr1.getTime(), FALSE, tr1);
while (TRUE) {
if (avail1
&& tr1.getTime() <= end
&& (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
== tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
&& (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
getNextTransition(tr1.getTime(), FALSE, tr1);
} else {
break;
}
}
if (avail2
&& (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
== tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
&& (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
getNextTransition(tr2.getTime(), FALSE, tr2);
while (TRUE) {
if (avail2
&& tr2.getTime() <= end
&& (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
== tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
&& (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
tz.getNextTransition(tr2.getTime(), FALSE, tr2);
} else {
break;
}
}
}

View file

@ -2476,6 +2476,18 @@ VTimeZone::writeFinalRule(VTZWriter& writer, UBool isDst, const AnnualTimeZoneRu
modifiedRule = FALSE;
dtrule = rule->getRule();
}
// 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
int32_t timeInDay = dtrule->getRuleMillisInDay();
if (timeInDay < 0) {
startTime = startTime + (0 - timeInDay);
} else if (timeInDay >= U_MILLIS_PER_DAY) {
startTime = startTime - (timeInDay - (U_MILLIS_PER_DAY - 1));
}
int32_t toOffset = rule->getRawOffset() + rule->getDSTSavings();
UnicodeString name;
rule->getName(name);

View file

@ -48,6 +48,11 @@ static const char *const TESTZIDS[] = {
"Etc/GMT+8"
};
static UBool hasEquivalentTransitions(/*const*/ BasicTimeZone& tz1, /*const*/BasicTimeZone& tz2,
UDate start, UDate end,
UBool ignoreDstAmount, int32_t maxTransitionTimeDelta,
UErrorCode& status);
class TestZIDEnumeration : public StringEnumeration {
public:
TestZIDEnumeration(UBool all = FALSE);
@ -786,12 +791,6 @@ TimeZoneRuleTest::TestVTimeZoneRoundTrip(void) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
if (!isICUVersionAtLeast(ICU_453)) {
// See ticket#7008
if (*tzid == UnicodeString("Asia/Amman")) {
continue;
}
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
vtz_org->setTZURL("http://source.icu-project.org/timezone");
@ -841,8 +840,15 @@ TimeZoneRuleTest::TestVTimeZoneRoundTrip(void) {
if (avail) {
if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
endTime, TRUE, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
int32_t maxDelta = 1000;
if (!hasEquivalentTransitions(*vtz_new, *tz, trans.getTime() + maxDelta,
endTime, TRUE, maxDelta, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
} else {
logln("VTimeZone for " + *tzid +
" differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
}
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
@ -885,12 +891,6 @@ TimeZoneRuleTest::TestVTimeZoneRoundTripPartial(void) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
if (!isICUVersionAtLeast(ICU_453)) {
// See ticket#7008
if (*tzid == UnicodeString("Asia/Amman")) {
continue;
}
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
VTimeZone *vtz_new = NULL;
@ -930,8 +930,16 @@ TimeZoneRuleTest::TestVTimeZoneRoundTripPartial(void) {
if (avail) {
if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
endTime, TRUE, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
int32_t maxDelta = 1000;
if (!hasEquivalentTransitions(*vtz_new, *tz, trans.getTime() + maxDelta,
endTime, TRUE, maxDelta, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
} else {
logln("VTimeZone for " + *tzid +
" differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
}
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
@ -2510,6 +2518,105 @@ TimeZoneRuleTest::compareTransitionsDescending(BasicTimeZone& z1, BasicTimeZone&
}
}
// Slightly modified version of BasicTimeZone::hasEquivalentTransitions.
// This version returns TRUE if transition time delta is within the given
// delta range.
static UBool hasEquivalentTransitions(/*const*/ BasicTimeZone& tz1, /*const*/BasicTimeZone& tz2,
UDate start, UDate end,
UBool ignoreDstAmount, int32_t maxTransitionTimeDelta,
UErrorCode& status) {
if (U_FAILURE(status)) {
return FALSE;
}
if (tz1.hasSameRules(tz2)) {
return TRUE;
}
// Check the offsets at the start time
int32_t raw1, raw2, dst1, dst2;
tz1.getOffset(start, FALSE, raw1, dst1, status);
if (U_FAILURE(status)) {
return FALSE;
}
tz2.getOffset(start, FALSE, raw2, dst2, status);
if (U_FAILURE(status)) {
return FALSE;
}
if (ignoreDstAmount) {
if ((raw1 + dst1 != raw2 + dst2)
|| (dst1 != 0 && dst2 == 0)
|| (dst1 == 0 && dst2 != 0)) {
return FALSE;
}
} else {
if (raw1 != raw2 || dst1 != dst2) {
return FALSE;
}
}
// Check transitions in the range
UDate time = start;
TimeZoneTransition tr1, tr2;
while (TRUE) {
UBool avail1 = tz1.getNextTransition(time, FALSE, tr1);
UBool avail2 = tz2.getNextTransition(time, FALSE, tr2);
if (ignoreDstAmount) {
// Skip a transition which only differ the amount of DST savings
while (TRUE) {
if (avail1
&& tr1.getTime() <= end
&& (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
== tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
&& (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
tz1.getNextTransition(tr1.getTime(), FALSE, tr1);
} else {
break;
}
}
while (TRUE) {
if (avail2
&& tr2.getTime() <= end
&& (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
== tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
&& (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
tz2.getNextTransition(tr2.getTime(), FALSE, tr2);
} else {
break;
}
}
}
UBool inRange1 = (avail1 && tr1.getTime() <= end);
UBool inRange2 = (avail2 && tr2.getTime() <= end);
if (!inRange1 && !inRange2) {
// No more transition in the range
break;
}
if (!inRange1 || !inRange2) {
return FALSE;
}
double delta = tr1.getTime() >= tr2.getTime() ? tr1.getTime() - tr2.getTime() : tr2.getTime() - tr1.getTime();
if (delta > (double)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;
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof