ICU-8916 Calendar APIs supporting options for handling ambiguous wall time (Merged from the work branch to the ICU4C trunk).

X-SVN-Rev: 31421
This commit is contained in:
Yoshito Umaoka 2012-02-17 23:53:05 +00:00
parent 06f294319e
commit 9d6a55f33d
8 changed files with 857 additions and 39 deletions

View file

@ -31,6 +31,10 @@
#if !UCONFIG_NO_FORMATTING
#include "unicode/gregocal.h"
#include "unicode/basictz.h"
#include "unicode/simpletz.h"
#include "unicode/rbtz.h"
#include "unicode/vtzone.h"
#include "gregoimp.h"
#include "buddhcal.h"
#include "taiwncal.h"
@ -51,6 +55,7 @@
#include "uresimp.h"
#include "ustrenum.h"
#include "uassert.h"
#include "olsontz.h"
#if !UCONFIG_NO_SERVICE
static icu::ICULocaleService* gService = NULL;
@ -665,7 +670,9 @@ fAreFieldsVirtuallySet(FALSE),
fNextStamp((int32_t)kMinimumUserStamp),
fTime(0),
fLenient(TRUE),
fZone(0)
fZone(0),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
clear();
fZone = TimeZone::createDefault();
@ -686,7 +693,9 @@ fAreFieldsVirtuallySet(FALSE),
fNextStamp((int32_t)kMinimumUserStamp),
fTime(0),
fLenient(TRUE),
fZone(0)
fZone(0),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
if(zone == 0) {
#if defined (U_DEBUG_CAL)
@ -714,7 +723,9 @@ fAreFieldsVirtuallySet(FALSE),
fNextStamp((int32_t)kMinimumUserStamp),
fTime(0),
fLenient(TRUE),
fZone(0)
fZone(0),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
clear();
fZone = zone.clone();
@ -755,6 +766,8 @@ Calendar::operator=(const Calendar &right)
fAreFieldsSet = right.fAreFieldsSet;
fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet;
fLenient = right.fLenient;
fRepeatedWallTime = right.fRepeatedWallTime;
fSkippedWallTime = right.fSkippedWallTime;
if (fZone != NULL) {
delete fZone;
}
@ -936,6 +949,8 @@ Calendar::isEquivalentTo(const Calendar& other) const
{
return typeid(*this) == typeid(other) &&
fLenient == other.fLenient &&
fRepeatedWallTime == other.fRepeatedWallTime &&
fSkippedWallTime == other.fSkippedWallTime &&
fFirstDayOfWeek == other.fFirstDayOfWeek &&
fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek &&
fWeekendOnset == other.fWeekendOnset &&
@ -2094,6 +2109,40 @@ Calendar::isLenient() const
// -------------------------------------
void
Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option)
{
if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) {
fRepeatedWallTime = option;
}
}
// -------------------------------------
UCalendarWallTimeOption
Calendar::getRepeatedWallTimeOption(void) const
{
return fRepeatedWallTime;
}
// -------------------------------------
void
Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option)
{
fSkippedWallTime = option;
}
// -------------------------------------
UCalendarWallTimeOption
Calendar::getSkippedWallTimeOption(void) const
{
return fSkippedWallTime;
}
// -------------------------------------
void
Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value)
{
@ -2589,33 +2638,97 @@ void Calendar::computeTime(UErrorCode& status) {
// is legacy behavior. Without this, clear(MONTH) has no effect,
// since the internally set JULIAN_DAY is used.
if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) &&
newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
} else {
millisInDay = computeMillisInDay();
}
newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
} else {
millisInDay = computeMillisInDay();
}
UDate t = 0;
if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET));
} else {
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
// for discussion purposes here.
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
// We assume standard time, that is, 2:30 am is interpreted as 3:30 am DST.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
// Again, we assume standard time, that is, 1:30 am is interpreted as 1:30 am Std.
//
// 1. The positive offset change such as transition into DST.
// Here, a designated time of 2:00 am - 2:59 am does not actually exist.
// For this case, skippedWallTime option specifies the behavior.
// For example, 2:30 am is interpreted as;
// - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
// - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
// - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
// 2. The negative offset change such as transition out of DST.
// Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid
// representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
// For this case, repeatedWallTime option specifies the behavior.
// For example, 1:30 am is interpreted as;
// - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
// - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
//
// In addition to above, when calendar is strict (not default), wall time falls into
// the skipped time range will be processed as an error case.
//
// These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
// at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
// subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
// should be also handled in the same place, but we cannot change the code flow without deprecating
// the protected method.
//
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) ||
fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
millisInDay -= internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET);
} else {
millisInDay -= computeZoneOffset(millis, millisInDay,status);
}
internalSetTime(millis + millisInDay);
if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) {
// When strict, invalidate a wall time falls into a skipped wall time range.
// When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
// the result time will be adjusted to the next valid time (on wall clock).
int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status);
UDate tmpTime = millis + millisInDay - zoneOffset;
int32_t raw, dst;
fZone->getOffset(tmpTime, FALSE, raw, dst, status);
if (U_SUCCESS(status)) {
// zoneOffset != (raw + dst) only when the given wall time fall into
// a skipped wall time range caused by positive zone offset transition.
if (zoneOffset != (raw + dst)) {
if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID);
// Adjust time to the next valid wall clock time.
// At this point, tmpTime is on or after the zone offset transition causing
// the skipped time range.
BasicTimeZone *btz = getBasicTimeZone();
if (btz) {
TimeZoneTransition transition;
UBool hasTransition = btz->getPreviousTransition(tmpTime, TRUE, transition);
if (hasTransition) {
t = transition.getTime();
} else {
// Could not find any transitions.
// Note: This should never happen.
status = U_INTERNAL_PROGRAM_ERROR;
}
} else {
// If not BasicTimeZone, return unsupported error for now.
// TODO: We may support non-BasicTimeZone in future.
status = U_UNSUPPORTED_ERROR;
}
}
} else {
t = tmpTime;
}
}
} else {
t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status);
}
}
if (U_SUCCESS(status)) {
internalSetTime(t);
}
}
/**
@ -2672,12 +2785,49 @@ int32_t Calendar::computeMillisInDay() {
*/
int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) {
int32_t rawOffset, dstOffset;
getTimeZone().getOffset(millis+millisInDay, TRUE, rawOffset, dstOffset, ec);
UDate wall = millis + millisInDay;
BasicTimeZone* btz = getBasicTimeZone();
if (btz) {
int duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kFormer : BasicTimeZone::kLatter;
int nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kLatter : BasicTimeZone::kFormer;
btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec);
} else {
const TimeZone& tz = getTimeZone();
// By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
tz.getOffset(wall, TRUE, rawOffset, dstOffset, ec);
UBool sawRecentNegativeShift = FALSE;
if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) {
// Check if the given wall time falls into repeated time range
UDate tgmt = wall - (rawOffset + dstOffset);
// Any negative zone transition within last 6 hours?
// Note: The maximum historic negative zone transition is -3 hours in the tz database.
// 6 hour window would be sufficient for this purpose.
int32_t tmpRaw, tmpDst;
tz.getOffset(tgmt - 6*60*60*1000, FALSE, tmpRaw, tmpDst, ec);
int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst);
U_ASSERT(offsetDelta < -6*60*60*1000);
if (offsetDelta < 0) {
sawRecentNegativeShift = TRUE;
// Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
// into the repeated time range, use offsets before the transition.
// Note: If it does not fall into the repeated time range, offsets remain unchanged below.
tz.getOffset(wall + offsetDelta, TRUE, rawOffset, dstOffset, ec);
}
}
if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) {
// When skipped wall time option is WALLTIME_FIRST,
// recalculate offsets from the resolved time (non-wall).
// When the given wall time falls into skipped wall time,
// the offsets will be based on the zone offsets AFTER
// the transition (which means, earliest possibe interpretation).
UDate tgmt = wall - (rawOffset + dstOffset);
tz.getOffset(tgmt, FALSE, rawOffset, dstOffset, ec);
}
}
return rawOffset + dstOffset;
// Note: Because we pass in wall millisInDay, rather than
// standard millisInDay, we interpret "1:00 am" on the day
// of cessation of DST as "1:00 am Std" (assuming the time
// of cessation is 2:00 am).
}
int32_t Calendar::computeJulianDay()
@ -3420,6 +3570,17 @@ Calendar::internalSet(EDateFields field, int32_t value)
internalSet((UCalendarDateFields) field, value);
}
BasicTimeZone*
Calendar::getBasicTimeZone(void) const {
if (dynamic_cast<const OlsonTimeZone *>(fZone) != NULL
|| dynamic_cast<const SimpleTimeZone *>(fZone) != NULL
|| dynamic_cast<const RuleBasedTimeZone *>(fZone) != NULL
|| dynamic_cast<const VTimeZone *>(fZone) != NULL) {
return (BasicTimeZone*)fZone;
}
return NULL;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -308,6 +308,12 @@ ucal_getAttribute( const UCalendar* cal,
case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK:
return ((Calendar*)cal)->getMinimalDaysInFirstWeek();
case UCAL_REPEATED_WALL_TIME:
return ((Calendar*)cal)->getRepeatedWallTimeOption();
case UCAL_SKIPPED_WALL_TIME:
return ((Calendar*)cal)->getSkippedWallTimeOption();
default:
break;
}
@ -332,6 +338,14 @@ ucal_setAttribute( UCalendar* cal,
case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK:
((Calendar*)cal)->setMinimalDaysInFirstWeek((uint8_t)newValue);
break;
case UCAL_REPEATED_WALL_TIME:
((Calendar*)cal)->setRepeatedWallTimeOption((UCalendarWallTimeOption)newValue);
break;
case UCAL_SKIPPED_WALL_TIME:
((Calendar*)cal)->setSkippedWallTimeOption((UCalendarWallTimeOption)newValue);
break;
}
}

