ICU-9606 Add ucal_getTimeZoneTransitionDate, UTimeZoneTransitionType and tests

X-SVN-Rev: 32545
This commit is contained in:
Peter Edberg 2012-10-08 00:32:59 +00:00
parent 6a81ec8327
commit 3e36b67cf7
3 changed files with 166 additions and 0 deletions

View file

@ -727,4 +727,32 @@ ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool
return en;
}
U_CAPI UBool U_EXPORT2
ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType type,
UDate* transition, UErrorCode* status)
{
if (U_FAILURE(*status)) {
return FALSE;
}
UDate base = ((Calendar*)cal)->getTime(*status);
const TimeZone& tz = ((Calendar*)cal)->getTimeZone();
// The reference returned from Calendar::getTimeZone() "is only valid until clients
// make another call to adoptTimeZone or setTimeZone, or this Calendar is destroyed."
// Add a mutex lock until after we clone (or fail)?
const BasicTimeZone * btz = dynamic_cast<const BasicTimeZone *>(&tz);
if (btz != NULL && U_SUCCESS(*status)) {
TimeZoneTransition tzt;
BasicTimeZone * btzClone = static_cast<BasicTimeZone *>(btz->clone()); // getNext/PreviousTransition are non-const
UBool inclusive = (type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE || type == UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE);
UBool result = (type == UCAL_TZ_TRANSITION_NEXT || type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE)?
btzClone->getNextTransition(base, inclusive, tzt):
btzClone->getPreviousTransition(base, inclusive, tzt);
if (result) {
*transition = tzt.getTime();
return TRUE;
}
}
return FALSE;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1432,6 +1432,62 @@ ucal_getFieldDifference(UCalendar* cal,
UCalendarDateFields field,
UErrorCode* status);
#ifndef U_HIDE_DRAFT_API
/**
* Time zone transition types for ucal_getTimeZoneTransitionDate
* @draft ICU 50
*/
enum UTimeZoneTransitionType {
/**
* Get the next transition after the current date,
* i.e. excludes the current date
* @draft ICU 50
*/
UCAL_TZ_TRANSITION_NEXT,
/**
* Get the next transition on or after the current date,
* i.e. may include the current date
* @draft ICU 50
*/
UCAL_TZ_TRANSITION_NEXT_INCLUSIVE,
/**
* Get the previous transition before the current date,
* i.e. excludes the current date
* @draft ICU 50
*/
UCAL_TZ_TRANSITION_PREVIOUS,
/**
* Get the previous transition on or before the current date,
* i.e. may include the current date
* @draft ICU 50
*/
UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE
};
/** @draft ICU 50 */
typedef enum UTimeZoneTransitionType UTimeZoneTransitionType;
/**
* Get the UDate for the next/previous time zone transition relative to
* the calendar's current date, in the time zone to which the calendar
* is currently set. If there is no known time zone transition of the
* requested type relative to the calendar's date, the function returns
* FALSE.
* @param cal The UCalendar to query.
* @param type The type of transition desired.
* @param transition A pointer to a UDate to be set to the transition time.
* If the function returns FALSE, the value set is unspecified.
* @param status A pointer to a UErrorCode to receive any errors.
* @return TRUE if a valid transition time is set in *transition, FALSE
* otherwise.
* @draft ICU 50
*/
U_DRAFT UBool U_EXPORT2
ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType type,
UDate* transition, UErrorCode* status);
#endif /* U_HIDE_DRAFT_API */
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif

View file

