/*
*******************************************************************************
-* Copyright (C) 2007-2009, International Business Machines Corporation and *
-* others. All Rights Reserved. *
+* Copyright (C) 2007-2013, International Business Machines Corporation and
+* others. All Rights Reserved.
*******************************************************************************
*/
#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"
+#include "uresimp.h"
U_NAMESPACE_BEGIN
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);
}
RelativeDateFormat::~RelativeDateFormat() {
- delete fDateFormat;
- delete fTimeFormat;
+ delete fDateTimeFormatter;
delete fCombinedFormat;
uprv_free(fDates);
}
// 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);
// look up string
- int32_t len;
+ int32_t len = 0;
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(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // 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;
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;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);
+ 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;
}
- 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
{
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;
{
if (!U_FAILURE(status)) {
result.remove();
- if ( fDateFormat ) {
- if ( fDateFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) {
- ((SimpleDateFormat*)fDateFormat)->toPattern(result);
- } else {
- status = U_UNSUPPORTED_ERROR;
- }
- }
+ result.setTo(fDatePattern);
}
return result;
}
{
if (!U_FAILURE(status)) {
result.remove();
- if ( fTimeFormat ) {
- if ( fTimeFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) {
- ((SimpleDateFormat*)fTimeFormat)->toPattern(result);
- } else {
- status = U_UNSUPPORTED_ERROR;
- }
- }
+ result.setTo(fTimePattern);
}
return result;
}
RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
{
if (!U_FAILURE(status)) {
- if ( fDateFormat && fDateFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) {
- status = U_UNSUPPORTED_ERROR;
- return;
- }
- if ( fTimeFormat && fTimeFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) {
- status = U_UNSUPPORTED_ERROR;
- return;
- }
- if ( fDateFormat ) {
- ((SimpleDateFormat*)fDateFormat)->applyPattern(datePattern);
- }
- if ( fTimeFormat ) {
- ((SimpleDateFormat*)fTimeFormat)->applyPattern(timePattern);
- }
+ fDatePattern.setTo(datePattern);
+ fTimePattern.setTo(timePattern);
}
}
+const DateFormatSymbols*
+RelativeDateFormat::getDateFormatSymbols() const
+{
+ return fDateTimeFormatter->getDateFormatSymbols();
+}
+
void RelativeDateFormat::loadDates(UErrorCode &status) {
CalendarData calData(fLocale, "gregorian", status);
UErrorCode tempStatus = status;
UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus);
- if(U_SUCCESS(tempStatus) && ures_getSize(dateTimePatterns) > DateFormat::kDateTime) {
- int32_t resStrLen = 0;
- const UChar *resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)DateFormat::kDateTime, &resStrLen, &tempStatus);
- fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus);
+ if(U_SUCCESS(tempStatus)) {
+ int32_t patternsSize = ures_getSize(dateTimePatterns);
+ if (patternsSize > kDateTime) {
+ int32_t resStrLen = 0;
+
+ int32_t glueIndex = kDateTime;
+ if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
+ // Get proper date time format
+ switch (fDateStyle) {
+ case kFullRelative:
+ case kFull:
+ glueIndex = kDateTimeOffset + kFull;
+ break;
+ case kLongRelative:
+ case kLong:
+ glueIndex = kDateTimeOffset + kLong;
+ break;
+ case kMediumRelative:
+ case kMedium:
+ glueIndex = kDateTimeOffset + kMedium;
+ break;
+ case kShortRelative:
+ case kShort:
+ glueIndex = kDateTimeOffset + kShort;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus);
+ fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus);
+ }
}
- UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", status);
+ UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status);
+ UResourceBundle *sb = ures_getByKeyWithFallback(rb, "fields", NULL, &status);
+ rb = ures_getByKeyWithFallback(sb, "day", rb, &status);
+ sb = ures_getByKeyWithFallback(rb, "relative", sb, &status);
+ ures_close(rb);
// set up min/max
fDayMin=-1;
fDayMax=1;
if(U_FAILURE(status)) {
fDatesLen=0;
+ ures_close(sb);
return;
}
- fDatesLen = ures_getSize(strings);
+ fDatesLen = ures_getSize(sb);
fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
// Load in each item into the array...
UResourceBundle *subString = NULL;
- while(ures_hasNext(strings) && U_SUCCESS(status)) { // iterate over items
- subString = ures_getNextResource(strings, subString, &status);
+ while(ures_hasNext(sb) && U_SUCCESS(status)) { // iterate over items
+ subString = ures_getNextResource(sb, subString, &status);
if(U_FAILURE(status) || (subString==NULL)) break;
n++;
}
ures_close(subString);
+ ures_close(sb);
// the fDates[] array could be sorted here, for direct access.
}