View file

@ -48,6 +48,7 @@ class ICUServiceFactory;
*/
typedef int32_t UFieldResolutionTable[12][8];
class BasicTimeZone;
/**
* <code>Calendar</code> is an abstract base class for converting between
* a <code>UDate</code> object and a set of integer fields such as
@ -821,6 +822,74 @@ public:
*/
UBool isLenient(void) const;
/**
* Sets the behavior for handling wall time repeating multiple times
* at negative time zone offset transitions. For example, 1:30 AM on
* November 6, 2011 in US Eastern time (Ameirca/New_York) occurs twice;
* 1:30 AM EDT, then 1:30 AM EST one hour later. When <code>UCAL_WALLTIME_FIRST</code>
* is used, the wall time 1:30AM in this example will be interpreted as 1:30 AM EDT
* (first occurrence). When <code>UCAL_WALLTIME_LAST</code> is used, it will be
* interpreted as 1:30 AM EST (last occurrence). The default value is
* <code>UCAL_WALLTIME_LAST</code>.
* <p>
* <b>Note:</b>When <code>UCAL_WALLTIME_NEXT_VALID</code> is not a valid
* option for this. When the argument is neither <code>UCAL_WALLTIME_FIRST</code>
* nor <code>UCAL_WALLTIME_LAST</code>, this method has no effect and will keep
* the current setting.
*
* @param option the behavior for handling repeating wall time, either
* <code>UCAL_WALLTIME_FIRST</code> or <code>UCAL_WALLTIME_LAST</code>.
* @see #getRepeatedWallTimeOption
* @draft ICU 49
*/
void setRepeatedWallTimeOption(UCalendarWallTimeOption option);
/**
* Gets the behavior for handling wall time repeating multiple times
* at negative time zone offset transitions.
*
* @return the behavior for handling repeating wall time, either
* <code>UCAL_WALLTIME_FIRST</code> or <code>UCAL_WALLTIME_LAST</code>.
* @see #setRepeatedWallTimeOption
* @draft ICU 49
*/
UCalendarWallTimeOption getRepeatedWallTimeOption(void) const;
/**
* Sets the behavior for handling skipped wall time at positive time zone offset
* transitions. For example, 2:30 AM on March 13, 2011 in US Eastern time (America/New_York)
* does not exist because the wall time jump from 1:59 AM EST to 3:00 AM EDT. When
* <code>UCAL_WALLTIME_FIRST</code> is used, 2:30 AM is interpreted as 30 minutes before 3:00 AM
* EDT, therefore, it will be resolved as 1:30 AM EST. When <code>UCAL_WALLTIME_LAST</code>
* is used, 2:30 AM is interpreted as 31 minutes after 1:59 AM EST, therefore, it will be
* resolved as 3:30 AM EDT. When <code>UCAL_WALLTIME_NEXT_VALID</code> is used, 2:30 AM will
* be resolved as next valid wall time, that is 3:00 AM EDT. The default value is
* <code>UCAL_WALLTIME_LAST</code>.
* <p>
* <b>Note:</b>This option is effective only when this calendar is lenient.
* When the calendar is strict, such non-existing wall time will cause an error.
*
* @param option the behavior for handling skipped wall time at positive time zone
* offset transitions, one of <code>UCAL_WALLTIME_FIRST</code>, <code>UCAL_WALLTIME_LAST</code> and
* <code>UCAL_WALLTIME_NEXT_VALID</code>.
* @see #getSkippedWallTimeOption
*
* @draft ICU 49
*/
void setSkippedWallTimeOption(UCalendarWallTimeOption option);
/**
* Gets the behavior for handling skipped wall time at positive time zone offset
* transitions.
*
* @return the behavior for handling skipped wall time, one of
* <code>UCAL_WALLTIME_FIRST</code>, <code>UCAL_WALLTIME_LAST</code>
* and <code>UCAL_WALLTIME_NEXT_VALID</code>.
* @see #setSkippedWallTimeOption
* @draft ICU 49
*/
UCalendarWallTimeOption getSkippedWallTimeOption(void) const;
#ifndef U_HIDE_DEPRECATED_API
/**
* Sets what the first day of the week is; e.g., Sunday in US, Monday in France.
@ -2010,6 +2079,18 @@ private:
*/
TimeZone* fZone;
/**
* Option for rpeated wall time
* @see #setRepeatedWallTimeOption
*/
UCalendarWallTimeOption fRepeatedWallTime;
/**
* Option for skipped wall time
* @see #setSkippedWallTimeOption
*/
UCalendarWallTimeOption fSkippedWallTime;
/**
* Both firstDayOfWeek and minimalDaysInFirstWeek are locale-dependent. They are
* used to figure out the week count for a specific date for a given locale. These
@ -2261,6 +2342,13 @@ private:
*/
const char* getLocaleID(ULocDataLocaleType type, UErrorCode &status) const;
#endif /* U_HIDE_INTERNAL_API */
private:
/**
* Cast TimeZone used by this object to BasicTimeZone, or NULL if the TimeZone
* is not an instance of BasicTimeZone.
*/
BasicTimeZone* getBasicTimeZone() const;
};
// -------------------------------------

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1996-2011, International Business Machines Corporation and
* Copyright (C) 1996-2012, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
@ -851,24 +851,75 @@ ucal_getGregorianChange(const UCalendar *cal, UErrorCode *pErrorCode);
* @stable ICU 2.0
*/
enum UCalendarAttribute {
/** Lenient parsing */
/**
* Lenient parsing
* @stable ICU 2.0
*/
UCAL_LENIENT,
/** First day of week */
/**
* First day of week
* @stable ICU 2.0
*/
UCAL_FIRST_DAY_OF_WEEK,
/** Minimum number of days in first week */
UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
/**
* Minimum number of days in first week
* @stable ICU 2.0
*/
UCAL_MINIMAL_DAYS_IN_FIRST_WEEK,
/**
* The behavior for handling wall time repeating multiple times
* at negative time zone offset transitions
* @draft ICU 49
*/
UCAL_REPEATED_WALL_TIME,
/**
* The behavior for handling skipped wall time at positive time
* zone offset transitions.
* @draft ICU 49
*/
UCAL_SKIPPED_WALL_TIME
};
/** @stable ICU 2.0 */
typedef enum UCalendarAttribute UCalendarAttribute;
/**
* Options for handling ambiguous wall time at time zone
* offset transitions.
* @draft ICU 49
*/
enum UCalendarWallTimeOption {
/**
* An ambiguous wall time to be interpreted as the latest.
* This option is valid for UCAL_REPEATED_WALL_TIME and
* UCAL_SKIPPED_WALL_TIME.
* @draft ICU 49
*/
UCAL_WALLTIME_LAST,
/**
* An ambiguous wall time to be interpreted as the earliest.
* This option is valid for UCAL_REPEATED_WALL_TIME and
* UCAL_SKIPPED_WALL_TIME.
* @draft ICU 49
*/
UCAL_WALLTIME_FIRST,
/**
* An ambiguous wall time to be interpreted as the next valid
* wall time. This option is valid for UCAL_SKIPPED_WALL_TIME.
* @draft ICU 49
*/
UCAL_WALLTIME_NEXT_VALID
};
/** @draft ICU 49 */
typedef enum UCalendarWallTimeOption UCalendarWallTimeOption;
/**
* Get a numeric attribute associated with a UCalendar.
* Numeric attributes include the first day of the week, or the minimal numbers
* of days in the first week of the month.
* @param cal The UCalendar to query.
* @param attr The desired attribute; one of UCAL_LENIENT, UCAL_FIRST_DAY_OF_WEEK,
* or UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
* UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, UCAL_REPEATED_WALL_TIME or UCAL_SKIPPED_WALL_TIME
* @return The value of attr.
* @see ucal_setAttribute
* @stable ICU 2.0
@ -883,7 +934,7 @@ ucal_getAttribute(const UCalendar* cal,
* of days in the first week of the month.
* @param cal The UCalendar to set.
* @param attr The desired attribute; one of UCAL_LENIENT, UCAL_FIRST_DAY_OF_WEEK,
* or UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
* UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, UCAL_REPEATED_WALL_TIME or UCAL_SKIPPED_WALL_TIME
* @param newValue The new value of attr.
* @see ucal_getAttribute
* @stable ICU 2.0

View file

@ -1,5 +1,5 @@
/********************************************************************
* Copyright (c) 1997-2011, International Business Machines
* Copyright (c) 1997-2012, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************
*
@ -49,6 +49,7 @@ void addCalTest(TestNode** root)
addTest(root, &TestGetKeywordValuesForLocale, "tsformat/ccaltst/TestGetKeywordValuesForLocale");
addTest(root, &TestWeekend, "tsformat/ccaltst/TestWeekend");
addTest(root, &TestFieldDifference, "tsformat/ccaltst/TestFieldDifference");
addTest(root, &TestAmbiguousWallTime, "tsformat/ccaltst/TestAmbiguousWallTime");
}
/* "GMT" */
@ -1783,4 +1784,104 @@ void TestFieldDifference() {
}
}
void TestAmbiguousWallTime() {
UErrorCode status = U_ZERO_ERROR;
UChar tzID[32];
UCalendar* ucal;
UDate t, expected;
u_uastrcpy(tzID, "America/New_York");
ucal = ucal_open(tzID, -1, NULL, UCAL_DEFAULT, &status);
if (U_FAILURE(status)) {
log_err("FAIL: Failed to create a calendar");
return;
}
if (ucal_getAttribute(ucal, UCAL_REPEATED_WALL_TIME) != UCAL_WALLTIME_LAST) {
log_err("FAIL: Default UCAL_REPEATED_WALL_TIME value is not UCAL_WALLTIME_LAST");
}
if (ucal_getAttribute(ucal, UCAL_SKIPPED_WALL_TIME) != UCAL_WALLTIME_LAST) {
log_err("FAIL: Default UCAL_SKIPPED_WALL_TIME value is not UCAL_WALLTIME_LAST");
}
/* UCAL_WALLTIME_FIRST on US fall transition */
ucal_setAttribute(ucal, UCAL_REPEATED_WALL_TIME, UCAL_WALLTIME_FIRST);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 11-1, 6, 1, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
expected = 1320557400000.0; /* 2011-11-06T05:30:00Z */
if (U_FAILURE(status)) {
log_err("FAIL: Calculating time 2011-11-06 01:30:00 with UCAL_WALLTIME_FIRST - %s\n", u_errorName(status));
status = U_ZERO_ERROR;
} else if (t != expected) {
log_err("FAIL: 2011-11-06 01:30:00 with UCAL_WALLTIME_FIRST - got: %f, expected: %f\n", t, expected);
}
/* UCAL_WALLTIME_LAST on US fall transition */
ucal_setAttribute(ucal, UCAL_REPEATED_WALL_TIME, UCAL_WALLTIME_LAST);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 11-1, 6, 1, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
expected = 1320561000000.0; /* 2011-11-06T06:30:00Z */
if (U_FAILURE(status)) {
log_err("FAIL: Calculating time 2011-11-06 01:30:00 with UCAL_WALLTIME_LAST - %s\n", u_errorName(status));
status = U_ZERO_ERROR;
} else if (t != expected) {
log_err("FAIL: 2011-11-06 01:30:00 with UCAL_WALLTIME_LAST - got: %f, expected: %f\n", t, expected);
}
/* UCAL_WALLTIME_FIRST on US spring transition */
ucal_setAttribute(ucal, UCAL_SKIPPED_WALL_TIME, UCAL_WALLTIME_FIRST);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 3-1, 13, 2, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
expected = 1299997800000.0; /* 2011-03-13T06:30:00Z */
if (U_FAILURE(status)) {
log_err("FAIL: Calculating time 2011-03-13 02:30:00 with UCAL_WALLTIME_FIRST - %s\n", u_errorName(status));
status = U_ZERO_ERROR;
} else if (t != expected) {
log_err("FAIL: 2011-03-13 02:30:00 with UCAL_WALLTIME_FIRST - got: %f, expected: %f\n", t, expected);
}
/* UCAL_WALLTIME_LAST on US spring transition */
ucal_setAttribute(ucal, UCAL_SKIPPED_WALL_TIME, UCAL_WALLTIME_LAST);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 3-1, 13, 2, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
expected = 1300001400000.0; /* 2011-03-13T07:30:00Z */
if (U_FAILURE(status)) {
log_err("FAIL: Calculating time 2011-03-13 02:30:00 with UCAL_WALLTIME_LAST - %s\n", u_errorName(status));
status = U_ZERO_ERROR;
} else if (t != expected) {
log_err("FAIL: 2011-03-13 02:30:00 with UCAL_WALLTIME_LAST - got: %f, expected: %f\n", t, expected);
}
/* UCAL_WALLTIME_NEXT_VALID on US spring transition */
ucal_setAttribute(ucal, UCAL_SKIPPED_WALL_TIME, UCAL_WALLTIME_NEXT_VALID);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 3-1, 13, 2, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
expected = 1299999600000.0; /* 2011-03-13T07:00:00Z */
if (U_FAILURE(status)) {
log_err("FAIL: Calculating time 2011-03-13 02:30:00 with UCAL_WALLTIME_NEXT_VALID - %s\n", u_errorName(status));
status = U_ZERO_ERROR;
} else if (t != expected) {
log_err("FAIL: 2011-03-13 02:30:00 with UCAL_WALLTIME_NEXT_VALID - got: %f, expected: %f\n", t, expected);
}
/* non-lenient on US spring transition */
ucal_setAttribute(ucal, UCAL_LENIENT, 0);
ucal_clear(ucal);
ucal_setDateTime(ucal, 2011, 3-1, 13, 2, 30, 0, &status);
t = ucal_getMillis(ucal, &status);
if (U_SUCCESS(status)) {
/* must return error */
log_err("FAIL: Non-lenient did not fail with 2011-03-13 02:30:00\n");
status = U_ZERO_ERROR;
}
ucal_close(ucal);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2010, International Business Machines Corporation and
* Copyright (c) 1997-2012, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
/********************************************************************************
@ -64,6 +64,10 @@
* Test weekend-related APIs
*/
static void TestWeekend(void);
/**
* Test ambiguous wall time
*/
static void TestAmbiguousWallTime(void);
/*Internal functions used*/
/**

View file

@ -1,6 +1,6 @@
/************************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2011, International Business Machines Corporation
* Copyright (c) 1997-2012, International Business Machines Corporation
* and others. All Rights Reserved.
************************************************************************/
@ -244,6 +244,27 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
TestISO8601();
}
break;
case 27:
name = "TestAmbiguousWallTimeAPIs";
if(exec) {
logln("TestAmbiguousWallTimeAPIs---"); logln("");
TestAmbiguousWallTimeAPIs();
}
break;
case 28:
name = "TestRepeatedWallTime";
if(exec) {
logln("TestRepeatedWallTime---"); logln("");
TestRepeatedWallTime();
}
break;
case 29:
name = "TestSkippedWallTime";
if(exec) {
logln("TestSkippedWallTime---"); logln("");
TestSkippedWallTime();
}
break;
default: name = ""; break;
}
}
@ -2257,6 +2278,377 @@ void CalendarTest::TestISO8601() {
}
void
CalendarTest::TestAmbiguousWallTimeAPIs(void) {
UErrorCode status = U_ZERO_ERROR;
Calendar* cal = Calendar::createInstance(status);
if (U_FAILURE(status)) {
errln("Fail: Error creating a calendar instance.");
return;
}
if (cal->getRepeatedWallTimeOption() != UCAL_WALLTIME_LAST) {
errln("Fail: Default repeted time option is not UCAL_WALLTIME_LAST");
}
if (cal->getSkippedWallTimeOption() != UCAL_WALLTIME_LAST) {
errln("Fail: Default skipped time option is not UCAL_WALLTIME_LAST");
}
Calendar* cal2 = cal->clone();
if (*cal != *cal2) {
errln("Fail: Cloned calendar != the original");
}
if (!cal->equals(*cal2, status)) {
errln("Fail: The time of cloned calendar is not equal to the original");
} else if (U_FAILURE(status)) {
errln("Fail: Error equals");
}
status = U_ZERO_ERROR;
cal2->setRepeatedWallTimeOption(UCAL_WALLTIME_FIRST);
cal2->setSkippedWallTimeOption(UCAL_WALLTIME_FIRST);
if (*cal == *cal2) {
errln("Fail: Cloned and modified calendar == the original");
}
if (!cal->equals(*cal2, status)) {
errln("Fail: The time of cloned calendar is not equal to the original after changing wall time options");
} else if (U_FAILURE(status)) {
errln("Fail: Error equals after changing wall time options");
}
status = U_ZERO_ERROR;
if (cal2->getRepeatedWallTimeOption() != UCAL_WALLTIME_FIRST) {
errln("Fail: Repeted time option is not UCAL_WALLTIME_FIRST");
}
if (cal2->getSkippedWallTimeOption() != UCAL_WALLTIME_FIRST) {
errln("Fail: Skipped time option is not UCAL_WALLTIME_FIRST");
}
cal2->setRepeatedWallTimeOption(UCAL_WALLTIME_NEXT_VALID);
if (cal2->getRepeatedWallTimeOption() != UCAL_WALLTIME_FIRST) {
errln("Fail: Repeated wall time option was updated other than UCAL_WALLTIME_FIRST");
}
delete cal;
delete cal2;
}
class CalFields {
public:
CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec);
CalFields(const Calendar& cal, UErrorCode& status);
void setTo(Calendar& cal) const;
char* toString(char* buf, int32_t len) const;
UBool operator==(const CalFields& rhs) const;
UBool operator!=(const CalFields& rhs) const;
private:
int32_t year;
int32_t month;
int32_t day;
int32_t hour;
int32_t min;
int32_t sec;
};
CalFields::CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec)
: year(year), month(month), day(day), hour(hour), min(min), sec(sec) {
}
CalFields::CalFields(const Calendar& cal, UErrorCode& status) {
year = cal.get(UCAL_YEAR, status);
month = cal.get(UCAL_MONTH, status) + 1;
day = cal.get(UCAL_DAY_OF_MONTH, status);
hour = cal.get(UCAL_HOUR_OF_DAY, status);
min = cal.get(UCAL_MINUTE, status);
sec = cal.get(UCAL_SECOND, status);
}
void
CalFields::setTo(Calendar& cal) const {
cal.clear();
cal.set(year, month - 1, day, hour, min, sec);
}
char*
CalFields::toString(char* buf, int32_t len) const {
char local[32];
sprintf(local, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec);
uprv_strncpy(buf, local, len - 1);
buf[len - 1] = 0;
return buf;
}
UBool
CalFields::operator==(const CalFields& rhs) const {
return year == rhs.year
&& month == rhs.month
&& day == rhs.day
&& hour == rhs.hour
&& min == rhs.min
&& sec == rhs.sec;
}
UBool
CalFields::operator!=(const CalFields& rhs) const {
return !(*this == rhs);
}
typedef struct {
const char* tzid;
const CalFields in;
const CalFields expLastGMT;
const CalFields expFirstGMT;
} RepeatedWallTimeTestData;
static const RepeatedWallTimeTestData RPDATA[] =
{
// Time zone Input wall time WALLTIME_LAST in GMT WALLTIME_FIRST in GMT
{"America/New_York", CalFields(2011,11,6,0,59,59), CalFields(2011,11,6,4,59,59), CalFields(2011,11,6,4,59,59)},
{"America/New_York", CalFields(2011,11,6,1,0,0), CalFields(2011,11,6,6,0,0), CalFields(2011,11,6,5,0,0)},
{"America/New_York", CalFields(2011,11,6,1,0,1), CalFields(2011,11,6,6,0,1), CalFields(2011,11,6,5,0,1)},
{"America/New_York", CalFields(2011,11,6,1,30,0), CalFields(2011,11,6,6,30,0), CalFields(2011,11,6,5,30,0)},
{"America/New_York", CalFields(2011,11,6,1,59,59), CalFields(2011,11,6,6,59,59), CalFields(2011,11,6,5,59,59)},
{"America/New_York", CalFields(2011,11,6,2,0,0), CalFields(2011,11,6,7,0,0), CalFields(2011,11,6,7,0,0)},
{"America/New_York", CalFields(2011,11,6,2,0,1), CalFields(2011,11,6,7,0,1), CalFields(2011,11,6,7,0,1)},
{"Australia/Lord_Howe", CalFields(2011,4,3,1,29,59), CalFields(2011,4,2,14,29,59), CalFields(2011,4,2,14,29,59)},
{"Australia/Lord_Howe", CalFields(2011,4,3,1,30,0), CalFields(2011,4,2,15,0,0), CalFields(2011,4,2,14,30,0)},
{"Australia/Lord_Howe", CalFields(2011,4,3,1,45,0), CalFields(2011,4,2,15,15,0), CalFields(2011,4,2,14,45,0)},
{"Australia/Lord_Howe", CalFields(2011,4,3,1,59,59), CalFields(2011,4,2,15,29,59), CalFields(2011,4,2,14,59,59)},
{"Australia/Lord_Howe", CalFields(2011,4,3,2,0,0), CalFields(2011,4,2,15,30,0), CalFields(2011,4,2,15,30,0)},
{"Australia/Lord_Howe", CalFields(2011,4,3,2,0,1), CalFields(2011,4,2,15,30,1), CalFields(2011,4,2,15,30,1)},
{NULL, CalFields(0,0,0,0,0,0), CalFields(0,0,0,0,0,0), CalFields(0,0,0,0,0,0)}
};
void CalendarTest::TestRepeatedWallTime(void) {
UErrorCode status = U_ZERO_ERROR;
GregorianCalendar calGMT((const TimeZone&)*TimeZone::getGMT(), status);
GregorianCalendar calDefault(status);
GregorianCalendar calLast(status);
GregorianCalendar calFirst(status);
if (U_FAILURE(status)) {
errln("Fail: Failed to create a calendar object.");
return;
}
calLast.setRepeatedWallTimeOption(UCAL_WALLTIME_LAST);
calFirst.setRepeatedWallTimeOption(UCAL_WALLTIME_FIRST);
for (int32_t i = 0; RPDATA[i].tzid != NULL; i++) {
char buf[32];
TimeZone *tz = TimeZone::createTimeZone(RPDATA[i].tzid);
// UCAL_WALLTIME_LAST
status = U_ZERO_ERROR;
calLast.setTimeZone(*tz);
RPDATA[i].in.setTo(calLast);
calGMT.setTime(calLast.getTime(status), status);
CalFields outLastGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_LAST) - ")
+ RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
} else {
if (outLastGMT != RPDATA[i].expLastGMT) {
errln(UnicodeString("Fail: UCAL_WALLTIME_LAST ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
+ outLastGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
// default
status = U_ZERO_ERROR;
calDefault.setTimeZone(*tz);
RPDATA[i].in.setTo(calDefault);
calGMT.setTime(calDefault.getTime(status), status);
CalFields outDefGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (default) - ")
+ RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
} else {
if (outDefGMT != RPDATA[i].expLastGMT) {
errln(UnicodeString("Fail: (default) ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
+ outDefGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
// UCAL_WALLTIME_FIRST
status = U_ZERO_ERROR;
calFirst.setTimeZone(*tz);
RPDATA[i].in.setTo(calFirst);
calGMT.setTime(calFirst.getTime(status), status);
CalFields outFirstGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_FIRST) - ")
+ RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
} else {
if (outFirstGMT != RPDATA[i].expFirstGMT) {
errln(UnicodeString("Fail: UCAL_WALLTIME_FIRST ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
+ outFirstGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expFirstGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
delete tz;
}
}
typedef struct {
const char* tzid;
const CalFields in;
UBool isValid;
const CalFields expLastGMT;
const CalFields expFirstGMT;
const CalFields expNextAvailGMT;
} SkippedWallTimeTestData;
static SkippedWallTimeTestData SKDATA[] =
{
// Time zone Input wall time valid? WALLTIME_LAST in GMT WALLTIME_FIRST in GMT WALLTIME_NEXT_VALID in GMT
{"America/New_York", CalFields(2011,3,13,1,59,59), TRUE, CalFields(2011,3,13,6,59,59), CalFields(2011,3,13,6,59,59), CalFields(2011,3,13,6,59,59)},
{"America/New_York", CalFields(2011,3,13,2,0,0), FALSE, CalFields(2011,3,13,7,0,0), CalFields(2011,3,13,6,0,0), CalFields(2011,3,13,7,0,0)},
{"America/New_York", CalFields(2011,3,13,2,1,0), FALSE, CalFields(2011,3,13,7,1,0), CalFields(2011,3,13,6,1,0), CalFields(2011,3,13,7,0,0)},
{"America/New_York", CalFields(2011,3,13,2,30,0), FALSE, CalFields(2011,3,13,7,30,0), CalFields(2011,3,13,6,30,0), CalFields(2011,3,13,7,0,0)},
{"America/New_York", CalFields(2011,3,13,2,59,59), FALSE, CalFields(2011,3,13,7,59,59), CalFields(2011,3,13,6,59,59), CalFields(2011,3,13,7,0,0)},
{"America/New_York", CalFields(2011,3,13,3,0,0), TRUE, CalFields(2011,3,13,7,0,0), CalFields(2011,3,13,7,0,0), CalFields(2011,3,13,7,0,0)},
{"Pacific/Apia", CalFields(2011,12,29,23,59,59), TRUE, CalFields(2011,12,30,9,59,59), CalFields(2011,12,30,9,59,59), CalFields(2011,12,30,9,59,59)},
{"Pacific/Apia", CalFields(2011,12,30,0,0,0), FALSE, CalFields(2011,12,30,10,0,0), CalFields(2011,12,29,10,0,0), CalFields(2011,12,30,10,0,0)},
{"Pacific/Apia", CalFields(2011,12,30,12,0,0), FALSE, CalFields(2011,12,30,22,0,0), CalFields(2011,12,29,22,0,0), CalFields(2011,12,30,10,0,0)},
{"Pacific/Apia", CalFields(2011,12,30,23,59,59), FALSE, CalFields(2011,12,31,9,59,59), CalFields(2011,12,30,9,59,59), CalFields(2011,12,30,10,0,0)},
{"Pacific/Apia", CalFields(2011,12,31,0,0,0), TRUE, CalFields(2011,12,30,10,0,0), CalFields(2011,12,30,10,0,0), CalFields(2011,12,30,10,0,0)},
{NULL, CalFields(0,0,0,0,0,0), TRUE, CalFields(0,0,0,0,0,0), CalFields(0,0,0,0,0,0), CalFields(0,0,0,0,0,0)}
};
void CalendarTest::TestSkippedWallTime(void) {
UErrorCode status = U_ZERO_ERROR;
GregorianCalendar calGMT((const TimeZone&)*TimeZone::getGMT(), status);
GregorianCalendar calDefault(status);
GregorianCalendar calLast(status);
GregorianCalendar calFirst(status);
GregorianCalendar calNextAvail(status);
if (U_FAILURE(status)) {
errln("Fail: Failed to create a calendar object.");
return;
}
calLast.setSkippedWallTimeOption(UCAL_WALLTIME_LAST);
calFirst.setSkippedWallTimeOption(UCAL_WALLTIME_FIRST);
calNextAvail.setSkippedWallTimeOption(UCAL_WALLTIME_NEXT_VALID);
for (int32_t i = 0; SKDATA[i].tzid != NULL; i++) {
UDate d;
char buf[32];
TimeZone *tz = TimeZone::createTimeZone(SKDATA[i].tzid);
for (int32_t j = 0; j < 2; j++) {
UBool bLenient = (j == 0);
// UCAL_WALLTIME_LAST
status = U_ZERO_ERROR;
calLast.setLenient(bLenient);
calLast.setTimeZone(*tz);
SKDATA[i].in.setTo(calLast);
d = calLast.getTime(status);
if (bLenient || SKDATA[i].isValid) {
calGMT.setTime(d, status);
CalFields outLastGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_LAST) - ")
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
} else {
if (outLastGMT != SKDATA[i].expLastGMT) {
errln(UnicodeString("Fail: UCAL_WALLTIME_LAST ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
+ outLastGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
} else if (U_SUCCESS(status)) {
// strict, invalid wall time - must report an error
errln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_LAST)") +
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
}
// default
status = U_ZERO_ERROR;
calDefault.setLenient(bLenient);
calDefault.setTimeZone(*tz);
SKDATA[i].in.setTo(calDefault);
d = calDefault.getTime(status);
if (bLenient || SKDATA[i].isValid) {
calGMT.setTime(d, status);
CalFields outDefGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calDefault/calGMT (default) - ")
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
} else {
if (outDefGMT != SKDATA[i].expLastGMT) {
errln(UnicodeString("Fail: (default) ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
+ outDefGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
} else if (U_SUCCESS(status)) {
// strict, invalid wall time - must report an error
errln(UnicodeString("Fail: An error expected (default)") +
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
}
// UCAL_WALLTIME_FIRST
status = U_ZERO_ERROR;
calFirst.setLenient(bLenient);
calFirst.setTimeZone(*tz);
SKDATA[i].in.setTo(calFirst);
d = calFirst.getTime(status);
if (bLenient || SKDATA[i].isValid) {
calGMT.setTime(d, status);
CalFields outFirstGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calFirst/calGMT (UCAL_WALLTIME_FIRST) - ")
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
} else {
if (outFirstGMT != SKDATA[i].expFirstGMT) {
errln(UnicodeString("Fail: UCAL_WALLTIME_FIRST ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
+ outFirstGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expFirstGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
} else if (U_SUCCESS(status)) {
// strict, invalid wall time - must report an error
errln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_FIRST)") +
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
}
// UCAL_WALLTIME_NEXT_VALID
status = U_ZERO_ERROR;
calNextAvail.setLenient(bLenient);
calNextAvail.setTimeZone(*tz);
SKDATA[i].in.setTo(calNextAvail);
d = calNextAvail.getTime(status);
if (bLenient || SKDATA[i].isValid) {
calGMT.setTime(d, status);
CalFields outNextAvailGMT(calGMT, status);
if (U_FAILURE(status)) {
errln(UnicodeString("Fail: Failed to get/set time calNextAvail/calGMT (UCAL_WALLTIME_NEXT_VALID) - ")
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
} else {
if (outNextAvailGMT != SKDATA[i].expNextAvailGMT) {
errln(UnicodeString("Fail: UCAL_WALLTIME_NEXT_VALID ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
+ outNextAvailGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expNextAvailGMT.toString(buf, sizeof(buf)) + "[GMT]");
}
}
} else if (U_SUCCESS(status)) {
// strict, invalid wall time - must report an error
errln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_NEXT_VALID)") +
+ SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
}
}
delete tz;
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof

View file

@ -1,5 +1,5 @@
/***********************************************************************
* Copyright (c) 1997-2011, International Business Machines Corporation
* Copyright (c) 1997-2012, International Business Machines Corporation
* and others. All Rights Reserved.
***********************************************************************/
@ -228,6 +228,13 @@ public: // package
* Test the ISO8601 calendar type
*/
void TestISO8601(void);
/**
* Test cases for [set|get][Repeated|Skipped]WallTimeOption
*/
void TestAmbiguousWallTimeAPIs(void);
void TestRepeatedWallTime(void);
void TestSkippedWallTime(void);
};
#endif /* #if !UCONFIG_NO_FORMATTING */