ICU-646 wall time support in icu

X-SVN-Rev: 17801
This commit is contained in:
Doug Felt 2005-06-04 03:08:25 +00:00
parent f089092803
commit 50c3a2554b
9 changed files with 394 additions and 111 deletions

View file

@ -92,13 +92,15 @@ static const UChar QUOTE = 0x27; // Single quote
SimpleDateFormat::~SimpleDateFormat()
{
delete fSymbols;
delete parsedTimeZone; // sanity check
}
//----------------------------------------------------------------------
SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
: fLocale(Locale::getDefault()),
fSymbols(NULL)
fSymbols(NULL),
parsedTimeZone(NULL)
{
construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
initializeDefaultCentury();
@ -110,7 +112,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
UErrorCode &status)
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(NULL)
fSymbols(NULL),
parsedTimeZone(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@ -123,7 +126,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
const Locale& locale,
UErrorCode& status)
: fPattern(pattern),
fLocale(locale)
fLocale(locale),
parsedTimeZone(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@ -137,7 +141,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
UErrorCode& status)
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(symbolsToAdopt)
fSymbols(symbolsToAdopt),
parsedTimeZone(NULL)
{
initializeCalendar(NULL,fLocale,status);
initialize(fLocale, status);
@ -151,7 +156,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
UErrorCode& status)
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(new DateFormatSymbols(symbols))
fSymbols(new DateFormatSymbols(symbols)),
parsedTimeZone(NULL)
{
initializeCalendar(NULL, fLocale, status);
initialize(fLocale, status);
@ -166,7 +172,8 @@ SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
const Locale& locale,
UErrorCode& status)
: fLocale(locale),
fSymbols(NULL)
fSymbols(NULL),
parsedTimeZone(NULL)
{
construct(timeStyle, dateStyle, fLocale, status);
if(U_SUCCESS(status)) {
@ -185,7 +192,8 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
UErrorCode& status)
: fPattern(gDefaultPattern),
fLocale(locale),
fSymbols(NULL)
fSymbols(NULL),
parsedTimeZone(NULL)
{
if (U_FAILURE(status)) return;
initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
@ -212,7 +220,8 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
: DateFormat(other),
fSymbols(NULL)
fSymbols(NULL),
parsedTimeZone(NULL)
{
*this = other;
}
@ -226,6 +235,8 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
delete fSymbols;
fSymbols = NULL;
delete parsedTimeZone; parsedTimeZone = NULL;
if (other.fSymbols)
fSymbols = new DateFormatSymbols(*other.fSymbols);
@ -254,11 +265,11 @@ SimpleDateFormat::operator==(const Format& other) const
if (DateFormat::operator==(other)) {
// DateFormat::operator== guarantees following cast is safe
SimpleDateFormat* that = (SimpleDateFormat*)&other;
return (fPattern == that->fPattern &&
return (fPattern == that->fPattern &&
fSymbols != NULL && // Check for pathological object
that->fSymbols != NULL && // Check for pathological object
*fSymbols == *that->fSymbols &&
fHaveDefaultCentury == that->fHaveDefaultCentury &&
that->fSymbols != NULL && // Check for pathological object
*fSymbols == *that->fSymbols &&
fHaveDefaultCentury == that->fHaveDefaultCentury &&
fDefaultCenturyStart == that->fDefaultCenturyStart);
}
return FALSE;
@ -673,43 +684,45 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
break;
// for the "z" symbols, we have to check our time zone data first. If we have a
// localized name for the time zone, then "zzzz" is the whole name and anything
// shorter is the abbreviation (we also have to check for daylight savings time
// since the name will be different). If we don't have a localized time zone name,
// localized name for the time zone, then "zzzz" / "zzz" indicate whether
// daylight time is in effect (long/short) and "zz" / "z" do not (long/short).
// If we don't have a localized time zone name,
// then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
// offset from GMT) regardless of how many z's were in the pattern symbol
case UDAT_TIMEZONE_FIELD: {
UnicodeString str;
int32_t zoneIndex = fSymbols->getZoneIndex(cal.getTimeZone().getID(str));
if (zoneIndex == -1) {
value = cal.get(UCAL_ZONE_OFFSET, status) +
cal.get(UCAL_DST_OFFSET, status);
value = cal.get(UCAL_ZONE_OFFSET, status) +
cal.get(UCAL_DST_OFFSET, status);
if (value < 0) {
appendTo += gGmtMinus;
value = -value; // suppress the '-' sign for text display.
}
else
appendTo += gGmtPlus;
zeroPaddingNumber(appendTo, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
appendTo += (UChar)0x003A /*':'*/;
zeroPaddingNumber(appendTo, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
}
else if (cal.get(UCAL_DST_OFFSET, status) != 0) {
if (count >= 4)
appendTo += fSymbols->fZoneStrings[zoneIndex][3];
else
appendTo += fSymbols->fZoneStrings[zoneIndex][4];
if (value < 0) {
appendTo += gGmtMinus;
value = -value; // suppress the '-' sign for text display.
}
else
appendTo += gGmtPlus;
zeroPaddingNumber(appendTo, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
appendTo += (UChar)0x003A /*':'*/;
zeroPaddingNumber(appendTo, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
}
else {
if (count >= 4)
appendTo += fSymbols->fZoneStrings[zoneIndex][1];
else
appendTo += fSymbols->fZoneStrings[zoneIndex][2];
int ix;
int zsrc = fSymbols->fZoneStringsColCount;
if (zsrc < 7 && count < 3) {
count += 2; // no generic time, default to full times
}
switch (count) {
case 1: ix = zsrc == 7 ? 6 : 7; break; // short generic time
case 2: ix = zsrc == 7 ? 5 : 6; break; // long generic time
case 3: ix = cal.get(UCAL_DST_OFFSET, status) != 0 ? 4 : 2; break; // short dst/std time
default: ix = cal.get(UCAL_DST_OFFSET, status) != 0 ? 3 : 1; break; // long dst/std time
}
appendTo += fSymbols->fZoneStrings[zoneIndex][ix];
}
}
break;
}
break;
case 23: // 'Z' - TIMEZONE_RFC
{
@ -780,6 +793,10 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
UBool ambiguousYear[] = { FALSE };
int32_t count = 0;
// hack, clear parsedTimeZone, cast away const
delete parsedTimeZone;
((SimpleDateFormat*)this)->parsedTimeZone = NULL;
// For parsing abutting numeric fields. 'abutPat' is the
// offset into 'pattern' of the first of 2 or more abutting
// numeric fields. 'abutStart' is the offset into 'text'
@ -974,20 +991,38 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
// front or the back of the default century. This only works because we adjust
// the year correctly to start with in other cases -- see subParse().
UErrorCode status = U_ZERO_ERROR;
if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year
if (ambiguousYear[0] || parsedTimeZone != NULL) // If this is true then the two-digit year == the default start year
{
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
Calendar *copy = cal.clone();
UDate parsedDate = copy->getTime(status);
// {sfb} check internalGetDefaultCenturyStart
if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart))
{
// We can't use add here because that does a complete() first.
cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
if (ambiguousYear[0]) {
UDate parsedDate = copy->getTime(status);
// {sfb} check internalGetDefaultCenturyStart
if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
// We can't use add here because that does a complete() first.
cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
}
}
if (parsedTimeZone != NULL) {
TimeZone *tz = parsedTimeZone;
// the calendar represents the parse as gmt time
// we need to turn this into local time, so we add the raw offset
// then we ask the timezone to handle this local time
int32_t rawOffset = 0;
int32_t dstOffset = 0;
tz->getOffset(copy->getTime(status)+tz->getRawOffset(), TRUE,
rawOffset, dstOffset, status);
if (U_SUCCESS(status)) {
cal.set(UCAL_ZONE_OFFSET, rawOffset);
cal.set(UCAL_DST_OFFSET, dstOffset);
}
}
delete copy;
}
@ -1309,20 +1344,20 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
// GMT[+-]hhmm or
// GMT.
if ((text.length() - start) >= gmtLen &&
(text.caseCompare(start, gmtLen, gGmt, 0, gmtLen, U_FOLD_CASE_DEFAULT)) == 0)
{
if ((text.length() - start) >= gmtLen &&
(text.caseCompare(start, gmtLen, gGmt, 0, gmtLen, U_FOLD_CASE_DEFAULT)) == 0)
{
cal.set(UCAL_DST_OFFSET, 0);
pos.setIndex(start + gmtLen);
if( text[pos.getIndex()] == 0x002B /*'+'*/ )
sign = 1;
sign = 1;
else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
sign = -1;
sign = -1;
else {
cal.set(UCAL_ZONE_OFFSET, 0 );
return pos.getIndex();
cal.set(UCAL_ZONE_OFFSET, 0 );
return pos.getIndex();
}
// Look for hours:minutes or hhmm.
@ -1331,59 +1366,38 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
Formattable tzNumber;
fNumberFormat->parse(text, tzNumber, pos);
if( pos.getIndex() == parseStart) {
return -start;
return -start;
}
if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
// This is the hours:minutes case
offset = tzNumber.getLong() * 60;
pos.setIndex(pos.getIndex() + 1);
parseStart = pos.getIndex();
fNumberFormat->parse(text, tzNumber, pos);
if( pos.getIndex() == parseStart) {
return -start;
}
offset += tzNumber.getLong();
// This is the hours:minutes case
offset = tzNumber.getLong() * 60;
pos.setIndex(pos.getIndex() + 1);
parseStart = pos.getIndex();
fNumberFormat->parse(text, tzNumber, pos);
if( pos.getIndex() == parseStart) {
return -start;
}
offset += tzNumber.getLong();
}
else {
// This is the hhmm case.
offset = tzNumber.getLong();
if( offset < 24 )
offset *= 60;
else
offset = offset % 100 + offset / 100 * 60;
// This is the hhmm case.
offset = tzNumber.getLong();
if( offset < 24 )
offset *= 60;
else
offset = offset % 100 + offset / 100 * 60;
}
// Fall through for final processing below of 'offset' and 'sign'.
}
}
else {
// At this point, check for named time zones by looking through
// the locale data from the DateFormatZoneData strings.
// Want to be able to parse both short and long forms.
const UnicodeString *zs;
int32_t j;
for (i = 0; i < fSymbols->fZoneStringsRowCount; i++)
{
// Checking long and short zones [1 & 2],
// and long and short daylight [3 & 4].
for (j = 1; j <= 4; ++j)
{
zs = &fSymbols->fZoneStrings[i][j];
// ### TODO markus 20021014: This use of caseCompare() will fail
// if the text contains a character that case-folds into multiple
// characters. In that case, zs->length() may be too long, and it does not match.
// We need a case-insensitive version of startsWith().
// There are similar cases of such caseCompare() uses elsewhere in ICU.
if (0 == (text.caseCompare(start, zs->length(), *zs, 0))) {
TimeZone *tz = TimeZone::createTimeZone(fSymbols->fZoneStrings[i][0]);
cal.set(UCAL_ZONE_OFFSET, tz->getRawOffset());
// Must call set() with something -- TODO -- Fix this to
// use the correct DST SAVINGS for the zone.
delete tz;
cal.set(UCAL_DST_OFFSET, j >= 3 ? U_MILLIS_PER_HOUR : 0);
return (start + fSymbols->fZoneStrings[i][j].length());
}
}
// !!! side effect, might set parsedZoneString
int32_t result = subParseZoneString(text, start, cal);
if (result != 0) {
return result;
}
// As a last resort, look for numeric timezones of the form
@ -1456,6 +1470,124 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
}
}
int32_t
SimpleDateFormat::getTimeZoneIndex(const UnicodeString& id) const
{
int32_t i = fSymbols->fZoneStringsRowCount;
while (--i >= 0 && fSymbols->fZoneStrings[i][0] != id);
return i;
}
int32_t
SimpleDateFormat::matchZoneString(const UnicodeString& text, int32_t start, int32_t zi) const
{
// ### TODO markus 20021014: This use of caseCompare() will fail
// if the text contains a character that case-folds into multiple
// characters. In that case, zs->length() may be too long, and it does not match.
// We need a case-insensitive version of startsWith().
// There are similar cases of such caseCompare() uses elsewhere in ICU.
int32_t i = fSymbols->fZoneStringsColCount;
const int32_t zscc = i;
while (--i >= 1) {
if (i == 5 && (zscc == 6 || zscc >= 8)) { // skip city name if we have it
continue;
}
const UnicodeString& zs = fSymbols->fZoneStrings[zi][i];
if (zs.length() > 0 && 0 == text.caseCompare(start, zs.length(), zs, 0)) {
break;
}
}
return i;
}
/**
* find time zone 'text' matched zoneStrings and set cal.
* includes optimizations for calendar and default time zones
*/
int32_t
SimpleDateFormat::subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal) const
{
// At this point, check for named time zones by looking through
// the locale data from the DateFormatZoneData strings.
// Want to be able to parse both short and long forms.
TimeZone *tz = NULL;
UnicodeString id;
int32_t zoneIndex = -1;
int32_t zi;
// optimize for calendar's current time zone
zi = getTimeZoneIndex(getTimeZone().getID(id));
if (zi != -1) {
int32_t j = matchZoneString(text, start, zi);
if (j > 0) {
tz = getTimeZone().clone();
zoneIndex = j;
}
}
// optimize for default time zone, assume different from caller
if (tz == NULL) {
TimeZone* defaultZone = TimeZone::createDefault();
zi = getTimeZoneIndex(defaultZone->getID(id));
if (zi != -1) {
int32_t j = matchZoneString(text, start, zi);
if (j > 0) {
zoneIndex = j;
tz = defaultZone;
}
}
if (tz == NULL) {
delete defaultZone;
}
}
// still no luck, check all time zone strings
if (tz == NULL) {
for (zi = 0; zi < fSymbols->fZoneStringsRowCount; zi++) {
int32_t j = matchZoneString(text, start, zi);
if (j > 0) {
tz = TimeZone::createTimeZone(fSymbols->fZoneStrings[zi][0]);
zoneIndex = j;
break;
}
}
}
if (tz != NULL) { // Matched any ?
// always set zone offset, needed to get correct hour in wall time
// when checking daylight savings
cal.set(UCAL_ZONE_OFFSET, tz->getRawOffset());
if (zoneIndex < 3) {
// standard time
cal.set(UCAL_DST_OFFSET, 0);
delete tz; tz = NULL;
} else if (zoneIndex < 5) {
// daylight time
// !!! todo - no getDSTSavings() in ICU's timezone
// use the correct DST SAVINGS for the zone.
// cal.set(UCAL_DST_OFFSET, tz->getDSTSavings());
cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
delete tz; tz = NULL;
} else {
// either standard or daylight
// need to finish getting the date, then compute dst offset as appropriate
// !!! hack for api compatibility, can't modify subParse(...) so can't
// pass this back any other way. cast away const.
((SimpleDateFormat*)this)->parsedTimeZone = tz;
}
return start + fSymbols->fZoneStrings[zi][zoneIndex].length();
}
// complete failure
return 0;
}
/**
* Parse an integer using fNumberFormat. This method is semantically
* const, but actually may modify fNumberFormat.

View file

@ -78,7 +78,7 @@ class DateFormat;
* a am/pm marker (Text) PM
* k hour in day (1~24) (Number) 24
* K hour in am/pm (0~11) (Number) 0
* z time zone (Text) Pacific Standard Time
* z time zone (Time) Pacific Standard Time
* Z time zone (RFC 822) (Number) -0800
* g Julian day (Number) 2451334
* A milliseconds in day (Number) 69540000
@ -91,6 +91,10 @@ class DateFormat;
* (Text): 4 or more, use full form, &lt;4, use short or abbreviated form if it
* exists. (e.g., "EEEE" produces "Monday", "EEE" produces "Mon")
* <P>
* (Time): 4 or 3, display long/short time zone names with daylight/standard
* designation (e.g., Pacific Daylight Time, PDT), 2 or 1, display long/short
* time zone generic names (e.g., Pacific Time, PT).
* <P>
* (Number): the minimum number of digits. Shorter numbers are zero-padded to
* this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
* specially; that is, if the count of 'y' is 2, the Year will be truncated to 2 digits.
@ -113,11 +117,11 @@ class DateFormat;
* \code
* Format Pattern Result
* -------------- -------
* "yyyy.MM.dd G 'at' HH:mm:ss z" ->> 1996.07.10 AD at 15:08:56 PDT
* "yyyy.MM.dd G 'at' HH:mm:ss zz" ->> 1996.07.10 AD at 15:08:56 Pacific Time
* "EEE, MMM d, ''yy" ->> Wed, July 10, '96
* "h:mm a" ->> 12:08 PM
* "hh 'o''clock' a, zzzz" ->> 12 o'clock PM, Pacific Daylight Time
* "K:mm a, z" ->> 0:00 PM, PST
* "K:mm a, z" ->> 0:00 PM, PT
* "yyyyy.MMMMM.dd GGG hh:mm aaa" ->> 1996.July.10 AD 12:08 PM
* \endcode
* </pre>
@ -765,6 +769,25 @@ private:
*/
void parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status);
/**
* Given a canonical time zone id, return the row index in our symbols for that id,
* or -1 if none found.
*/
int32_t getTimeZoneIndex(const UnicodeString& id) const;
/**
* Given text, a start in the text, and a row index, return the column index that
* of the zone name that matches (case insensitive) at start, or 0 if none matches.
*/
int32_t matchZoneString(const UnicodeString& text, int32_t start, int32_t zi) const;
/**
* Given text, a start in the text, and a calendar, return the next offset in the text
* after matching the zone string. If we fail to match, return 0. Update the calendar
* as appropriate.
*/
int32_t subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal) const;
/**
* Used to map pattern characters to Calendar field identifiers.
*/
@ -806,6 +829,8 @@ private:
*/
/*transient*/ int32_t fDefaultCenturyStartYear;
/*transient*/ TimeZone* parsedTimeZone; // here to avoid api change
UBool fHaveDefaultCentury;
};

View file

@ -274,7 +274,7 @@ static void TestCalendar()
}
ucal_getTimeZoneDisplayName(caldef, UCAL_SHORT_DST, "en_US", result, resultlength, &status);
u_uastrcpy(tzdname, "PDT");
u_uastrcpy(tzdname, "PT");
if(u_strcmp(tzdname, result) != 0){
log_err("FAIL: got the wrong time zone(SHORT_DST) display name %s, wanted %s\n", austrdup(result), austrdup(tzdname));
}
@ -286,7 +286,7 @@ static void TestCalendar()
}
ucal_getTimeZoneDisplayName(caldef, UCAL_SHORT_STANDARD, "en_US", result, resultlength, &status);
u_uastrcpy(tzdname, "PST");
u_uastrcpy(tzdname, "PT");
if(u_strcmp(tzdname, result) != 0){
log_err("FAIL: got the wrong time zone(SHORT_STANDARD) display name %s, wanted %s\n", austrdup(result), austrdup(tzdname));
}

View file

@ -170,7 +170,7 @@ static void TestDateFormat()
log_err("FAIL: Date Format for US locale failed using udat_format()\n");
/*format using fr */
u_unescape("10 juil. 96 16 h 05 HAP (\\u00c9UA)", temp, 30);
u_unescape("10 juil. 96 16 h 05 HP (\\u00c9UA)", temp, 30);
if(result != NULL) {
free(result);
result = NULL;

View file

@ -42,7 +42,7 @@ static const char* const txt_testResultStrings[] = {
"Quotes ', {, a 1 {0}",
"Quotes ', {, a 1 {0}",
"You deposited 1 times an amount of $3,456.00 on 1/12/70",
"{2,time,full}, for 3,456, 1 is 5:46:40 AM PST and full date is Monday, January 12, 1970",
"{2,time,full}, for 3,456, 1 is 5:46:40 AM PT and full date is Monday, January 12, 1970",
"{1,number,percent} for 1 is 345,600%"
};

View file

@ -19,6 +19,7 @@
#include "cmemory.h"
#include "cstring.h"
#include "caltest.h" // for fieldName
// *****************************************************************************
// class DateFormatTest
// *****************************************************************************
@ -51,6 +52,8 @@ void DateFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &nam
TESTCASE(21,TestInvalidPattern);
TESTCASE(22,TestGeneral);
TESTCASE(23,TestGreekMay);
TESTCASE(24,TestGenericTime);
TESTCASE(25,TestGenericTimeZoneOrder);
default: name = ""; break;
}
}
@ -188,7 +191,7 @@ DateFormatTest::TestTwoDigitYearDSTParse(void)
int32_t y, m, day, hr, min, sec;
dateToFields(d, y, m, day, hr, min, sec);
if (hr != hour)
errln((UnicodeString)"FAIL: Should parse to hour " + hour);
errln((UnicodeString)"FAIL: Should parse to hour " + hour + " but got " + hr);
if (U_FAILURE(status))
errln((UnicodeString)"FAIL: " + (int32_t)status);
@ -304,13 +307,13 @@ void DateFormatTest::TestFieldPosition() {
// Fields are given in order of DateFormat field number
const char* EXPECTED[] = {
"", "1997", "August", "13", "", "", "34", "12", "",
"Wednesday", "", "", "", "", "PM", "2", "", "PDT", "", "", "", "", "", "",
"Wednesday", "", "", "", "", "PM", "2", "", "PT", "", "", "", "", "", "",
"", "1997", "ao\\u00FBt", "13", "", "14", "34", "", "",
"mercredi", "", "", "", "", "", "", "", "HAP (\\u00C9UA)", "", "", "", "", "", "",
"mercredi", "", "", "", "", "", "", "", "HP (\\u00C9UA)", "", "", "", "", "", "",
"AD", "1997", "8", "13", "14", "14", "34", "12", "5",
"Wed", "225", "2", "33", "3", "PM", "2", "2", "PDT", "1997", "4", "1997", "2450674", "52452513", "-0700",
"Wed", "225", "2", "33", "3", "PM", "2", "2", "PT", "1997", "4", "1997", "2450674", "52452513", "-0700",
"AD", "1997", "August", "0013", "0014", "0014", "0034", "0012", "5130",
"Wednesday", "0225", "0002", "0033", "0003", "PM", "0002", "0002", "Pacific Daylight Time", "1997", "0004", "1997", "2450674", "52452513", "-0700",
@ -956,10 +959,10 @@ DateFormatTest::TestLocaleDateFormat() // Bug 495
DateFormat::FULL, Locale::getFrench());
DateFormat *dfUS = DateFormat::createDateTimeInstance(DateFormat::FULL,
DateFormat::FULL, Locale::getUS());
UnicodeString expectedFRENCH ( "lundi 15 septembre 1997 00 h 00 HAP (\\u00C9UA)" );
UnicodeString expectedFRENCH ( "lundi 15 septembre 1997 00 h 00 HP (\\u00C9UA)" );
expectedFRENCH = expectedFRENCH.unescape();
//UnicodeString expectedUS ( "Monday, September 15, 1997 12:00:00 o'clock AM PDT" );
UnicodeString expectedUS ( "Monday, September 15, 1997 12:00:00 AM PDT" );
UnicodeString expectedUS ( "Monday, September 15, 1997 12:00:00 AM PT" );
logln((UnicodeString)"Date set to : " + dateToString(testDate));
UnicodeString out;
dfFrench->format(testDate, out);
@ -1328,6 +1331,125 @@ void DateFormatTest::expect(const char** data, int32_t data_length,
}
}
void DateFormatTest::TestGenericTime() {
// any zone pattern should parse any zone
const Locale en("en");
const char* ZDATA[] = {
"yyyy MM dd HH:mm zzz",
// round trip
"y/M/d H:mm zzzz", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Standard Time",
"y/M/d H:mm zzz", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 PST",
"y/M/d H:mm zz", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Time",
"y/M/d H:mm z", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 PT",
// non-generic timezone string influences dst offset even if wrong for date/time
"y/M/d H:mm zzz", "pf", "2004/1/1 1:00 PDT", "2004 01 01 01:00 PDT", "2004/1/1 0:00 PST",
"y/M/d H:mm zz", "pf", "2004/1/1 1:00 PDT", "2004 01 01 01:00 PDT", "2004/1/1 0:00 Pacific Time",
"y/M/d H:mm zzz", "pf", "2004/7/1 1:00 PST", "2004 07 01 02:00 PDT", "2004/7/1 2:00 PDT",
"y/M/d H:mm zz", "pf", "2004/7/1 1:00 PST", "2004 07 01 02:00 PDT", "2004/7/1 2:00 Pacific Time",
// generic timezone generates dst offset appropriate for local time
"y/M/d H:mm zzz", "pf", "2004/1/1 1:00 PT", "2004 01 01 01:00 PST", "2004/1/1 1:00 PST",
"y/M/d H:mm zz", "pf", "2004/1/1 1:00 PT", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Time",
"y/M/d H:mm zzz", "pf", "2004/7/1 1:00 PT", "2004 07 01 01:00 PDT", "2004/7/1 1:00 PDT",
"y/M/d H:mm zz", "pf", "2004/7/1 1:00 PT", "2004 07 01 01:00 PDT", "2004/7/1 1:00 Pacific Time",
// daylight savings time transition edge cases.
// time to parse does not really exist, PT interpreted as earlier time
"y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PST", // adjust earlier
"y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PST", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PDT",
"y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PDT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PST",
"y/M/d H:mm z", "pf", "2005/4/3 2:30 PT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PT", // adjust earlier
"y/M/d H:mm z", "pf", "2005/4/3 2:30 PST", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PT",
"y/M/d H:mm z", "pf", "2005/4/3 2:30 PDT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PT",
"y/M/d H:mm", "pf", "2005/4/3 2:30", "2005 04 03 01:30 PST", "2005/4/3 1:30",
// time to parse is ambiguous, PT interpreted as earlier time (?)
"y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PDT", // fail
"y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PST", "2004 10 31 01:30 PST", "2004/10/31 1:30 PST",
"y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PDT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PDT",
"y/M/d H:mm z", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PT", // fail
"y/M/d H:mm z", "pf", "2004/10/31 1:30 PST", "2004 10 31 01:30 PST", "2004/10/31 1:30 PT",
"y/M/d H:mm z", "pf", "2004/10/31 1:30 PDT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PT",
"y/M/d H:mm", "pf", "2004/10/31 1:30", "2004 10 31 01:30 PDT", "2004/10/31 1:30", // fail
};
const int32_t ZDATA_length = sizeof(ZDATA)/ sizeof(ZDATA[0]);
expect(ZDATA, ZDATA_length, en);
UErrorCode status = U_ZERO_ERROR;
logln("cross format/parse tests");
UnicodeString basepat("yy/MM/dd H:mm ");
SimpleDateFormat formats[] = {
SimpleDateFormat(basepat + "z", en, status),
SimpleDateFormat(basepat + "zz", en, status),
SimpleDateFormat(basepat + "zzz", en, status),
SimpleDateFormat(basepat + "zzzz", en, status)
};
const int32_t formats_length = sizeof(formats)/sizeof(formats[0]);
UnicodeString test;
SimpleDateFormat univ("yyyy MM dd HH:mm zzz", en, status);
const UnicodeString times[] = {
"2004 01 02 03:04 PST",
"2004 07 08 09:10 PDT"
};
int32_t times_length = sizeof(times)/sizeof(times[0]);
for (int i = 0; i < times_length; ++i) {
UDate d = univ.parse(times[i], status);
logln(UnicodeString("\ntime: ") + d);
for (int j = 0; j < formats_length; ++j) {
test.remove();
formats[j].format(d, test);
logln("\ntest: '" + test + "'");
for (int k = 0; k < formats_length; ++k) {
UDate t = formats[k].parse(test, status);
if (U_SUCCESS(status)) {
if (d != t) {
errln((UnicodeString)"FAIL: format " + k +
" incorrectly parsed output of format " + j +
" (" + test + "), returned " +
dateToString(t) + " instead of " + dateToString(d));
} else {
logln((UnicodeString)"OK: format " + k + " parsed ok");
}
} else if (status == U_PARSE_ERROR) {
errln((UnicodeString)"FAIL: format " + k +
" could not parse output of format " + j +
" (" + test + ")");
}
}
}
}
}
void DateFormatTest::TestGenericTimeZoneOrder() {
// generic times should parse the same no matter what the placement of the time zone string
// should work for standard and daylight times
const char* XDATA[] = {
"yyyy MM dd HH:mm zzz",
// standard time, explicit daylight/standard
"y/M/d H:mm zzz", "pf", "2004/1/1 1:00 PT", "2004 01 01 01:00 PST", "2004/1/1 1:00 PST",
"y/M/d zzz H:mm", "pf", "2004/1/1 PT 1:00", "2004 01 01 01:00 PST", "2004/1/1 PST 1:00",
"zzz y/M/d H:mm", "pf", "PT 2004/1/1 1:00", "2004 01 01 01:00 PST", "PST 2004/1/1 1:00",
// standard time, generic
"y/M/d H:mm zz", "pf", "2004/1/1 1:00 PT", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Time",
"y/M/d zz H:mm", "pf", "2004/1/1 PT 1:00", "2004 01 01 01:00 PST", "2004/1/1 Pacific Time 1:00",
"zz y/M/d H:mm", "pf", "PT 2004/1/1 1:00", "2004 01 01 01:00 PST", "Pacific Time 2004/1/1 1:00",
// dahylight time, explicit daylight/standard
"y/M/d H:mm zzz", "pf", "2004/7/1 1:00 PT", "2004 07 01 01:00 PDT", "2004/7/1 1:00 PDT",
"y/M/d zzz H:mm", "pf", "2004/7/1 PT 1:00", "2004 07 01 01:00 PDT", "2004/7/1 PDT 1:00",
"zzz y/M/d H:mm", "pf", "PT 2004/7/1 1:00", "2004 07 01 01:00 PDT", "PDT 2004/7/1 1:00",
// daylight time, generic
"y/M/d H:mm zz", "pf", "2004/7/1 1:00 PT", "2004 07 01 01:00 PDT", "2004/7/1 1:00 Pacific Time",
"y/M/d zz H:mm", "pf", "2004/7/1 PT 1:00", "2004 07 01 01:00 PDT", "2004/7/1 Pacific Time 1:00",
"zz y/M/d H:mm", "pf", "PT 2004/7/1 1:00", "2004 07 01 01:00 PDT", "Pacific Time 2004/7/1 1:00",
};
const int32_t XDATA_length = sizeof(XDATA)/sizeof(XDATA[0]);
Locale en("en");
expect(XDATA, XDATA_length, en);
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof

View file

@ -157,6 +157,10 @@ public: // package
void TestGreekMay(void);
void TestGenericTime(void);
void TestGenericTimeZoneOrder(void);
private:
void expectParse(const char** data, int32_t data_length,
const Locale& locale);

View file

@ -1138,7 +1138,7 @@ void TestMessageFormat::TestUnlimitedArgsAndSubformats() {
UnicodeString expected =
"On Nov 20, 2286 (aka 11/20/86, aka November 20, 2286) "
"at 9:46:40 AM (aka 9:46 AM, aka 9:46:40 AM PST) "
"at 9:46:40 AM (aka 9:46 AM, aka 9:46:40 AM PT) "
"there were 1,303 werjes "
"(a 8% increase over 1,202) "
"despite the Glimmung's efforts "

View file

@ -887,8 +887,8 @@ TimeZoneTest::TestDisplayName()
TimeZone::EDisplayType style;
const char *expect;
} kData[] = {
{FALSE, TimeZone::SHORT, "PST"},
{TRUE, TimeZone::SHORT, "PDT"},
{FALSE, TimeZone::SHORT, "PT"},
{TRUE, TimeZone::SHORT, "PT"},
{FALSE, TimeZone::LONG, "Pacific Standard Time"},
{TRUE, TimeZone::LONG, "Pacific Daylight Time"},