From 5d4f7e2492a04c03c3b521f31773f4031e5f7d5a Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Mon, 9 Apr 2007 22:54:03 +0000 Subject: [PATCH] ICU-2895 Relative Date Format X-SVN-Rev: 21382 --- .gitattributes | 2 + icu4c/source/i18n/reldtfmt.cpp | 308 +++++++++++++++++++++++++++++++++ icu4c/source/i18n/reldtfmt.h | 209 ++++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 icu4c/source/i18n/reldtfmt.cpp create mode 100644 icu4c/source/i18n/reldtfmt.h diff --git a/.gitattributes b/.gitattributes index d1acc8ed7f4..1b3b41f6b02 100644 --- a/.gitattributes +++ b/.gitattributes @@ -82,6 +82,8 @@ icu4c/source/data/mappings/ibm-964_P110_P100-1999.ucm -text icu4c/source/data/mappings/ibm-970_P110_P110-2006.ucm -text icu4c/source/i18n/persncal.cpp -text icu4c/source/i18n/persncal.h -text +icu4c/source/i18n/reldtfmt.cpp -text +icu4c/source/i18n/reldtfmt.h -text icu4c/source/samples/ucnv/data02.bin -text icu4c/source/test/perf/charperf/CharPerf_r.pl -text icu4c/source/test/perf/convperf/ConvPerf_r.pl -text diff --git a/icu4c/source/i18n/reldtfmt.cpp b/icu4c/source/i18n/reldtfmt.cpp new file mode 100644 index 00000000000..8b247144248 --- /dev/null +++ b/icu4c/source/i18n/reldtfmt.cpp @@ -0,0 +1,308 @@ +/* +******************************************************************************* +* Copyright (C) 2007, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +//#define DEBUG_RELDTFMT + +#include +#include + +#include "reldtfmt.h" +#include "unicode/msgfmt.h" + +#include "gregoimp.h" // for CalendarData + +U_NAMESPACE_BEGIN + + +/** + * An array of URelativeString structs is used to store the resource data loaded out of the bundle. + */ +struct URelativeString { + int32_t offset; /** offset of this item, such as, the relative date **/ + const UChar* string; /** string, or NULL if not set **/ + int32_t len; /** length of the string **/ +}; + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) + +RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : +DateFormat(other),fCalData(NULL), fStrings(NULL), dates(NULL), datesLen(0), fCombinedFormat(NULL) { + + dateStyle = other.dateStyle; + timeStyle = other.timeStyle; + if(other.fDateFormat != NULL) { + fDateFormat = (DateFormat*)other.fDateFormat->clone(); + } else { + fDateFormat = NULL; + } + + if(other.fTimeFormat != NULL) { + fTimeFormat = (DateFormat*)other.fTimeFormat->clone(); + } else { + fTimeFormat = NULL; + } +} + +RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status) + : dateStyle(dateStyle), timeStyle(timeStyle), fDateFormat(NULL), fCombinedFormat(NULL), fTimeFormat(NULL), + locale(locale),fCalData(NULL), fStrings(NULL), dates(NULL), datesLen(0) { + if(U_FAILURE(status) ) { + return; + } + + if(dateStyle != UDAT_NONE) { + EStyle newStyle = (EStyle)(dateStyle & ~UDAT_RELATIVE); + // Create a DateFormat in the non-relative style requested. + fDateFormat = createDateInstance(newStyle, locale); + } + if(timeStyle != UDAT_NONE) { + // don't support time style, for now + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Initialize the parent fCalendar, so that parse() works correctly. + initializeCalendar(NULL, locale, status); +} + +RelativeDateFormat::~RelativeDateFormat() { + delete fDateFormat; + delete fTimeFormat; + delete fCombinedFormat; + delete [] dates; +// do NOT: delete fStrings - as they are loaded from mapped memory, all owned by fCalData. + delete fCalData; +} + + +Format* RelativeDateFormat::clone(void) const { + RelativeDateFormat *other = new RelativeDateFormat(*this); + return other; +} + +UBool RelativeDateFormat::operator==(const Format& other) const { + if(DateFormat::operator==(other)) { + // DateFormat::operator== guarantees following cast is safe + RelativeDateFormat* that = (RelativeDateFormat*)&other; + return (dateStyle==that->dateStyle && + timeStyle==that->timeStyle && + locale==that->locale); + } + return FALSE; +} + +UnicodeString& RelativeDateFormat::format( Calendar& cal, + UnicodeString& appendTo, + FieldPosition& pos) const { + + UErrorCode status = U_ZERO_ERROR; + + // calculate the difference, in days, between 'cal' and now. + int dayDiff = dayDifference(cal, status); + + // look up string + int32_t len; + const UChar *theString = getStringForDay(dayDiff, len, status); + + if(U_FAILURE(status) || (theString==NULL)) { + // didn't find it. Fall through to the fDateFormat + if(fDateFormat != NULL) { + return fDateFormat->format(cal,appendTo,pos); + } else { + return appendTo; // no op + } + } else { + // found a relative string + return appendTo.append(theString, len); + } +} + +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;nfCalData = new CalendarData(locale, "gregorian", status); + } + + if(fStrings == NULL) { + // load the string object + UResourceBundle *theStrings = fCalData->getByKey3("fields", "day", "relative", status); + + if (U_FAILURE(status)) { + return NULL; + } + + ((RelativeDateFormat*)this)->fStrings = theStrings; // cast away const + } + return fStrings; +} + +const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { + if(U_FAILURE(status)) { + return NULL; + } + + if(dates == NULL) { + loadDates(status); + if(U_FAILURE(status)) { + return NULL; + } + } + + // no strings. + if(datesLen == 0) { + return NULL; + } + + // Is it outside the resource bundle's range? + if(day < dayMin || day > dayMax) { + return NULL; // don't have it. + } + + // Linear search the held strings + for(int n=0;ndayMin=-1; + nonConstThis->dayMax=1; + + if(U_FAILURE(status)) { + nonConstThis->datesLen=0; + return; + } + + nonConstThis->datesLen = ures_getSize(strings); + nonConstThis->dates = new URelativeString[datesLen]; + + // Load in each item into the array... + int n = 0; + + UResourceBundle *subString = NULL; + + while(ures_hasNext(strings) && U_SUCCESS(status)) { // iterate over items + subString = ures_getNextResource(strings, subString, &status); + + if(U_FAILURE(status) || (subString==NULL)) break; + + // key = offset # + const char *key = ures_getKey(subString); + + // load the string and length + int32_t aLen; + const UChar* aString = ures_getString(subString, &aLen, &status); + + if(U_FAILURE(status) || aString == NULL) break; + + // calculate the offset + int32_t offset = atoi(key); + + // set min/max + if(offset < dayMin) { + nonConstThis->dayMin = offset; + } + if(offset > dayMax) { + nonConstThis->dayMax = offset; + } + + // copy the string pointer + nonConstThis->dates[n].offset = offset; + nonConstThis->dates[n].string = aString; + nonConstThis->dates[n].len = aLen; + + n++; + } + + // the dates[] array could be sorted here, for direct access. +} + + +// this should to be in DateFormat, instead it was copied from SimpleDateFormat. + +Calendar* +RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) +{ + if(!U_FAILURE(status)) { + fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); + } + if (U_SUCCESS(status) && fCalendar == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return fCalendar; +} + +int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { + if(U_FAILURE(status)) { + return 0; + } + // TODO: Cache the nowCal to avoid heap allocs? + Calendar *nowCal = cal.clone(); + nowCal->setTime(Calendar::getNow(), status); + int32_t dayDiff = nowCal->fieldDifference(cal.getTime(status), Calendar::DATE, status); + delete nowCal; + return dayDiff; +} + +U_NAMESPACE_END + +#endif \ No newline at end of file diff --git a/icu4c/source/i18n/reldtfmt.h b/icu4c/source/i18n/reldtfmt.h new file mode 100644 index 00000000000..de7b991b279 --- /dev/null +++ b/icu4c/source/i18n/reldtfmt.h @@ -0,0 +1,209 @@ +/* +******************************************************************************* +* Copyright (C) 2007, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef RELDTFMT_H +#define RELDTFMT_H + +#include "unicode/utypes.h" + +/** + * \file + * \brief C++ API: Format and parse relative dates and times. + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/datefmt.h" + +U_NAMESPACE_BEGIN + +// forward declarations +class CalendarData; +class MessageFormat; + +// internal structure used for caching strings +struct URelativeString; + +/** + * This class is normally accessed using the kRelative or k...Relative values of EStyle as parameters to DateFormat::createDateInstance. + * + * Example: + * DateFormat *fullrelative = DateFormat::createDateInstance(DateFormat::kFullRelative, loc); + * + * @draft ICU 3.8 + */ + +class U_I18N_API RelativeDateFormat : public DateFormat { +public: + RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status); + + // overrides + /** + * Copy constructor. + * @draft ICU 3.8 + */ + RelativeDateFormat(const RelativeDateFormat&); + + /** + * Assignment operator. + * @draft ICU 3.8 + */ + RelativeDateFormat& operator=(const RelativeDateFormat&); + + /** + * Destructor. + * @draft ICU 3.8 + */ + virtual ~RelativeDateFormat(); + + /** + * Clone this Format object polymorphically. The caller owns the result and + * should delete it when done. + * @return A copy of the object. + * @draft ICU 3.8 + */ + virtual Format* clone(void) const; + + /** + * Return true if the given Format objects are semantically equal. Objects + * of different subclasses are considered unequal. + * @param other the object to be compared with. + * @return true if the given Format objects are semantically equal. + * @draft ICU 3.8 + */ + virtual UBool operator==(const Format& other) const; + + /** + * Format a date or time, which is the standard millis since 24:00 GMT, Jan + * 1, 1970. Overrides DateFormat pure virtual method. + *

+ * Example: using the US locale: "yyyy.MM.dd e 'at' HH:mm:ss zzz" ->> + * 1996.07.10 AD at 15:08:56 PDT + * + * @param cal Calendar set to the date and time to be formatted + * into a date/time string. + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param pos The formatting position. On input: an alignment field, + * if desired. On output: the offsets of the alignment field. + * @return Reference to 'appendTo' parameter. + * @draft ICU 3.8 + */ + virtual UnicodeString& format( Calendar& cal, + UnicodeString& appendTo, + FieldPosition& pos) const; + + + /** + * Parse a date/time string beginning at the given parse position. For + * example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date + * that is equivalent to Date(837039928046). + *

+ * By default, parsing is lenient: If the input is not in the form used by + * this object's format method but can still be parsed as a date, then the + * parse succeeds. Clients may insist on strict adherence to the format by + * calling setLenient(false). + * + * @param text The date/time string to be parsed + * @param cal a Calendar set to the date and time to be formatted + * into a date/time string. + * @param pos On input, the position at which to start parsing; on + * output, the position at which parsing terminated, or the + * start position if the parse failed. + * @return A valid UDate if the input could be parsed. + * @draft ICU 3.8 + */ + virtual void parse( const UnicodeString& text, + Calendar& cal, + ParsePosition& pos) const; +private: + DateFormat *fDateFormat; // the held date format + DateFormat *fTimeFormat; // the held time format + MessageFormat *fCombinedFormat; // the {0} {1} format. + + UDateFormatStyle dateStyle; + UDateFormatStyle timeStyle; + Locale locale; + + CalendarData *fCalData; // holds a reference to the resource data + UResourceBundle *fStrings; // the array of strings + + int32_t dayMin; // day id of lowest # + int32_t dayMax; // day id of highest # + int32_t datesLen; // Length of array + URelativeString *dates; // array of strings + + + /** + * Get the string at a specific offset. + * @param day day offset ( -1, 0, 1, etc.. ) + * @param len on output, length of string. + * @return the string, or NULL if none at that location. + */ + const UChar *getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const; + + /** + * Load the Date string array + */ + void loadDates(UErrorCode &status) const; + + /** + * Load the Strings resource needed for relative dates. Returns an error and null if it could not be loaded. + * Semantic const. + */ + UResourceBundle *getStrings(UErrorCode &status) const; + + /** + * @return the number of days in "until-now" + */ + static int32_t dayDifference(Calendar &until, UErrorCode &status); + + /** + * initializes fCalendar from parameters. Returns fCalendar as a convenience. + * @param adoptZone Zone to be adopted, or NULL for TimeZone::createDefault(). + * @param locale Locale of the calendar + * @param status Error code + * @return the newly constructed fCalendar + * @draft ICU 3.8 + */ + Calendar* initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status); + +public: + /** + * Return the class ID for this class. This is useful only for comparing to + * a return value from getDynamicClassID(). For example: + *

+     * .   Base* polymorphic_pointer = createPolymorphicObject();
+     * .   if (polymorphic_pointer->getDynamicClassID() ==
+     * .       erived::getStaticClassID()) ...
+     * 
+ * @return The class ID for all objects of this class. + * @draft ICU 3.8 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This + * method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() + * methods call this method. + * + * @return The class ID for this object. All objects of a + * given class have the same class ID. Objects of + * other classes have different class IDs. + * @draft ICU 3.8 + */ + virtual UClassID getDynamicClassID(void) const; +}; + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif // RELDTFMT_H +//eof