@ -33,6 +33,7 @@
void TestGregorianChange(void);
void TestFieldDifference(void);
void TestAddRollEra0AndEraBounds(void);
void TestGetTZTransition(void);
void addCalTest(TestNode** root);
@ -52,6 +53,7 @@ void addCalTest(TestNode** root)
addTest(root, &TestFieldDifference, "tsformat/ccaltst/TestFieldDifference");
addTest(root, &TestAmbiguousWallTime, "tsformat/ccaltst/TestAmbiguousWallTime");
addTest(root, &TestAddRollEra0AndEraBounds, "tsformat/ccaltst/TestAddRollEra0AndEraBounds");
addTest(root, &TestGetTZTransition, "tsformat/ccaltst/TestGetTZTransition");
}
/* "GMT" */
@ -2186,4 +2188,84 @@ void TestAddRollEra0AndEraBounds() {
}
}
/**
* TestGetTZTransition, for #9606
*/
typedef struct {
const char *descrip; /* test description */
const UChar * zoneName; /* pointer to zero-terminated zone name */
int32_t year; /* starting point for test is gregorian calendar noon on day specified by y,M,d here */
int32_t month;
int32_t day;
UBool hasPrev; /* does it have a previous transition from starting point? If so we test inclusive from that */
UBool hasNext; /* does it have a next transition from starting point? If so we test inclusive from that */
} TZTransitionItem;
/* have zoneGMT above */
static const UChar zoneUSPacific[] = { 0x55,0x53,0x2F,0x50,0x61,0x63,0x69,0x66,0x69,0x63,0 }; /* "US/Pacific" */
static const UChar zoneCairo[] = { 0x41,0x66,0x72,0x69,0x63,0x61,0x2F,0x43,0x61,0x69,0x72,0x6F,0 }; /* "Africa/Cairo", DST cancelled since 2011 */
static const UChar zoneIceland[] = { 0x41,0x74,0x6C,0x61,0x6E,0x74,0x69,0x63,0x2F,0x52,0x65,0x79,0x6B,0x6A,0x61,0x76,0x69,0x6B,0 }; /* "Atlantic/Reykjavik", always on DST (since when?) */
static const TZTransitionItem tzTransitionItems[] = {
{ "USPacific mid 2012", zoneUSPacific, 2012, UCAL_JULY, 1, TRUE , TRUE },
{ "USPacific mid 100", zoneUSPacific, 100, UCAL_JULY, 1, FALSE, TRUE }, /* no transitions before 100 CE... */
{ "Cairo mid 2012", zoneCairo, 2012, UCAL_JULY, 1, TRUE , FALSE }, /* DST cancelled since 2011 */
{ "Iceland mid 2012", zoneIceland, 2012, UCAL_JULY, 1, TRUE , FALSE }, /* always on DST */
{ NULL, NULL, 0, 0, 0, FALSE, FALSE } /* terminator */
};
void TestGetTZTransition() {
UErrorCode status = U_ZERO_ERROR;
UCalendar * ucal = ucal_open(zoneGMT, -1, "en", UCAL_GREGORIAN, &status);
if ( U_SUCCESS(status) ) {
const TZTransitionItem * itemPtr;
for (itemPtr = tzTransitionItems; itemPtr->descrip != NULL; itemPtr++) {
UDate curMillis;
ucal_setTimeZone(ucal, itemPtr->zoneName, -1, &status);
ucal_setDateTime(ucal, itemPtr->year, itemPtr->month, itemPtr->day, 12, 0, 0, &status);
curMillis = ucal_getMillis(ucal, &status);
if ( U_SUCCESS(status) ) {
UDate transition1, transition2;
UBool result;
result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_PREVIOUS, &transition1, &status);
if (U_FAILURE(status) || result != itemPtr->hasPrev) {
log_err("FAIL: %s ucal_getTimeZoneTransitionDate prev status %s, expected result %d but got %d\n",
itemPtr->descrip, u_errorName(status), itemPtr->hasPrev, result);
} else if (result) {
ucal_setMillis(ucal, transition1, &status);
result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE, &transition2, &status);
if (U_FAILURE(status) || !result || transition2 != transition1) {
log_err("FAIL: %s ucal_getTimeZoneTransitionDate prev_inc status %s, result %d, expected date %.1f but got %.1f\n",
itemPtr->descrip, u_errorName(status), result, transition1, transition2);
}
}
status = U_ZERO_ERROR;
result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_NEXT, &transition1, &status);
if (U_FAILURE(status) || result != itemPtr->hasNext) {
log_err("FAIL: %s ucal_getTimeZoneTransitionDate next status %s, expected result %d but got %d\n",
itemPtr->descrip, u_errorName(status), itemPtr->hasNext, result);
} else if (result) {
ucal_setMillis(ucal, transition1, &status);
result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_NEXT_INCLUSIVE, &transition2, &status);
if (U_FAILURE(status) || !result || transition2 != transition1) {
log_err("FAIL: %s ucal_getTimeZoneTransitionDate next_inc status %s, result %d, expected date %.1f but got %.1f\n",
itemPtr->descrip, u_errorName(status), result, transition1, transition2);
}
}
status = U_ZERO_ERROR;
} else {
log_data_err("FAIL setup: can't setup calendar for %s, status %s\n",
itemPtr->descrip, u_errorName(status));
status = U_ZERO_ERROR;
}
}
ucal_close(ucal);
} else {
log_data_err("FAIL setup: ucal_open status %s\n", u_errorName(status));
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */