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
This commit is contained in:
Peter Edberg 2012-10-15 23:32:31 +00:00
parent cd1a970510
commit 553bcf0b33
5 changed files with 415 additions and 191 deletions

View File

@ -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 <stdio.h>
#include <stdlib.h>
#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<SimpleDateFormat *>(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<SimpleDateFormat *>(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<SimpleDateFormat *>(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
}
}
// Linear search the relative strings
for(int n=0;n<fDatesLen;n++) {
if(fDates[n].string != NULL &&
(0==text.compare(pos.getIndex(),
fDates[n].len,
fDates[n].string))) {
UErrorCode status = U_ZERO_ERROR;
// 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 fields
pos.setErrorIndex(pos.getIndex()+fDates[n].len);
} else {
pos.setIndex(pos.getIndex()+fDates[n].len);
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);
}
}
return;
}
if (!matchedRelative) {
// just parse as normal date
fDateTimeFormatter->applyPattern(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;
}
}
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<SimpleDateFormat*>(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<SimpleDateFormat*>(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<SimpleDateFormat*>(fDateFormat)) == NULL) {
status = U_UNSUPPORTED_ERROR;
return;
}
if (fTimeFormat && (stmfmt = dynamic_cast<SimpleDateFormat*>(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<SimpleDateFormat*>(fDateFormat)) != NULL) {
return sdtfmt->getDateFormatSymbols();
}
return NULL;
return fDateTimeFormatter->getDateFormatSymbols();
}
void RelativeDateFormat::loadDates(UErrorCode &status) {

View File

@ -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 #

View File

@ -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);

View File

@ -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; q<UCAL_FIELD_COUNT; q++) {
if (fromSet.isSet((UCalendarDateFields)q)) {
//int32_t oldv = cal->get((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);
}
}

View File

@ -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.