From: Peter Edberg Date: Mon, 15 Oct 2012 23:32:31 +0000 (+0000) Subject: ICU-9656 Change RelativeDateFormat to keep 2 patterns (not formatters), and for fmt... X-Git-Tag: milestone-59-0-1~3411 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=553bcf0b33dd35ce47961591fafe49e5018737a2;p=icu ICU-9656 Change RelativeDateFormat to keep 2 patterns (not formatters), and for fmt/parse glue them (with substitutions) and apply to single formatter X-SVN-Rev: 32638 --- diff --git a/icu4c/source/i18n/reldtfmt.cpp b/icu4c/source/i18n/reldtfmt.cpp index 065b0a1e612..d079ca5db13 100644 --- a/icu4c/source/i18n/reldtfmt.cpp +++ b/icu4c/source/i18n/reldtfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2011, International Business Machines Corporation and +* Copyright (C) 2007-2012, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ @@ -9,14 +9,10 @@ #if !UCONFIG_NO_FORMATTING -//#define DEBUG_RELDTFMT - -#include -#include - #include "reldtfmt.h" -#include "unicode/msgfmt.h" +#include "unicode/datefmt.h" #include "unicode/smpdtfmt.h" +#include "unicode/msgfmt.h" #include "gregoimp.h" // for CalendarData #include "cmemory.h" @@ -39,50 +35,68 @@ static const char DT_DateTimePatternsTag[]="DateTimePatterns"; UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : -DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), -fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocale), -fDayMin(other.fDayMin), fDayMax(other.fDayMax), -fDatesLen(other.fDatesLen), fDates(NULL) + DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), + fTimePattern(other.fTimePattern), fCombinedFormat(NULL), + fDateStyle(other.fDateStyle), fLocale(other.fLocale), + fDayMin(other.fDayMin), fDayMax(other.fDayMax), + fDatesLen(other.fDatesLen), fDates(NULL) { - if(other.fDateFormat != NULL) { - fDateFormat = (DateFormat*)other.fDateFormat->clone(); - } else { - fDateFormat = NULL; + if(other.fDateTimeFormatter != NULL) { + fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone(); + } + if(other.fCombinedFormat != NULL) { + fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone(); } if (fDatesLen > 0) { fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen); } - //fCalendar = other.fCalendar->clone(); -/* - if(other.fTimeFormat != NULL) { - fTimeFormat = (DateFormat*)other.fTimeFormat->clone(); - } else { - fTimeFormat = NULL; - } -*/ } -RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status) - : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), -fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDates(NULL) - { +RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, + const Locale& locale, UErrorCode& status) : + DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), + fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL) +{ if(U_FAILURE(status) ) { return; } - if(fDateStyle != UDAT_NONE) { - EStyle newStyle = (EStyle)(fDateStyle & ~UDAT_RELATIVE); - // Create a DateFormat in the non-relative style requested. - fDateFormat = createDateInstance(newStyle, locale); - } - if(fTimeStyle >= UDAT_FULL && fTimeStyle <= UDAT_SHORT) { - fTimeFormat = createTimeInstance((EStyle)fTimeStyle, locale); - } else if(fTimeStyle != UDAT_NONE) { + if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { // don't support other time styles (e.g. relative styles), for now status = U_ILLEGAL_ARGUMENT_ERROR; return; } + UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; + DateFormat * df; + // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). + // We do need to get separate patterns for the date & time styles. + if (baseDateStyle != UDAT_NONE) { + df = createDateInstance((EStyle)baseDateStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == NULL) { + status = U_UNSUPPORTED_ERROR; + return; + } + fDateTimeFormatter->toPattern(fDatePattern); + if (timeStyle != UDAT_NONE) { + df = createTimeInstance((EStyle)timeStyle, locale); + SimpleDateFormat *sdf = dynamic_cast(df); + if (sdf != NULL) { + sdf->toPattern(fTimePattern); + delete sdf; + } + } + } else { + // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter + df = createTimeInstance((EStyle)timeStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == NULL) { + status = U_UNSUPPORTED_ERROR; + return; + } + fDateTimeFormatter->toPattern(fTimePattern); + } // Initialize the parent fCalendar, so that parse() works correctly. initializeCalendar(NULL, locale, status); @@ -90,8 +104,7 @@ fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDa } RelativeDateFormat::~RelativeDateFormat() { - delete fDateFormat; - delete fTimeFormat; + delete fDateTimeFormatter; delete fCombinedFormat; uprv_free(fDates); } @@ -106,19 +119,21 @@ UBool RelativeDateFormat::operator==(const Format& other) const { // DateFormat::operator== guarantees following cast is safe RelativeDateFormat* that = (RelativeDateFormat*)&other; return (fDateStyle==that->fDateStyle && - fTimeStyle==that->fTimeStyle && + fDatePattern==that->fDatePattern && + fTimePattern==that->fTimePattern && fLocale==that->fLocale); } return FALSE; } +static const UChar APOSTROPHE = (UChar)0x0027; + UnicodeString& RelativeDateFormat::format( Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const { UErrorCode status = U_ZERO_ERROR; - UChar emptyStr = 0; - UnicodeString dateString(&emptyStr); + UnicodeString relativeDayString; // calculate the difference, in days, between 'cal' and now. int dayDiff = dayDifference(cal, status); @@ -128,34 +143,35 @@ UnicodeString& RelativeDateFormat::format( Calendar& cal, const UChar *theString = getStringForDay(dayDiff, len, status); if(U_SUCCESS(status) && (theString!=NULL)) { // found a relative string - dateString.setTo(theString, len); + relativeDayString.setTo(theString, len); } - if(fTimeFormat == NULL || fCombinedFormat == 0) { - if (dateString.length() > 0) { - appendTo.append(dateString); - } else if(fDateFormat != NULL) { - fDateFormat->format(cal,appendTo,pos); + if (fDatePattern.isEmpty()) { + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->format(cal,appendTo,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + if (relativeDayString.length() > 0) { + appendTo.append(relativeDayString); + } else { + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(cal,appendTo,pos); } } else { - if (dateString.length() == 0 && fDateFormat != NULL) { - fDateFormat->format(cal,dateString,pos); - } - UnicodeString timeString(&emptyStr); - FieldPosition timepos = pos; - fTimeFormat->format(cal,timeString,timepos); - Formattable timeDateStrings[] = { timeString, dateString }; - fCombinedFormat->format(timeDateStrings, 2, appendTo, pos, status); // pos is ignored by this - int32_t offset; - if (pos.getEndIndex() > 0 && (offset = appendTo.indexOf(dateString)) >= 0) { - // pos.field was found in dateString, offset start & end based on final position of dateString - pos.setBeginIndex( pos.getBeginIndex() + offset ); - pos.setEndIndex( pos.getEndIndex() + offset ); - } else if (timepos.getEndIndex() > 0 && (offset = appendTo.indexOf(timeString)) >= 0) { - // pos.field was found in timeString, offset start & end based on final position of timeString - pos.setBeginIndex( timepos.getBeginIndex() + offset ); - pos.setEndIndex( timepos.getEndIndex() + offset ); + UnicodeString datePattern; + if (relativeDayString.length() > 0) { + // Need to quote the relativeDayString to make it a legal date pattern + relativeDayString.findAndReplace(UnicodeString("'"), UnicodeString("''") ); // double any existing APOSTROPHE + relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... + relativeDayString.append(APOSTROPHE); // and at end + datePattern.setTo(relativeDayString); + } else { + datePattern.setTo(fDatePattern); } + UnicodeString combinedPattern; + Formattable timeDatePatterns[] = { fTimePattern, datePattern }; + fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->format(cal,appendTo,pos); } return appendTo; @@ -181,40 +197,97 @@ void RelativeDateFormat::parse( const UnicodeString& text, Calendar& cal, ParsePosition& pos) const { - // Can the fDateFormat parse it? - if(fDateFormat != NULL) { - ParsePosition aPos(pos); - fDateFormat->parse(text,cal,aPos); - if((aPos.getIndex() != pos.getIndex()) && - (aPos.getErrorIndex()==-1)) { - pos=aPos; // copy the sub parse - return; // parsed subfmt OK + int32_t startIndex = pos.getIndex(); + if (fDatePattern.isEmpty()) { + // no date pattern, try parsing as time + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->parse(text,cal,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + // no time pattern or way to combine, try parsing as date + // first check whether text matches a relativeDayString + UBool matchedRelative = FALSE; + for (int n=0; n < fDatesLen && !matchedRelative; n++) { + if (fDates[n].string != NULL && + text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { + // it matched, handle the relative day string + UErrorCode status = U_ZERO_ERROR; + matchedRelative = TRUE; + + // Set the calendar to now+offset + cal.setTime(Calendar::getNow(),status); + cal.add(UCAL_DATE,fDates[n].offset, status); + + if(U_FAILURE(status)) { + // failure in setting calendar field, set offset to beginning of rel day string + pos.setErrorIndex(startIndex); + } else { + pos.setIndex(startIndex + fDates[n].len); + } + } } - } - - // Linear search the relative strings - for(int n=0;napplyPattern(fDatePattern); + fDateTimeFormatter->parse(text,cal,pos); + } + } else { + // Here we replace any relativeDayString in text with the equivalent date + // formatted per fDatePattern, then parse text normally using the combined pattern. + UnicodeString modifiedText(text); + FieldPosition fPos; + int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; + UErrorCode status = U_ZERO_ERROR; + for (int n=0; n < fDatesLen; n++) { + int32_t relativeStringOffset; + if (fDates[n].string != NULL && + (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { + // it matched, replace the relative date with a real one for parsing + UnicodeString dateString; + Calendar * tempCal = cal.clone(); + + // Set the calendar to now+offset + tempCal->setTime(Calendar::getNow(),status); + tempCal->add(UCAL_DATE,fDates[n].offset, status); + if(U_FAILURE(status)) { + pos.setErrorIndex(startIndex); + delete tempCal; + return; + } + + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(*tempCal, dateString, fPos); + dateStart = relativeStringOffset; + origDateLen = fDates[n].len; + modDateLen = dateString.length(); + modifiedText.replace(dateStart, origDateLen, dateString); + delete tempCal; + break; } - return; + } + UnicodeString combinedPattern; + Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; + fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, fPos, status); // pos is ignored by this + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->parse(modifiedText,cal,pos); + + // Adjust offsets + UBool noError = (pos.getErrorIndex() < 0); + int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); + if (offset >= dateStart + modDateLen) { + // offset at or after the end of the replaced text, + // correct by the difference between original and replacement + offset -= (modDateLen - origDateLen); + } else if (offset >= dateStart) { + // offset in the replaced text, set it to the beginning of that text + // (i.e. the beginning of the relative day string) + offset = dateStart; + } + if (noError) { + pos.setIndex(offset); + } else { + pos.setErrorIndex(offset); } } - - // parse failed } UDate @@ -260,23 +333,14 @@ RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const { if (!U_FAILURE(status)) { result.remove(); - if (fTimeFormat == NULL || fCombinedFormat == 0) { - if (fDateFormat != NULL) { - UnicodeString datePattern; - this->toPatternDate(datePattern, status); - if (!U_FAILURE(status)) { - result.setTo(datePattern); - } - } + if (fDatePattern.isEmpty()) { + result.setTo(fTimePattern); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + result.setTo(fDatePattern); } else { - UnicodeString datePattern, timePattern; - this->toPatternDate(datePattern, status); - this->toPatternTime(timePattern, status); - if (!U_FAILURE(status)) { - Formattable timeDatePatterns[] = { timePattern, datePattern }; - FieldPosition pos; - fCombinedFormat->format(timeDatePatterns, 2, result, pos, status); - } + Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; + FieldPosition pos; + fCombinedFormat->format(timeDatePatterns, 2, result, pos, status); } } return result; @@ -287,14 +351,7 @@ RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) con { if (!U_FAILURE(status)) { result.remove(); - if ( fDateFormat ) { - SimpleDateFormat* sdtfmt = dynamic_cast(fDateFormat); - if (sdtfmt != NULL) { - sdtfmt->toPattern(result); - } else { - status = U_UNSUPPORTED_ERROR; - } - } + result.setTo(fDatePattern); } return result; } @@ -304,14 +361,7 @@ RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) con { if (!U_FAILURE(status)) { result.remove(); - if ( fTimeFormat ) { - SimpleDateFormat* sdtfmt = dynamic_cast(fTimeFormat); - if (sdtfmt != NULL) { - sdtfmt->toPattern(result); - } else { - status = U_UNSUPPORTED_ERROR; - } - } + result.setTo(fTimePattern); } return result; } @@ -320,33 +370,15 @@ void RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) { if (!U_FAILURE(status)) { - SimpleDateFormat* sdtfmt = NULL; - SimpleDateFormat* stmfmt = NULL; - if (fDateFormat && (sdtfmt = dynamic_cast(fDateFormat)) == NULL) { - status = U_UNSUPPORTED_ERROR; - return; - } - if (fTimeFormat && (stmfmt = dynamic_cast(fTimeFormat)) == NULL) { - status = U_UNSUPPORTED_ERROR; - return; - } - if ( fDateFormat ) { - sdtfmt->applyPattern(datePattern); - } - if ( fTimeFormat ) { - stmfmt->applyPattern(timePattern); - } + fDatePattern.setTo(datePattern); + fTimePattern.setTo(timePattern); } } const DateFormatSymbols* RelativeDateFormat::getDateFormatSymbols() const { - SimpleDateFormat* sdtfmt = NULL; - if (fDateFormat && (sdtfmt = dynamic_cast(fDateFormat)) != NULL) { - return sdtfmt->getDateFormatSymbols(); - } - return NULL; + return fDateTimeFormatter->getDateFormatSymbols(); } void RelativeDateFormat::loadDates(UErrorCode &status) { diff --git a/icu4c/source/i18n/reldtfmt.h b/icu4c/source/i18n/reldtfmt.h index d7eed6e4582..1206ea3add0 100644 --- a/icu4c/source/i18n/reldtfmt.h +++ b/icu4c/source/i18n/reldtfmt.h @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2011, International Business Machines Corporation and * +* Copyright (C) 2007-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -18,6 +18,7 @@ #if !UCONFIG_NO_FORMATTING #include "unicode/datefmt.h" +#include "unicode/smpdtfmt.h" U_NAMESPACE_BEGIN @@ -233,12 +234,12 @@ public: private: - DateFormat *fDateFormat; // the held date format - DateFormat *fTimeFormat; // the held time format + SimpleDateFormat *fDateTimeFormatter; + UnicodeString fDatePattern; + UnicodeString fTimePattern; MessageFormat *fCombinedFormat; // the {0} {1} format. UDateFormatStyle fDateStyle; - UDateFormatStyle fTimeStyle; Locale fLocale; int32_t fDayMin; // day id of lowest # diff --git a/icu4c/source/test/cintltst/cdattst.c b/icu4c/source/test/cintltst/cdattst.c index cad794ab309..0d2ab2521d3 100644 --- a/icu4c/source/test/cintltst/cdattst.c +++ b/icu4c/source/test/cintltst/cdattst.c @@ -396,6 +396,9 @@ static const UChar newTimePatn[] = { 0x0048, 0x0048, 0x002C, 0x006D, 0x006D, 0 } static const UChar minutesStr[] = { 0x0034, 0x0039, 0 }; /* "49", minutes string to search for in output */ enum { kDateOrTimeOutMax = 96, kDateAndTimeOutMax = 192 }; +static const UDate minutesTolerance = 2 * 60.0 * 1000.0; +static const UDate daysTolerance = 2 * 24.0 * 60.0 * 60.0 * 1000.0; + static void TestRelativeDateFormat() { UDate today = 0.0; @@ -423,10 +426,10 @@ static void TestRelativeDateFormat() UDateFormat* fmtTime; int32_t dayOffset, limit; UFieldPosition fp; - UChar strDateTime[kDateAndTimeOutMax]; - UChar strDate[kDateOrTimeOutMax]; - UChar strTime[kDateOrTimeOutMax]; - UChar * strPtr; + UChar strDateTime[kDateAndTimeOutMax]; + UChar strDate[kDateOrTimeOutMax]; + UChar strTime[kDateOrTimeOutMax]; + UChar * strPtr; int32_t dtpatLen; fmtRelDateTime = udat_open(UDAT_SHORT, *stylePtr | UDAT_RELATIVE, trdfLocale, trdfZone, -1, NULL, 0, &status); @@ -450,37 +453,37 @@ static void TestRelativeDateFormat() dtpatLen = udat_toPatternRelativeDate(fmtRelDateTime, strDate, kDateAndTimeOutMax, &status); if ( U_FAILURE(status) ) { - log_err("udat_toPatternRelativeDate timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); - status = U_ZERO_ERROR; + log_err("udat_toPatternRelativeDate timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); + status = U_ZERO_ERROR; } else if ( u_strstr(strDate, *monthPtnPtr) == NULL || dtpatLen != u_strlen(strDate) ) { - log_err("udat_toPatternRelativeDate timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) date pattern incorrect\n", *stylePtr ); + log_err("udat_toPatternRelativeDate timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) date pattern incorrect\n", *stylePtr ); } dtpatLen = udat_toPatternRelativeTime(fmtRelDateTime, strTime, kDateAndTimeOutMax, &status); if ( U_FAILURE(status) ) { - log_err("udat_toPatternRelativeTime timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); - status = U_ZERO_ERROR; + log_err("udat_toPatternRelativeTime timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); + status = U_ZERO_ERROR; } else if ( u_strstr(strTime, minutesPatn) == NULL || dtpatLen != u_strlen(strTime) ) { - log_err("udat_toPatternRelativeTime timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) time pattern incorrect\n", *stylePtr ); + log_err("udat_toPatternRelativeTime timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) time pattern incorrect\n", *stylePtr ); } dtpatLen = udat_toPattern(fmtRelDateTime, FALSE, strDateTime, kDateAndTimeOutMax, &status); if ( U_FAILURE(status) ) { - log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); - status = U_ZERO_ERROR; + log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); + status = U_ZERO_ERROR; } else if ( u_strstr(strDateTime, strDate) == NULL || u_strstr(strDateTime, strTime) == NULL || dtpatLen != u_strlen(strDateTime) ) { - log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) dateTime pattern incorrect\n", *stylePtr ); + log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) dateTime pattern incorrect\n", *stylePtr ); } udat_applyPatternRelative(fmtRelDateTime, strDate, u_strlen(strDate), newTimePatn, u_strlen(newTimePatn), &status); if ( U_FAILURE(status) ) { - log_err("udat_applyPatternRelative timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); - status = U_ZERO_ERROR; + log_err("udat_applyPatternRelative timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); + status = U_ZERO_ERROR; } else { - udat_toPattern(fmtRelDateTime, FALSE, strDateTime, kDateAndTimeOutMax, &status); - if ( U_FAILURE(status) ) { - log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); - status = U_ZERO_ERROR; - } else if ( u_strstr(strDateTime, newTimePatn) == NULL ) { - log_err("udat_applyPatternRelative timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) didn't update time pattern\n", *stylePtr ); - } + udat_toPattern(fmtRelDateTime, FALSE, strDateTime, kDateAndTimeOutMax, &status); + if ( U_FAILURE(status) ) { + log_err("udat_toPattern timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); + status = U_ZERO_ERROR; + } else if ( u_strstr(strDateTime, newTimePatn) == NULL ) { + log_err("udat_applyPatternRelative timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) didn't update time pattern\n", *stylePtr ); + } } udat_applyPatternRelative(fmtRelDateTime, strDate, u_strlen(strDate), strTime, u_strlen(strTime), &status); /* restore original */ @@ -493,12 +496,30 @@ static void TestRelativeDateFormat() log_err("udat_format timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); status = U_ZERO_ERROR; } else { + int32_t parsePos = 0; + UDate dateResult = udat_parse(fmtRelDateTime, strDateTime, -1, &parsePos, &status); + UDate dateDiff = (dateResult >= dateToUse)? dateResult - dateToUse: dateToUse - dateResult; + if ( U_FAILURE(status) || dateDiff > minutesTolerance ) { + log_err("udat_parse timeStyle SHORT dateStyle (%d | UDAT_RELATIVE) fails, error %s, expect approx %.1f, got %.1f, parsePos %d\n", + *stylePtr, myErrorName(status), dateToUse, dateResult, parsePos ); + status = U_ZERO_ERROR; + } + udat_format(fmtRelDate, dateToUse, strDate, kDateOrTimeOutMax, NULL, &status); if ( U_FAILURE(status) ) { log_err("udat_format timeStyle NONE dateStyle (%d | UDAT_RELATIVE) fails, error %s\n", *stylePtr, myErrorName(status) ); status = U_ZERO_ERROR; } else if ( u_strstr(strDateTime, strDate) == NULL ) { log_err("relative date string not found in udat_format timeStyle SHORT dateStyle (%d | UDAT_RELATIVE)\n", *stylePtr ); + } else { + parsePos = 0; + dateResult = udat_parse(fmtRelDate, strDate, -1, &parsePos, &status); + dateDiff = (dateResult >= dateToUse)? dateResult - dateToUse: dateToUse - dateResult; + if ( U_FAILURE(status) || dateDiff > daysTolerance ) { + log_err("udat_parse timeStyle NONE dateStyle (%d | UDAT_RELATIVE) fails, error %s, expect approx %.1f, got %.1f, parsePos %d\n", + *stylePtr, myErrorName(status), dateToUse, dateResult, parsePos ); + status = U_ZERO_ERROR; + } } udat_format(fmtTime, dateToUse, strTime, kDateOrTimeOutMax, NULL, &status); @@ -1194,7 +1215,7 @@ static void TestRelativeCrash(void) { } } { - UChar erabuf[32]; + UChar erabuf[32]; UErrorCode subStatus = U_ZERO_ERROR; what = "udat_getSymbols"; log_verbose("Trying %s on a relative date..\n", what); diff --git a/icu4c/source/test/intltest/dadrfmt.cpp b/icu4c/source/test/intltest/dadrfmt.cpp index a3c58362f15..c3a533aede8 100644 --- a/icu4c/source/test/intltest/dadrfmt.cpp +++ b/icu4c/source/test/intltest/dadrfmt.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2010, International Business Machines Corporation and + * Copyright (c) 1997-2010,2012, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -73,14 +73,16 @@ void DataDrivenFormatTest::runIndexedTest(int32_t index, UBool exec, /* - * Headers { "locale","spec", "date", "str"} + * Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either an unsigned long (millis), or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // str: the expected unicode string Cases { { - "en_US@calendar=gregorian", + "en_US@calendar=gregorian", + "", "DATE=SHORT,TIME=SHORT", "ERA=1,YEAR=2007,MONTH=AUGUST,DATE=8,HOUR=18,MINUTE=54,SECOND=12", "8/8/2007 6:54pm" @@ -130,6 +132,11 @@ void DataDrivenFormatTest::testConvertDate(TestData *testData, errln("case %d: No 'locale' line.", n); continue; } + UnicodeString zone = currentCase->getString("zone", status); + if (U_FAILURE(status)) { + errln("case %d: No 'zone' line.", n); + continue; + } UnicodeString spec = currentCase->getString("spec", status); if(U_FAILURE(status)) { errln("case %d: No 'spec' line.", n); @@ -175,6 +182,13 @@ void DataDrivenFormatTest::testConvertDate(TestData *testData, if(U_FAILURE(status)) { errln("case %d: could not create calendar from %s", n, calLoc); } + + if (zone.length() > 0) { + TimeZone * tz = TimeZone::createTimeZone(zone); + cal->setTimeZone(*tz); + format->setTimeZone(*tz); + delete tz; + } // parse 'date' if(date.startsWith(kMILLIS)) { @@ -197,8 +211,13 @@ void DataDrivenFormatTest::testConvertDate(TestData *testData, for (int q=0; qget((UCalendarDateFields)q, status); - cal->add((UCalendarDateFields)q, - fromSet.get((UCalendarDateFields)q), status); + if (q == UCAL_DATE) { + cal->add((UCalendarDateFields)q, + fromSet.get((UCalendarDateFields)q), status); + } else { + cal->set((UCalendarDateFields)q, + fromSet.get((UCalendarDateFields)q)); + } //int32_t newv = cal->get((UCalendarDateFields)q, status); } } diff --git a/icu4c/source/test/testdata/format.txt b/icu4c/source/test/testdata/format.txt index a941b23ce84..9827bd6fce9 100644 --- a/icu4c/source/test/testdata/format.txt +++ b/icu4c/source/test/testdata/format.txt @@ -19,23 +19,27 @@ format:table(nofallback) { Type { "date_parse" } }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either 'MILLIS=####' where #### is millis, // or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // or RELATIVE_MILLIS=### where ### is a signed value which is added to the current millis - // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time + // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time, + // and any other fields present will be set explicitly. // str: the expected unicode string Cases { { - "en_US@calendar=gregorian", + "en_US@calendar=gregorian", + "", "DATE=SHORT,TIME=SHORT", "ERA=1,YEAR=2007,MONTH=AUGUST,DATE=8,HOUR_OF_DAY=18,MINUTE=54,SECOND=0", "8/8/07, 6:54 PM" }, { "zh_TW@calendar=roc", + "", "DATE=LONG", "ERA=1,YEAR=98,MONTH=0,DATE=24", "民國98年1月24日", @@ -43,6 +47,7 @@ format:table(nofallback) { { //民國前2年1月24日 -> 1910-1-24 "zh_TW@calendar=roc", + "", "DATE=LONG", "ERA=0,YEAR=2,MONTH=0,DATE=24", "民國前2年1月24日", @@ -58,46 +63,147 @@ format:table(nofallback) { Type { "date_format" } }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} Cases { { "en_US@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_ADD:DATE=1", // one day from now "Tomorrow" }, { "en_US@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_MILLIS=0", // today "Today" }, { "en_US@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_ADD:DATE=-1", // one day before now "Yesterday" }, + // date only, out of relative range + { + "en_US@calendar=gregorian", + "GMT", + "DATE=RELATIVE_FULL", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "Monday, October 8, 2012" + }, + // time only + { + "en_US@calendar=gregorian", + "GMT", + "TIME=LONG", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "11:59:00 PM GMT" + }, + { + "en_US@calendar=gregorian", + "GMT", + "TIME=LONG", + "RELATIVE_ADD:DATE=-1,HOUR_OF_DAY=17,MINUTE=0,SECOND=0", // one day before now at specified time + "5:00:00 PM GMT" + }, + // normal formats, combined using 'at' + { + "en_US@calendar=gregorian", + "GMT", + "DATE=RELATIVE_FULL,TIME=LONG", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "Monday, October 8, 2012 at 11:59:00 PM GMT" + }, + // normal formats, combined using ", " + { + "en_US@calendar=gregorian", + "GMT", + "DATE=RELATIVE_MEDIUM,TIME=SHORT", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "Oct 8, 2012, 11:59 PM" + }, + // formats with relative day, combined using 'at' + { + "en_US@calendar=gregorian", + "GMT", + "DATE=RELATIVE_FULL,TIME=LONG", + "RELATIVE_ADD:DATE=-1,HOUR_OF_DAY=17,MINUTE=0,SECOND=0", // one day before now at specified time + "Yesterday at 5:00:00 PM GMT" + }, + // formats with relative day, combined using ", " + { + "en_US@calendar=gregorian", + "GMT", + "DATE=RELATIVE_MEDIUM,TIME=SHORT", + "RELATIVE_ADD:DATE=-1,HOUR_OF_DAY=17,MINUTE=0,SECOND=0", // one day before now at specified time + "Yesterday, 5:00 PM" + }, + // normal formats that have quoted literals, combined + { + "pt@calendar=gregorian", + "GMT", + "DATE=RELATIVE_FULL,TIME=LONG", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "segunda-feira, 8 de outubro de 2012 23:59:00 Horário de Greenwich" + }, + // vi combined formats have time first + { + "vi@calendar=gregorian", + "GMT", + "DATE=RELATIVE_LONG,TIME=MEDIUM", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "23:59:00 Ngày 08 tháng 10 năm 2012" + }, + { + "vi@calendar=gregorian", + "GMT", + "DATE=RELATIVE_LONG,TIME=MEDIUM", + "RELATIVE_ADD:DATE=-1,HOUR_OF_DAY=17,MINUTE=0,SECOND=0", // one day before now at specified time + "17:00:00 Ngày hôm qua" + }, + // el combines formats using hyphen + { + "el@calendar=gregorian", + "GMT", + "DATE=RELATIVE_LONG,TIME=MEDIUM", + "ERA=1,YEAR=2012,MONTH=OCTOBER,DATE=8,HOUR_OF_DAY=23,MINUTE=59,SECOND=0", + "8 Οκτωβρίου 2012 - 11:59:00 μ.μ." + }, + { + "el@calendar=gregorian", + "GMT", + "DATE=RELATIVE_LONG,TIME=MEDIUM", + "RELATIVE_ADD:DATE=-1,HOUR_OF_DAY=17,MINUTE=0,SECOND=0", // one day before now at specified time + "Χτες - 5:00:00 μ.μ." + }, + // other tests { "mt_MT@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_ADD:DATE=1", // one day from now "Għada" }, { "mt_MT@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_MILLIS=0", // today "Illum" }, { "mt_MT@calendar=gregorian", + "", "DATE=RELATIVE_SHORT", "RELATIVE_ADD:DATE=-1", // one day before now "Lbieraħ" }, { "ru", + "", "DATE=RELATIVE_SHORT", "RELATIVE_ADD:DATE=-2", // 2 days ago "Позавчера" @@ -118,13 +224,15 @@ format:table(nofallback) { // Type { "date_parse" } // }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either 'MILLIS=####' where #### is millis, // or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // or RELATIVE_MILLIS=### where ### is a signed value which is added to the current millis - // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time + // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time, + // and any other fields present will be set explicitly. // str: the expected unicode string // from CLDR UTS 35: @@ -134,30 +242,35 @@ format:table(nofallback) { //AD 1 1 01 001 0001 00001 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=1", "AD 1" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=1", "AD 01" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=1", "AD 001" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=1", "AD 0001" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=1", "AD 00001" @@ -165,30 +278,35 @@ format:table(nofallback) { //AD 12 12 12 012 0012 00012 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=12", "AD 12" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=12", "AD 12" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=12", "AD 012" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=12", "AD 0012" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=12", "AD 00012" @@ -196,30 +314,35 @@ format:table(nofallback) { //AD 123 123 23 123 0123 00123 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=123", "AD 123" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=123", "AD 23" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=123", "AD 123" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=123", "AD 0123" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=123", "AD 00123" @@ -227,30 +350,35 @@ format:table(nofallback) { //AD 1234 1234 34 1234 1234 01234 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=1234", "AD 1234" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=1234", "AD 34" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=1234", "AD 1234" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=1234", "AD 1234" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=1234", "AD 01234" @@ -258,30 +386,35 @@ format:table(nofallback) { //AD 12345 12345 45 12345 12345 12345 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=12345", "AD 12345" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=12345", "AD 45" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=12345", "AD 12345" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=12345", "AD 12345" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=12345", "AD 12345" @@ -301,13 +434,15 @@ format:table(nofallback) { Type { "date_parse" } }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either 'MILLIS=####' where #### is millis, // or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // or RELATIVE_MILLIS=### where ### is a signed value which is added to the current millis - // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time + // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time, + // and any other fields present will be set explicitly. // str: the expected unicode string // from CLDR UTS 35: @@ -317,30 +452,35 @@ format:table(nofallback) { //AD 1 1 01 001 0001 00001 { "en_US@calendar=gregorian", + "", "PATTERN=G y", "YEAR=2008", "AD 2008" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yy", "YEAR=2008", "AD 08" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyy", "YEAR=2008", "AD 2008" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyy", "YEAR=2008", "AD 2008" }, { "en_US@calendar=gregorian", + "", "PATTERN=G yyyyy", "YEAR=2008", "AD 02008" @@ -349,30 +489,35 @@ format:table(nofallback) { // Japanese { "en_US@calendar=japanese", + "", "PATTERN=G y", "YEAR=8", "Heisei 8" }, { "en_US@calendar=japanese", + "", "PATTERN=G yy", "YEAR=8", "Heisei 08" }, { "en_US@calendar=japanese", + "", "PATTERN=G yyy", "YEAR=8", "Heisei 008" }, { "en_US@calendar=japanese", + "", "PATTERN=G yyyy", "YEAR=8", "Heisei 0008" }, { "en_US@calendar=japanese", + "", "PATTERN=G yyyyy", "YEAR=8", "Heisei 00008" @@ -390,18 +535,21 @@ format:table(nofallback) { Type { "date_format" } }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either 'MILLIS=####' where #### is millis, // or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // or RELATIVE_MILLIS=### where ### is a signed value which is added to the current millis - // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time + // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time, + // and any other fields present will be set explicitly. // str: the expected unicode string Cases { { "en_US@calendar=hebrew", + "", "DATE=FULL,TIME=FULL", "MILLIS=3076424179200000", "Friday, Heshvan 3, 103217 at 12:00:00 AM GMT-08:00" @@ -419,18 +567,21 @@ format:table(nofallback) { Type { "date_parse" } }, } - Headers { "locale","spec", "date", "str"} + Headers { "locale", "zone", "spec", "date", "str"} // locale: locale including calendar type + // zone: time zone name, or "" to not explicitly set zone // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' // date: either 'MILLIS=####' where #### is millis, // or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale // or RELATIVE_MILLIS=### where ### is a signed value which is added to the current millis - // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time + // or RELATIVE_ADD:DATE=1 which means that the field "DATE" will be added by +1 relative to current time, + // and any other fields present will be set explicitly. // str: the expected unicode string Cases { { "en_US@calendar=gregorian", + "", "PATTERN=YYYYHHmmssEEEww", "YEAR=1999,HOUR_OF_DAY=4,MINUTE=5,SECOND=6,DAY_OF_WEEK=2,WEEK_OF_YEAR=4", // won't roundtrip.