mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
ICU-646 wall time support in icu
X-SVN-Rev: 17801
This commit is contained in:
parent
f089092803
commit
50c3a2554b
9 changed files with 394 additions and 111 deletions
|
@ -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.
|
||||
|
|
|
@ -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, <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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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%"
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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"},
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue