mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
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:
parent
06f294319e
commit
9d6a55f33d
8 changed files with 857 additions and 39 deletions
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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*/
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue