From f9a059009816218ec801b3b6e54524c7b2195293 Mon Sep 17 00:00:00 2001 From: Xiaomei Ji Date: Wed, 28 May 2008 00:33:44 +0000 Subject: [PATCH] ICU-6157 date interval format X-SVN-Rev: 24005 --- .gitattributes | 9 + icu4c/source/common/Makefile.in | 2 +- icu4c/source/common/dtintrv.cpp | 50 + icu4c/source/common/unicode/dtintrv.h | 166 +++ icu4c/source/i18n/Makefile.in | 2 +- icu4c/source/i18n/datefmt.cpp | 31 + icu4c/source/i18n/dtitv_impl.h | 93 ++ icu4c/source/i18n/dtitvfmt.cpp | 1495 +++++++++++++++++++++++ icu4c/source/i18n/dtitvinf.cpp | 583 +++++++++ icu4c/source/i18n/smpdtfmt.cpp | 95 ++ icu4c/source/i18n/unicode/datefmt.h | 29 +- icu4c/source/i18n/unicode/dtitvfmt.h | 1303 ++++++++++++++++++++ icu4c/source/i18n/unicode/dtitvinf.h | 541 ++++++++ icu4c/source/i18n/unicode/smpdtfmt.h | 46 + icu4c/source/i18n/unicode/udat.h | 88 ++ icu4c/source/test/intltest/Makefile.in | 2 +- icu4c/source/test/intltest/dtifmtts.cpp | 563 +++++++++ icu4c/source/test/intltest/dtifmtts.h | 41 + icu4c/source/test/intltest/itformat.cpp | 4 +- 19 files changed, 5138 insertions(+), 5 deletions(-) create mode 100644 icu4c/source/common/dtintrv.cpp create mode 100644 icu4c/source/common/unicode/dtintrv.h create mode 100644 icu4c/source/i18n/dtitv_impl.h create mode 100644 icu4c/source/i18n/dtitvfmt.cpp create mode 100644 icu4c/source/i18n/dtitvinf.cpp create mode 100644 icu4c/source/i18n/unicode/dtitvfmt.h create mode 100644 icu4c/source/i18n/unicode/dtitvinf.h create mode 100644 icu4c/source/test/intltest/dtifmtts.cpp create mode 100644 icu4c/source/test/intltest/dtifmtts.h diff --git a/.gitattributes b/.gitattributes index 7dbfb4db84d..6a588eeccd3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -48,14 +48,23 @@ README text !eol *.spp -text *.tri2 -text +icu4c/source/common/dtintrv.cpp -text icu4c/source/common/mutex.cpp -text +icu4c/source/common/unicode/dtintrv.h -text icu4c/source/data/coll/ur.txt -text icu4c/source/data/coll/ur_IN.txt -text icu4c/source/data/coll/ur_PK.txt -text icu4c/source/data/xml/collation/ur.xml -text +icu4c/source/i18n/dtitv_impl.h -text +icu4c/source/i18n/dtitvfmt.cpp -text +icu4c/source/i18n/dtitvinf.cpp -text +icu4c/source/i18n/unicode/dtitvfmt.h -text +icu4c/source/i18n/unicode/dtitvinf.h -text icu4c/source/samples/layout/cgnomelayout.c -text icu4c/source/samples/ucnv/data02.bin -text icu4c/source/test/compat/tzone.pl -text +icu4c/source/test/intltest/dtifmtts.cpp -text +icu4c/source/test/intltest/dtifmtts.h -text icu4c/source/test/perf/Makefile.in -text icu4c/source/test/perf/README -text icu4c/source/test/perf/strsrchperf/Makefile.in -text diff --git a/icu4c/source/common/Makefile.in b/icu4c/source/common/Makefile.in index 43b38661f22..18a285937b2 100644 --- a/icu4c/source/common/Makefile.in +++ b/icu4c/source/common/Makefile.in @@ -85,7 +85,7 @@ uarrsort.o brkiter.o ubrk.o brkeng.o dictbe.o triedict.o \ rbbi.o rbbidata.o rbbinode.o rbbirb.o rbbiscan.o rbbisetb.o rbbistbl.o rbbitblb.o \ serv.o servnotf.o servls.o servlk.o servlkf.o servrbf.o servslkf.o \ uidna.o usprep.o punycode.o \ -util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o mutex.o +util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o mutex.o dtintrv.o ## Header files to install HEADERS = $(srcdir)/unicode/*.h unicode/*.h diff --git a/icu4c/source/common/dtintrv.cpp b/icu4c/source/common/dtintrv.cpp new file mode 100644 index 00000000000..df9a519a75f --- /dev/null +++ b/icu4c/source/common/dtintrv.cpp @@ -0,0 +1,50 @@ +/******************************************************************************* +* Copyright (C) 2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTINTRV.CPP +* +******************************************************************************* +*/ + + + +#include "unicode/dtintrv.h" + + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateInterval) + +//DateInterval::DateInterval(){} + + +DateInterval::DateInterval(const UDate from, const UDate to) +: fromDate(from), + toDate(to) +{} + + +DateInterval::~DateInterval(){} + + +DateInterval::DateInterval(const DateInterval& other) +: UObject(other) { + *this = other; +} + + +DateInterval& +DateInterval::operator=(const DateInterval& other) { + if ( this != &other ) { + fromDate = other.fromDate; + toDate = other.toDate; + } + return *this; +} + + + +U_NAMESPACE_END + diff --git a/icu4c/source/common/unicode/dtintrv.h b/icu4c/source/common/unicode/dtintrv.h new file mode 100644 index 00000000000..88426dc9886 --- /dev/null +++ b/icu4c/source/common/unicode/dtintrv.h @@ -0,0 +1,166 @@ +/* +******************************************************************************* +* Copyright (C) 2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTINTRV.H +* +******************************************************************************* +*/ + +#ifndef __DTINTRV_H__ +#define __DTINTRV_H__ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + + +/** + * This class represents date interval. + * It is a pair of UDate representing from UDate 1 to UDate 2. + * @draft ICU 4.0 +**/ +class U_COMMON_API DateInterval : public UObject { +public: + + /** + * Constructor given from date and to date. + * @param fromDate The from date in date interval. + * @param toDate The to date in date interval. + * @draft ICU 4.0 + */ + DateInterval(const UDate fromDate, const UDate toDate); + + /** + * destructor + * @draft ICU 4.0 + */ + ~DateInterval(); + + /** + * Get the from date. + * @return the from date in dateInterval. + * @draft ICU 4.0 + */ + UDate getFromDate() const; + + /** + * Get the to date. + * @return the to date in dateInterval. + * @draft ICU 4.0 + */ + UDate getToDate() const; + + + /** + * 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 4.0 + */ + 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 4.0 + */ + virtual UClassID getDynamicClassID(void) const; + + + /** + * Copy constructor. + * @draft ICU 4.0 + */ + DateInterval(const DateInterval& other); + + /** + * Default assignment operator + * @draft ICU 4.0 + */ + DateInterval& operator=(const DateInterval&); + + /** + * Equality operator. + * @return TRUE if the two DateIntervals are the same + * @draft ICU 4.0 + */ + UBool operator==(const DateInterval& other) const; + + /** + * Non-equality operator + * @return TRUE if the two DateIntervals are not the same + * @draft ICU 4.0 + */ + UBool operator!=(const DateInterval& other) const; + + + /** + * clone this object. + * The caller owns the result and should delete it when done. + * @return a cloned DateInterval + * @draft ICU 4.0 + */ + DateInterval* clone() const; + +private: + /** + * Default constructor, not implemented. + * @draft ICU 4.0 + */ + DateInterval(); + + UDate fromDate; + UDate toDate; + +} ;// end class DateInterval + + +inline UDate +DateInterval::getFromDate() const { + return fromDate; +} + + +inline UDate +DateInterval::getToDate() const { + return toDate; +} + + +inline UBool +DateInterval::operator==(const DateInterval& other) const { + return ( fromDate == other.fromDate && toDate == other.toDate ); +} + + +inline UBool +DateInterval::operator!=(const DateInterval& other) const { + return ( !operator==(other) ); +} + + +inline DateInterval* +DateInterval::clone() const { + return new DateInterval(*this); +} + + + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index a26852c38f5..e9c41819c2f 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -80,7 +80,7 @@ regexcmp.o rematch.o repattrn.o regexst.o udatpg.o uregex.o uregexc.o \ ulocdata.o measfmt.o currfmt.o curramt.o currunit.o measure.o utmscale.o \ csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8.o inputext.o \ windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o \ -zonemeta.o zstrfmt.o plurrule.o plurfmt.o +zonemeta.o zstrfmt.o plurrule.o plurfmt.o dtitvfmt.o dtitvinf.o ## Header files to install HEADERS = $(srcdir)/unicode/*.h diff --git a/icu4c/source/i18n/datefmt.cpp b/icu4c/source/i18n/datefmt.cpp index d73ae5063f6..64f3466ef59 100644 --- a/icu4c/source/i18n/datefmt.cpp +++ b/icu4c/source/i18n/datefmt.cpp @@ -24,6 +24,7 @@ #include "unicode/ures.h" #include "unicode/datefmt.h" #include "unicode/smpdtfmt.h" +#include "unicode/dtptngen.h" #include "reldtfmt.h" #include "cstring.h" @@ -274,6 +275,36 @@ DateFormat::createInstance() return create(kShort, (EStyle) (kShort + kDateOffset), Locale::getDefault()); } +//---------------------------------------------------------------------- +DateFormat* U_EXPORT2 +DateFormat::createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale) +{ + UErrorCode status = U_ZERO_ERROR; + + DateTimePatternGenerator* dtptg = + DateTimePatternGenerator::createInstance(locale, status); + if ( dtptg == NULL || U_FAILURE(status) ) { + status = U_MEMORY_ALLOCATION_ERROR; + delete dtptg; + return NULL; + } + + // FIXME: use adjustFieldWidth later + const UnicodeString pattern = dtptg->getBestPattern(skeleton, status); + delete dtptg; + if ( U_FAILURE(status) ) { + return NULL; + } + SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status); + if ( U_FAILURE(status) ) { + delete dtfmt; + return NULL; + } + return dtfmt; +} + //---------------------------------------------------------------------- DateFormat* U_EXPORT2 diff --git a/icu4c/source/i18n/dtitv_impl.h b/icu4c/source/i18n/dtitv_impl.h new file mode 100644 index 00000000000..009d9a8b4ae --- /dev/null +++ b/icu4c/source/i18n/dtitv_impl.h @@ -0,0 +1,93 @@ +/* +******************************************************************************* +* Copyright (C) 2007-2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITV_IMPL.H +* +******************************************************************************* +*/ + + +#ifndef DTITV_IMPL_H__ +#define DTITV_IMPL_H__ + +/** + * \file + * \brief C++ API: Defines macros for interval format implementation + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/unistr.h" + + +#define QUOTE ((UChar)0x0027) +#define LOW_LINE ((UChar)0x005F) +#define COLON ((UChar)0x003A) +#define LEFT_CURLY_BRACKET ((UChar)0x007B) +#define RIGHT_CURLY_BRACKET ((UChar)0x007D) +#define SPACE ((UChar)0x0020) +#define EN_DASH ((UChar)0x2013) + +#define DIGIT_ZERO ((UChar)0x0030) +#define DIGIT_ONE ((UChar)0x0031) + +#define LOW_A ((UChar)0x0061) +#define LOW_B ((UChar)0x0062) +#define LOW_C ((UChar)0x0063) +#define LOW_D ((UChar)0x0064) +#define LOW_E ((UChar)0x0065) +#define LOW_F ((UChar)0x0066) +#define LOW_G ((UChar)0x0067) +#define LOW_H ((UChar)0x0068) +#define LOW_I ((UChar)0x0069) +#define LOW_J ((UChar)0x006a) +#define LOW_K ((UChar)0x006B) +#define LOW_L ((UChar)0x006C) +#define LOW_M ((UChar)0x006D) +#define LOW_N ((UChar)0x006E) +#define LOW_O ((UChar)0x006F) +#define LOW_P ((UChar)0x0070) +#define LOW_Q ((UChar)0x0071) +#define LOW_R ((UChar)0x0072) +#define LOW_S ((UChar)0x0073) +#define LOW_T ((UChar)0x0074) +#define LOW_U ((UChar)0x0075) +#define LOW_V ((UChar)0x0076) +#define LOW_W ((UChar)0x0077) +#define LOW_Y ((UChar)0x0079) +#define LOW_Z ((UChar)0x007A) + +#define CAP_A ((UChar)0x0041) +#define CAP_C ((UChar)0x0043) +#define CAP_D ((UChar)0x0044) +#define CAP_E ((UChar)0x0045) +#define CAP_F ((UChar)0x0046) +#define CAP_G ((UChar)0x0047) +#define CAP_H ((UChar)0x0048) +#define CAP_K ((UChar)0x004B) +#define CAP_L ((UChar)0x004C) +#define CAP_M ((UChar)0x004D) +#define CAP_O ((UChar)0x004F) +#define CAP_Q ((UChar)0x0051) +#define CAP_S ((UChar)0x0053) +#define CAP_T ((UChar)0x0054) +#define CAP_V ((UChar)0x0056) +#define CAP_W ((UChar)0x0057) +#define CAP_Y ((UChar)0x0059) +#define CAP_Z ((UChar)0x005A) + +//#define MINIMUM_SUPPORTED_CALENDAR_FIELD UCAL_MINUTE + +#define MAX_E_COUNT 5 +#define MAX_M_COUNT 5 +//#define MAX_INTERVAL_INDEX 4 +#define MAX_POSITIVE_INT 56632; + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif +//eof diff --git a/icu4c/source/i18n/dtitvfmt.cpp b/icu4c/source/i18n/dtitvfmt.cpp new file mode 100644 index 00000000000..1f9a42c0e45 --- /dev/null +++ b/icu4c/source/i18n/dtitvfmt.cpp @@ -0,0 +1,1495 @@ +/******************************************************************************* +* Copyright (C) 2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITVFMT.CPP +* +******************************************************************************* +*/ + + +//FIXME: put in compilation +//#define DTITVFMT_DEBUG 1 + +#include "unicode/msgfmt.h" +#include "unicode/dtptngen.h" +#include "unicode/dtitvfmt.h" +#include "unicode/dtitvinf.h" +#include "unicode/calendar.h" +#include "dtitv_impl.h" + +#ifdef DTITVFMT_DEBUG +#include +#include "cstring.h" +#endif + + +#if !UCONFIG_NO_FORMATTING + +U_NAMESPACE_BEGIN + + + +#ifdef DTITVFMT_DEBUG +#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } +#endif + + +static const UChar gDateFormatSkeleton[][11] = { +//yMMMMEEEEd +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, +//yMMMMd +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, +//yMMMd +{LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, +//yMd +{LOW_Y, CAP_M, LOW_D, 0} }; + + +static const char gDateTimePatternsTag[]="DateTimePatterns"; + + +// latestFirst: +static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON, 0}; + +// earliestFirst: +static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON, 0}; + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(UErrorCode& status) { + return createInstance(Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const Locale& locale, UErrorCode& status) { + return createDateTimeIntervalInstance(DateFormat::kDefault, + DateFormat::kDefault, locale, status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createDateIntervalInstance(DateFormat::EStyle style, + UErrorCode& status) { + return createDateIntervalInstance(style, Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createDateIntervalInstance(DateFormat::EStyle style, + const Locale& locale, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return NULL; + } + DateFormat* dtfmt = DateFormat::createDateInstance(style, locale); + DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); + // for CJK, even for non-short format, + // get skeleton will always return yMd. + // so, assign it directly instead of getting if from getSkeleton(). + UnicodeString skeleton = UnicodeString(gDateFormatSkeleton[(int32_t)style]); + return create(dtfmt, dtitvinf, &skeleton, status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createTimeIntervalInstance(DateFormat::EStyle style, + UErrorCode& status) { + return createTimeIntervalInstance(style, Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createTimeIntervalInstance(DateFormat::EStyle style, + const Locale& locale, + UErrorCode& status) { + return createDateTimeIntervalInstance(DateFormat::kNone, style, locale, status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createDateTimeIntervalInstance(DateFormat::EStyle dateStyle, + DateFormat::EStyle timeStyle, + UErrorCode& status) { + return createDateTimeIntervalInstance(dateStyle, timeStyle, Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createDateTimeIntervalInstance(DateFormat::EStyle dateStyle, + DateFormat::EStyle timeStyle, + const Locale& locale, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return NULL; + } + DateFormat* dtfmt = DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale); + DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); + return create(dtfmt, dtitvinf, status); +} + + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + UErrorCode& status) { + return createInstance(skeleton, adjustFieldWidth, Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return NULL; + } + DateFormat* dtfmt = DateFormat::createInstance(skeleton, adjustFieldWidth, locale); + +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + skeleton.extract(0, skeleton.length(), result, "UTF-8"); + UnicodeString pat; + ((SimpleDateFormat*)dtfmt)->toPattern(pat); + pat.extract(0, pat.length(), result_1, "UTF-8"); + sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1); + PRINTMESG(mesg) +#endif + + DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); + return create(dtfmt, dtitvinf, &skeleton, status); +} + + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + DateIntervalInfo* dtitvinf, + UErrorCode& status) { + return createInstance(skeleton, adjustFieldWidth, Locale::getDefault(), dtitvinf, status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale, + DateIntervalInfo* dtitvinf, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + delete dtitvinf; + return NULL; + } + DateFormat* dtfmt = DateFormat::createInstance(skeleton, adjustFieldWidth, locale); + return create(dtfmt, dtitvinf, &skeleton, status); +} + + +DateIntervalFormat::DateIntervalFormat() +: fInfo(NULL), + fDateFormat(NULL), + fFromCalendar(NULL), + fToCalendar(NULL) +{} + + +DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) +: Format(itvfmt), + fInfo(NULL), + fDateFormat(NULL), + fFromCalendar(NULL), + fToCalendar(NULL) { + *this = itvfmt; +} + + +DateIntervalFormat& +DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { + if ( this != &itvfmt ) { + delete fDateFormat; + delete fInfo; + delete fFromCalendar; + delete fToCalendar; + if ( itvfmt.fDateFormat ) { + fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone(); + } else { + fDateFormat = NULL; + } + if ( itvfmt.fInfo ) { + fInfo = itvfmt.fInfo->clone(); + } else { + fInfo = NULL; + } + if ( itvfmt.fFromCalendar ) { + fFromCalendar = itvfmt.fFromCalendar->clone(); + } else { + fFromCalendar = NULL; + } + if ( itvfmt.fToCalendar ) { + fToCalendar = itvfmt.fToCalendar->clone(); + } else { + fToCalendar = NULL; + } + fSkeleton = itvfmt.fSkeleton; + int8_t i; + for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { + fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; + } + } + return *this; +} + + +DateIntervalFormat::~DateIntervalFormat() { + delete fInfo; + delete fDateFormat; + delete fFromCalendar; + delete fToCalendar; +} + + +Format* +DateIntervalFormat::clone(void) const { + return new DateIntervalFormat(*this); +} + + +UBool +DateIntervalFormat::operator==(const Format& other) const { + if ( other.getDynamicClassID() == DateIntervalFormat::getStaticClassID() ) { + DateIntervalFormat* fmt = (DateIntervalFormat*)&other; +#ifdef DTITVFMT_DEBUG + UBool equal; + equal = (this == fmt); + + equal = (*fInfo == *fmt->fInfo); + equal = (*fDateFormat == *fmt->fDateFormat); + equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ; + equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ; + equal = (fSkeleton == fmt->fSkeleton); +#endif + UBool res; + res = ( this == fmt ) || + ( Format::operator==(other) && + fInfo && + ( *fInfo == *fmt->fInfo ) && + fDateFormat && + ( *fDateFormat == *fmt->fDateFormat ) && + fFromCalendar && + fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) && + fToCalendar && + fToCalendar->isEquivalentTo(*fmt->fToCalendar) && + fSkeleton == fmt->fSkeleton ); + int8_t i; + for (i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX && res == TRUE; ++i ) { + res = ( fIntervalPatterns[i].firstPart == + fmt->fIntervalPatterns[i].firstPart) && + ( fIntervalPatterns[i].secondPart == + fmt->fIntervalPatterns[i].secondPart ) && + ( fIntervalPatterns[i].laterDateFirst == + fmt->fIntervalPatterns[i].laterDateFirst) ; + } + return res; + } + return FALSE; +} + + + + +UnicodeString& +DateIntervalFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + if ( obj.getType() == Formattable::kObject ) { + const UObject* formatObj = obj.getObject(); + if (formatObj->getDynamicClassID() == DateInterval::getStaticClassID()){ + return format((DateInterval*)formatObj, appendTo, fieldPosition, status); + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; +} + + +UnicodeString& +DateIntervalFormat::format(const DateInterval* dtInterval, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + if ( fFromCalendar != NULL && fToCalendar != NULL && + fDateFormat != NULL && fInfo != NULL ) { + fFromCalendar->setTime(dtInterval->getFromDate(), status); + fToCalendar->setTime(dtInterval->getToDate(), status); + if ( U_SUCCESS(status) ) { + return format(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status); + } + } + return appendTo; +} + + +UnicodeString& +DateIntervalFormat::format(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + // not support different calendar types and time zones + //if ( fromCalendar.getType() != toCalendar.getType() ) { + if ( !fromCalendar.isEquivalentTo(toCalendar) ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + + // First, find the largest different calendar field. + UCalendarDateFields field = UCAL_FIELD_COUNT; + + if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { + field = UCAL_ERA; + } else if ( fromCalendar.get(UCAL_YEAR, status) != + toCalendar.get(UCAL_YEAR, status) ) { + field = UCAL_YEAR; + } else if ( fromCalendar.get(UCAL_MONTH, status) != + toCalendar.get(UCAL_MONTH, status) ) { + field = UCAL_MONTH; + } else if ( fromCalendar.get(UCAL_DATE, status) != + toCalendar.get(UCAL_DATE, status) ) { + field = UCAL_DATE; + } else if ( fromCalendar.get(UCAL_AM_PM, status) != + toCalendar.get(UCAL_AM_PM, status) ) { + field = UCAL_AM_PM; + } else if ( fromCalendar.get(UCAL_HOUR, status) != + toCalendar.get(UCAL_HOUR, status) ) { + field = UCAL_HOUR; + } else if ( fromCalendar.get(UCAL_MINUTE, status) != + toCalendar.get(UCAL_MINUTE, status) ) { + field = UCAL_MINUTE; + } + + if ( U_FAILURE(status) ) { + return appendTo; + } + if ( field == UCAL_FIELD_COUNT ) { + /* ignore the second/millisecond etc. small fields' difference. + * use single date when all the above are the same. + */ + return fDateFormat->format(fromCalendar, appendTo, pos); + } + + // following will not set wrong status + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; + + if ( intervalPattern.firstPart.isEmpty() && + intervalPattern.secondPart.isEmpty() ) { + if ( fDateFormat->isFieldUnitIgnored(field) ) { + /* the largest different calendar field is small than + * the smallest calendar field in pattern, + * return single date format. + */ + return fDateFormat->format(fromCalendar, appendTo, pos); + } + return fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); + } + // If the first part in interval pattern is empty, + // the 2nd part of it saves the full-pattern used in fall-back. + // For a 'real' interval pattern, the first part will never be empty. + if ( intervalPattern.firstPart.isEmpty() ) { + // fall back + UnicodeString originalPattern; + fDateFormat->toPattern(originalPattern); + fDateFormat->applyPattern(intervalPattern.secondPart); + appendTo = fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); + fDateFormat->applyPattern(originalPattern); + return appendTo; + } + Calendar* firstCal; + Calendar* secondCal; + if ( intervalPattern.laterDateFirst ) { + firstCal = &toCalendar; + secondCal = &fromCalendar; + } else { + firstCal = &fromCalendar; + secondCal = &toCalendar; + } + // break the interval pattern into 2 parts, + // first part should not be empty, + UnicodeString originalPattern; + fDateFormat->toPattern(originalPattern); + fDateFormat->applyPattern(intervalPattern.firstPart); + fDateFormat->format(*firstCal, appendTo, pos); + if ( !intervalPattern.secondPart.isEmpty() ) { + fDateFormat->applyPattern(intervalPattern.secondPart); + fDateFormat->format(*secondCal, appendTo, pos); + } + fDateFormat->applyPattern(originalPattern); + return appendTo; +} + + + +void +DateIntervalFormat::parseObject(const UnicodeString& /* source */, + Formattable& /* result */, + ParsePosition& /* parse_pos */) const { + // FIXME: THERE is no error code, + // then, where to set the not-supported error +} + + + + +void +DateIntervalFormat::setDateFormat(const DateFormat& newDateFormat, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + if ( newDateFormat.getDynamicClassID() == SimpleDateFormat::getStaticClassID() ) { + delete fDateFormat; + delete fFromCalendar; + delete fToCalendar; + fDateFormat = new SimpleDateFormat((SimpleDateFormat&)newDateFormat); + if ( fDateFormat && fDateFormat->getCalendar() ) { + fFromCalendar = fDateFormat->getCalendar()->clone(); + fToCalendar = fDateFormat->getCalendar()->clone(); + } else { + fFromCalendar = NULL; + fToCalendar = NULL; + } + if ( fInfo ) { + initializePattern(status); + } + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + + +void +DateIntervalFormat::adoptDateFormat(DateFormat* newDateFormat, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + if ( newDateFormat->getDynamicClassID() == SimpleDateFormat::getStaticClassID() ) { + delete fDateFormat; + delete fFromCalendar; + delete fToCalendar; + fDateFormat = (SimpleDateFormat*)newDateFormat; + if ( fDateFormat && fDateFormat->getCalendar() ) { + fFromCalendar = fDateFormat->getCalendar()->clone(); + fToCalendar = fDateFormat->getCalendar()->clone(); + } else { + fFromCalendar = NULL; + fToCalendar = NULL; + } + if ( fInfo ) { + initializePattern(status); + } + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + + +DateIntervalFormat::DateIntervalFormat(DateFormat* dtfmt, + DateIntervalInfo* dtItvInfo, + UErrorCode& status) +{ + DateIntervalFormat(dtfmt, dtItvInfo, NULL, status); +} + + + +DateIntervalFormat::DateIntervalFormat(DateFormat* dtfmt, + DateIntervalInfo* dtItvInfo, + const UnicodeString* skeleton, + UErrorCode& status) +: fInfo(0), + fDateFormat(0), + fFromCalendar(0), + fToCalendar(0) +{ + if ( U_FAILURE(status) ) { + delete dtfmt; + delete dtItvInfo; + return; + } + if ( dtfmt == NULL || dtItvInfo == NULL ) { + status = U_MEMORY_ALLOCATION_ERROR; + // safe to delete NULL + delete dtfmt; + delete dtItvInfo; + return; + } + if ( skeleton ) { + fSkeleton = *skeleton; + } + fInfo = dtItvInfo; + fDateFormat = (SimpleDateFormat*)dtfmt; + if ( dtfmt->getCalendar() ) { + fFromCalendar = dtfmt->getCalendar()->clone(); + fToCalendar = dtfmt->getCalendar()->clone(); + } else { + fFromCalendar = NULL; + fToCalendar = NULL; + } + initializePattern(status); +} + + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::create(DateFormat* dtfmt, + DateIntervalInfo* dtItvInfo, + UErrorCode& status) { + return create(dtfmt, dtItvInfo, NULL, status); +} + + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::create(DateFormat* dtfmt, + DateIntervalInfo* dtitvinf, + const UnicodeString* skeleton, + UErrorCode& status) { + DateIntervalFormat* f = new DateIntervalFormat(dtfmt, dtitvinf, + skeleton, status); + if ( f == NULL ) { + status = U_MEMORY_ALLOCATION_ERROR; + delete dtfmt; + delete dtitvinf; + } else if ( U_FAILURE(status) ) { + // safe to delete f, although nothing acutally is saved + delete f; + f = 0; + } + return f; +} + + + +/** + * Initialize interval patterns locale to this formatter + * + * This code is a bit complicated since + * 1. the interval patterns saved in resource bundle files are interval + * patterns based on date or time only. + * It does not have interval patterns based on both date and time. + * Interval patterns on both date and time are algorithm generated. + * + * For example, it has interval patterns on skeleton "dMy" and "hm", + * but it does not have interval patterns on skeleton "dMyhm". + * + * The rule to genearte interval patterns for both date and time skeleton are + * 1) when the year, month, or day differs, concatenate the two original + * expressions with a separator between, + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 11, 2007 10:10am" is + * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" + * + * 2) otherwise, present the date followed by the range expression + * for the time. + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 10, 2007 11:10am" is + * "Jan 10, 2007 10:10 am - 11:10am" + * + * 2. even a pattern does not request a certion calendar field, + * the interval pattern needs to include such field if such fields are + * different between 2 dates. + * For example, a pattern/skeleton is "hm", but the interval pattern + * includes year, month, and date when year, month, and date differs. + * + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ +void +DateIntervalFormat::initializePattern(UErrorCode& status) { + // FIXME: WHY can not getLocale() + const Locale& locale = fDateFormat->getSmpFmtLocale(); + DateTimePatternGenerator* dtpng = DateTimePatternGenerator::createInstance(locale, status); + if ( U_FAILURE(status) ) { + delete dtpng; + return; + } + if ( fSkeleton.isEmpty() ) { + UnicodeString fullPattern; + fDateFormat->toPattern(fullPattern); +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); + sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + // fSkeleton is already set by createDateIntervalInstance() + // or by createInstance(UnicodeString skeleton, .... ) + fSkeleton = dtpng->getSkeleton(fullPattern, status); + if ( U_FAILURE(status) ) { + delete dtpng; + return; + } + } + + // initialize the fIntervalPattern ordering + int8_t i; + for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { + fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); + } + + /* Check whether the skeleton is a combination of date and time. + * For the complication reason 1 explained above. + */ + UnicodeString dateSkeleton; + UnicodeString timeSkeleton; + UnicodeString normalizedTimeSkeleton; + UnicodeString normalizedDateSkeleton; + + + /* the difference between time skeleton and normalizedTimeSkeleton are: + * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton, + * 2. 'a' is omitted in normalized time skeleton. + * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized + * time skeleton + * + * The difference between date skeleton and normalizedDateSkeleton are: + * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton + * 2. 'E' and 'EE' are normalized into 'EEE' + * 3. 'MM' is normalized into 'M' + */ + getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton, + timeSkeleton, normalizedTimeSkeleton); + +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); + sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + + UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, + normalizedTimeSkeleton); + + if ( found == false ) { + // use fallback + // TODO: if user asks "m"(minute), but "d"(day) differ + if ( timeSkeleton.length() != 0 ) { + if ( dateSkeleton.length() == 0 ) { + // prefix with yMd + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]); + UnicodeString pattern =dtpng->getBestPattern(timeSkeleton, status); + if ( U_FAILURE(status) ) { + delete dtpng; + return; + } + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); + } else { + // TODO: fall back + } + } else { + // TODO: fall back + } + delete dtpng; + return; + } // end of skeleton not found + // interval patterns for skeleton are found in resource + if ( timeSkeleton.length() == 0 ) { + // done + } else if ( dateSkeleton.length() == 0 ) { + // prefix with yMd + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]); + UnicodeString pattern =dtpng->getBestPattern(timeSkeleton, status); + if ( U_FAILURE(status) ) { + delete dtpng; + return; + } + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); + } else { + /* if both present, + * 1) when the year, month, or day differs, + * concatenate the two original expressions with a separator between, + * 2) otherwise, present the date followed by the + * range expression for the time. + */ + /* + * 1) when the year, month, or day differs, + * concatenate the two original expressions with a separator between, + */ + // if field exists, use fall back + UnicodeString skeleton = fSkeleton; + if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { + // prefix skeleton with 'd' + skeleton.insert(0, LOW_D); + setFallbackPattern(UCAL_DATE, skeleton, dtpng, status); + } + if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { + // then prefix skeleton with 'M' + skeleton.insert(0, CAP_M); + setFallbackPattern(UCAL_MONTH, skeleton, dtpng, status); + } + if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { + // then prefix skeleton with 'y' + skeleton.insert(0, LOW_Y); + setFallbackPattern(UCAL_YEAR, skeleton, dtpng, status); + } + + /* + * 2) otherwise, present the date followed by the + * range expression for the time. + */ + // Need the Date/Time pattern for concatnation the date with + // the time interval. + // The date/time pattern ( such as {0} {1} ) is saved in + // calendar, that is why need to get the CalendarData here. + CalendarData* calData = new CalendarData(locale, NULL, status); + + if ( calData == NULL ) { + status = U_MEMORY_ALLOCATION_ERROR; + delete dtpng; + return; + } + + const UResourceBundle* dateTimePatternsRes = calData->getByKey( + gDateTimePatternsTag, status); + int32_t dateTimeFormatLength; + const UChar* dateTimeFormat = ures_getStringByIndex( + dateTimePatternsRes, + (int32_t)DateFormat::kDateTime, + &dateTimeFormatLength, &status); + if ( U_FAILURE(status) ) { + delete dtpng; + return; + } + + UnicodeString datePattern = dtpng->getBestPattern(dateSkeleton, status); + + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, + datePattern, UCAL_AM_PM, status); + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, + datePattern, UCAL_HOUR, status); + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, + datePattern, UCAL_MINUTE, status); + delete calData; + } + delete dtpng; +} + + + +void U_EXPORT2 +DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, + UnicodeString& dateSkeleton, + UnicodeString& normalizedDateSkeleton, + UnicodeString& timeSkeleton, + UnicodeString& normalizedTimeSkeleton) { + // dateSkeleton follows the sequence of y*M*E*d* + // timeSkeleton follows the sequence of hm*[v|z]? + int32_t ECount = 0; + int32_t dCount = 0; + int32_t MCount = 0; + int32_t yCount = 0; + int32_t hCount = 0; + int32_t mCount = 0; + int32_t vCount = 0; + int32_t zCount = 0; + int32_t i; + + for (i = 0; i < skeleton.length(); ++i) { + UChar ch = skeleton[i]; + switch ( ch ) { + case CAP_E: + dateSkeleton.append(ch); + ++ECount; + break; + case LOW_D: + dateSkeleton.append(ch); + ++dCount; + break; + case CAP_M: + dateSkeleton.append(ch); + ++MCount; + break; + case LOW_Y: + dateSkeleton.append(ch); + ++yCount; + break; + case CAP_G: + case CAP_Y: + case LOW_U: + case CAP_Q: + case LOW_Q: + case CAP_L: + case LOW_L: + case CAP_W: + case LOW_W: + case CAP_D: + case CAP_F: + case LOW_G: + case LOW_E: + case LOW_C: + normalizedDateSkeleton.append(ch); + dateSkeleton.append(ch); + break; + case LOW_A: + // 'a' is implicitly handled + timeSkeleton.append(ch); + break; + case LOW_H: + case CAP_H: + timeSkeleton.append(ch); + ++hCount; + break; + case LOW_M: + timeSkeleton.append(ch); + ++mCount; + break; + case LOW_Z: + ++zCount; + timeSkeleton.append(ch); + break; + case LOW_V: + ++vCount; + timeSkeleton.append(ch); + break; + // FIXME: what is the difference between CAP_V/Z and LOW_V/Z + case CAP_V: + case CAP_Z: + case LOW_K: + case CAP_K: + case LOW_J: + case LOW_S: + case CAP_S: + case CAP_A: + timeSkeleton.append(ch); + normalizedTimeSkeleton.append(ch); + break; + } + } + + /* generate normalized form for date*/ + if ( yCount != 0 ) { + normalizedDateSkeleton.append(LOW_Y); + } + if ( MCount != 0 ) { + if ( MCount < 3 ) { + normalizedDateSkeleton.append(CAP_M); + } else { + int32_t i; + for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { + normalizedDateSkeleton.append(CAP_M); + } + } + } + if ( ECount != 0 ) { + if ( ECount <= 3 ) { + normalizedDateSkeleton.append(CAP_E); + normalizedDateSkeleton.append(CAP_E); + normalizedDateSkeleton.append(CAP_E); + } else { + int32_t i; + for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { + normalizedDateSkeleton.append(CAP_E); + } + } + } + if ( dCount != 0 ) { + normalizedDateSkeleton.append(LOW_D); + } + + /* generate normalized form for time */ + if ( hCount != 0 ) { + normalizedTimeSkeleton.append(LOW_H); + } + if ( mCount != 0 ) { + normalizedTimeSkeleton.append(LOW_M); + } + if ( zCount != 0 ) { + normalizedTimeSkeleton.append(LOW_Z); + } + if ( vCount != 0 ) { + normalizedTimeSkeleton.append(LOW_V); + } +} + + +/** + * Generate date or time interval pattern from resource, + * and set them into the interval pattern locale to this formatter. + * + * It needs to handle the following: + * 1. need to adjust field width. + * For example, the interval patterns saved in DateIntervalInfo + * includes "dMMMy", but not "dMMMMy". + * Need to get interval patterns for dMMMMy from dMMMy. + * Another example, the interval patterns saved in DateIntervalInfo + * includes "hmv", but not "hmz". + * Need to get interval patterns for "hmz' from 'hmv' + * + * 2. there might be no pattern for 'y' differ for skeleton "Md", + * in order to get interval patterns for 'y' differ, + * need to look for it from skeleton 'yMd' + * + * @param dateSkeleton normalized date skeleton + * @param timeSkeleton normalized time skeleton + * @return whether the resource is found for the skeleton. + * TRUE if interval pattern found for the skeleton, + * FALSE otherwise. + * @draft ICU 4.0 + */ +UBool +DateIntervalFormat::setSeparateDateTimePtn( + const UnicodeString& dateSkeleton, + const UnicodeString& timeSkeleton) { + const UnicodeString* skeleton; + // if both date and time skeleton present, + // the final interval pattern might include time interval patterns + // ( when, am_pm, hour, minute differ ), + // but not date interval patterns ( when year, month, day differ ). + // For year/month/day differ, it falls back to fall-back pattern. + if ( timeSkeleton.length() != 0 ) { + skeleton = &timeSkeleton; + } else { + skeleton = &dateSkeleton; + } + + /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") + * are defined in resource, + * interval patterns for skeleton "dMMMMy" are calculated by + * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" + * 2. get the interval patterns for "dMMMy", + * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" + * getBestSkeleton() is step 1. + */ + // best skeleton, and the difference information + int8_t differenceInfo = 0; + const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, + differenceInfo); + + // difference: + // 0 means the best matched skeleton is the same as input skeleton + // 1 means the fields are the same, but field width are different + // 2 means the only difference between fields are v/z, + // -1 means there are other fields difference + if ( differenceInfo == -1 ) { + // skeleton has different fields, not only v/z difference + return false; + } + + if ( timeSkeleton.length() == 0 ) { + UnicodeString extendedSkeleton; + UnicodeString extendedBestSkeleton; + // only has date skeleton + setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + + UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, + differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + + if ( extended ) { + bestSkeleton = &extendedBestSkeleton; + skeleton = &extendedSkeleton; + } + setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + } else { + setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); + setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); + setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); + } + return true; +} + + + +void +DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, + const UnicodeString& skeleton, + DateTimePatternGenerator* dtpng, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + UnicodeString pattern =dtpng->getBestPattern(skeleton, status); + if ( U_FAILURE(status) ) { + return; + } + setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); +} + + + + +void +DateIntervalFormat::setPatternInfo(UCalendarDateFields field, + const UnicodeString* firstPart, + const UnicodeString* secondPart, + UBool laterDateFirst) { + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + UErrorCode status = U_ZERO_ERROR; + // following will not set any wrong status. + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; + if ( firstPart ) { + ptn.firstPart = *firstPart; + } + if ( secondPart ) { + ptn.secondPart = *secondPart; + } + ptn.laterDateFirst = laterDateFirst; +} + +void +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern) { + UBool order = fInfo->getDefaultOrder(); + setIntervalPattern(field, intervalPattern, order); +} + + +void +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern, + UBool laterDateFirst) { + const UnicodeString* pattern = &intervalPattern; + UBool order = laterDateFirst; + // check for "latestFirst:" or "earliestFirst:" prefix + int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]); + int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]); + UnicodeString realPattern; + if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { + order = true; + intervalPattern.extract(prefixLength, + intervalPattern.length() - prefixLength, + realPattern); + pattern = &realPattern; + } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, + earliestFirstLength) ) { + order = false; + intervalPattern.extract(earliestFirstLength, + intervalPattern.length() - earliestFirstLength, + realPattern); + pattern = &realPattern; + } + + int32_t splitPoint = splitPatternInto2Part(*pattern); + + UnicodeString firstPart; + UnicodeString secondPart; + pattern->extract(0, splitPoint, firstPart); + if ( splitPoint < pattern->length() ) { + pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); + } + setPatternInfo(field, &firstPart, &secondPart, order); +} + + + + +/** + * Generate interval pattern from existing resource + * + * It not only save the interval patterns, + * but also return the extended skeleton and its best match skeleton. + * + * @param field largest different calendar field + * @param skeleton skeleton + * @param bestSkeleton the best match skeleton which has interval pattern + * defined in resource + * @param differenceInfo the difference between skeleton and best skeleton + * 0 means the best matched skeleton is the same as input skeleton + * 1 means the fields are the same, but field width are different + * 2 means the only difference between fields are v/z, + * -1 means there are other fields difference + * + * @param extendedSkeleton extended skeleton + * @param extendedBestSkeleton extended best match skeleton + * @return whether the interval pattern is found + * through extending skeleton or not. + * TRUE if interval pattern is found by + * extending skeleton, FALSE otherwise. + * @draft ICU 4.0 + */ +UBool +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString* skeleton, + const UnicodeString* bestSkeleton, + int8_t differenceInfo, + UnicodeString* extendedSkeleton, + UnicodeString* extendedBestSkeleton) { + UErrorCode status = U_ZERO_ERROR; + // following getIntervalPattern() should not generate error status + const UnicodeString* pattern = fInfo->getIntervalPattern(*bestSkeleton, + field, status); + if ( pattern == NULL ) { + // single date + if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { + // do nothing, format will handle it + return false; + } + + // for 24 hour system, interval patterns in resource file + // might not include pattern when am_pm differ, + // which should be the same as hour differ. + // add it here for simplicity + if ( field == UCAL_AM_PM ) { + pattern = fInfo->getIntervalPattern(*bestSkeleton, + UCAL_HOUR, + status); + if ( pattern != NULL ) { + setIntervalPattern(field, *pattern); + } + return false; + } + // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, + // first, get best match pattern "MMMd", + // since there is no pattern for 'y' differs for skeleton 'MMMd', + // need to look for it from skeleton 'yMMMd', + // if found, adjust field width in interval pattern from + // "MMM" to "MMMM". + UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; + if ( extendedSkeleton ) { + *extendedSkeleton = *skeleton; + *extendedBestSkeleton = *bestSkeleton; + extendedSkeleton->insert(0, fieldLetter); + extendedBestSkeleton->insert(0, fieldLetter); + pattern = fInfo->getIntervalPattern(*extendedBestSkeleton, field, status); + } + } + if ( pattern != NULL ) { + if ( differenceInfo != 0 ) { + UnicodeString adjustIntervalPattern; + adjustFieldWidth(*skeleton, *bestSkeleton, *pattern, differenceInfo, + adjustIntervalPattern); + setIntervalPattern(field, adjustIntervalPattern); + } else { + setIntervalPattern(field, *pattern); + } + if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { + return TRUE; + } + } + return FALSE; +} + + + +int32_t U_EXPORT2 +DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { + UBool inQuote = false; + UChar prevCh = 0; + int32_t count = 0; + + /* repeatedPattern used to record whether a pattern has already seen. + It is a pattern applies to first calendar if it is first time seen, + otherwise, it is a pattern applies to the second calendar + */ + UBool patternRepeated[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int8_t PATTERN_CHAR_BASE = 0x41; + + /* loop through the pattern string character by character looking for + * the first repeated pattern letter, which breaks the interval pattern + * into 2 parts. + */ + int32_t i; + UBool foundRepetition = false; + for (i = 0; i < intervalPattern.length(); ++i) { + UChar ch = intervalPattern.charAt(i); + + if (ch != prevCh && count > 0) { + // check the repeativeness of pattern letter + UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; + if ( repeated == FALSE ) { + patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; + } else { + foundRepetition = true; + break; + } + count = 0; + } + if (ch == '\'') { + // Consecutive single quotes are a single quote literal, + // either outside of quotes or between quotes + if ((i+1) < intervalPattern.length() && + intervalPattern.charAt(i+1) == '\'') { + ++i; + } else { + inQuote = ! inQuote; + } + } + else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + // ch is a date-time pattern character + prevCh = ch; + ++count; + } + } + // check last pattern char, distinguish + // "dd MM" ( no repetition ), + // "d-d"(last char repeated ), and + // "d-d MM" ( repetition found ) + if ( count > 0 && foundRepetition == FALSE ) { + if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { + count = 0; + } + } + return (i - count); +} + + + +UnicodeString& +DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + // the fall back + // no need delete earlierDate and laterDate since they are adopted + UnicodeString* earlierDate = new UnicodeString(); + *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos); + UnicodeString* laterDate = new UnicodeString(); + *laterDate = fDateFormat->format(toCalendar, *laterDate, pos); + const UnicodeString& fallbackPattern = fInfo->getFallbackIntervalPattern(); + Formattable fmtArray[2]; + fmtArray[0].adoptString(earlierDate); + fmtArray[1].adoptString(laterDate); + + UnicodeString fallback; + MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status); + if ( U_FAILURE(status) ) { + return appendTo; + } + appendTo.append(fallback); + return appendTo; +} + + + + +UBool U_EXPORT2 +DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, + const UnicodeString& skeleton) +{ + const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; + return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; +} + + + +void U_EXPORT2 +DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, + const UnicodeString& bestMatchSkeleton, + const UnicodeString& bestIntervalPattern, + int8_t differenceInfo, + UnicodeString& adjustedPtn) { + adjustedPtn = bestIntervalPattern; + int32_t inputSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int32_t bestMatchSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); + DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); + if ( differenceInfo == 2 ) { + adjustedPtn.findAndReplace("v", "z"); + } + + UBool inQuote = false; + UChar prevCh = 0; + int32_t count = 0; + + const int8_t PATTERN_CHAR_BASE = 0x41; + + // loop through the pattern string character by character + int32_t adjustedPtnLength = adjustedPtn.length(); + int32_t i; + for (i = 0; i < adjustedPtnLength; ++i) { + UChar ch = adjustedPtn.charAt(i); + if (ch != prevCh && count > 0) { + // check the repeativeness of pattern letter + UChar skeletonChar = prevCh; + if ( skeletonChar == CAP_L ) { + // there is no "L" (always be "M") in skeleton, + // but there is "L" in pattern. + // for skeleton "M+", the pattern might be "...L..." + skeletonChar = CAP_M; + } + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + if ( fieldCount == count && inputFieldCount > fieldCount ) { + count = inputFieldCount - fieldCount; + int32_t j; + for ( j = 0; j < count; ++j ) { + adjustedPtn.insert(i, prevCh); + } + i += count; + adjustedPtnLength += count; + } + count = 0; + } + if (ch == '\'') { + // Consecutive single quotes are a single quote literal, + // either outside of quotes or between quotes + if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { + ++i; + } else { + inQuote = ! inQuote; + } + } + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + // ch is a date-time pattern character + prevCh = ch; + ++count; + } + } + if ( count > 0 ) { + // last item + // check the repeativeness of pattern letter + UChar skeletonChar = prevCh; + if ( skeletonChar == CAP_L ) { + // there is no "L" (always be "M") in skeleton, + // but there is "L" in pattern. + // for skeleton "M+", the pattern might be "...L..." + skeletonChar = CAP_M; + } + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + if ( fieldCount == count && inputFieldCount > fieldCount ) { + count = inputFieldCount - fieldCount; + int32_t j; + for ( j = 0; j < count; ++j ) { + adjustedPtn.append(prevCh); + } + } + } +} + + + +void +DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format, + int32_t formatLen, + const UnicodeString& datePattern, + UCalendarDateFields field, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + // following will not set wrong status + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; + if ( !timeItvPtnInfo.firstPart.isEmpty() ) { + // UnicodeString allocated here is adopted, so no need to delete + UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart); + timeIntervalPattern->append(timeItvPtnInfo.secondPart); + UnicodeString* dateStr = new UnicodeString(datePattern); + Formattable fmtArray[2]; + fmtArray[0].adoptString(timeIntervalPattern); + fmtArray[1].adoptString(dateStr); + UnicodeString combinedPattern; + MessageFormat::format(UnicodeString(TRUE, format, formatLen), + fmtArray, 2, combinedPattern, status); + if ( U_FAILURE(status) ) { + return; + } + setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); + } + // else: fall back + // it should not happen if the interval format defined is valid +} + + + +const UChar +DateIntervalFormat::fgCalendarFieldToPatternLetter[] = +{ + /*GyM*/ CAP_G, LOW_Y, CAP_M, + /*wWd*/ LOW_W, CAP_W, LOW_D, + /*DEF*/ CAP_D, CAP_E, CAP_F, + /*ahH*/ LOW_A, LOW_H, CAP_H, + /*m..*/ LOW_M, +}; + + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/dtitvinf.cpp b/icu4c/source/i18n/dtitvinf.cpp new file mode 100644 index 00000000000..80323831d4a --- /dev/null +++ b/icu4c/source/i18n/dtitvinf.cpp @@ -0,0 +1,583 @@ +/******************************************************************************* +* Copyright (C) 2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITVINF.CPP +* +******************************************************************************* +*/ + + +//FIXME: how to define it in compiler time +//#define DTITVINF_DEBUG 0 + + +#ifdef DTITVINF_DEBUG +#include +#endif + +#include "cstring.h" +#include "unicode/msgfmt.h" +#include "unicode/dtitvinf.h" +#include "dtitv_impl.h" + +#if !UCONFIG_NO_FORMATTING + + +U_NAMESPACE_BEGIN + + +#ifdef DTITVINF_DEBUG +#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo) + +static const char gIntervalDateTimePatternTag[]="IntervalDateTimePatterns"; +static const char gFallbackPatternTag[]="Fallback"; + +// {0} +static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, 0}; +// {1} +static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0}; + +// default fall-back +static const UChar gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0}; + + + +DateIntervalInfo::DateIntervalInfo(UErrorCode& status) +: fFallbackIntervalPattern(gDefaultFallbackPattern), + fFirstDateInPtnIsLaterDate(false), + fIntervalPatterns(NULL) +{ + if ( U_FAILURE(status) ) { + return; + } + fIntervalPatterns = initHash(status); +} + + + +DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status) +: fFallbackIntervalPattern(gDefaultFallbackPattern), + fFirstDateInPtnIsLaterDate(false), + fIntervalPatterns(NULL) +{ + if ( U_FAILURE(status) ) { + return; + } + initializeData(locale, status); +} + + + +void +DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + + if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) { + setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status); + setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status); + } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH || + lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) { + setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status); + } else { + setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status); + } +} + + +void +DateIntervalInfo::setFallbackIntervalPattern( + const UnicodeString& fallbackPattern) { + int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern, + sizeof(gFirstPattern)/sizeof(gFirstPattern[0]), 0); + int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern, + sizeof(gSecondPattern)/sizeof(gSecondPattern[0]), 0); + if ( firstPatternIndex > secondPatternIndex ) { + fFirstDateInPtnIsLaterDate = true; + } + fFallbackIntervalPattern = fallbackPattern; +} + + + +DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf) +: UObject(dtitvinf), + fIntervalPatterns(NULL) +{ + *this = dtitvinf; +} + + + +DateIntervalInfo& +DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) { + if ( this == &dtitvinf ) { + return *this; + } + + UErrorCode status = U_ZERO_ERROR; + deleteHash(fIntervalPatterns); + fIntervalPatterns = initHash(status); + if ( U_FAILURE(status) ) { + return *this; + } + copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status); + if ( U_FAILURE(status) ) { + return *this; + } + + fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern; + fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate; + return *this; +} + + +DateIntervalInfo* +DateIntervalInfo::clone() const { + return new DateIntervalInfo(*this); +} + + +DateIntervalInfo::~DateIntervalInfo() { + deleteHash(fIntervalPatterns); + fIntervalPatterns = NULL; +} + + +UBool +DateIntervalInfo::operator==(const DateIntervalInfo& other) const { + UBool equal = ( + fFallbackIntervalPattern == other.fFallbackIntervalPattern && + fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate ); + + if ( equal == TRUE ) { + equal = fIntervalPatterns->equals(*(other.fIntervalPatterns)); + } + + return equal; +} + + +const UnicodeString* +DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields field, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return NULL; + } + + const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton); + if ( patternsOfOneSkeleton != NULL ) { + int8_t index = (int8_t)calendarFieldToIntervalIndex(field, status); + if ( U_FAILURE(status) ) { + return NULL; + } + const UnicodeString& intervalPattern = patternsOfOneSkeleton[index]; + if ( !intervalPattern.isEmpty() ) { + return &intervalPattern; + } + } + return NULL; +} + + +void +DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status) +{ + fIntervalPatterns = initHash(status); + if ( U_FAILURE(status) ) { + return; + } + CalendarData* calData = new CalendarData(locale, NULL, status); + if ( calData == NULL ) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + const UResourceBundle* itvDtPtnResource = calData->getByKey( + gIntervalDateTimePatternTag, status); + + // look for fallback first, since it establishes the default order + const UChar* resStr; + int32_t resStrLen = 0; + resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, + gFallbackPatternTag, + &resStrLen, &status); + if ( U_FAILURE(status) ) { + delete calData; + return; + } + + UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen); + setFallbackIntervalPattern(pattern); + + int32_t size = ures_getSize(itvDtPtnResource); + int32_t index; + for ( index = 0; index < size; ++index ) { + UResourceBundle* oneRes = ures_getByIndex(itvDtPtnResource, index, + NULL, &status); + if ( U_FAILURE(status) ) { + delete calData; + return; + } + + const char* skeleton = ures_getKey(oneRes); + ures_close(oneRes); + if ( skeleton == NULL ) { + status = U_MISSING_RESOURCE_ERROR; + delete calData; + return; + } + if ( uprv_strcmp(skeleton, gFallbackPatternTag) == 0 ) { + continue; // fallback + } + + UResourceBundle* intervalPatterns = ures_getByKey(itvDtPtnResource, + skeleton, NULL, &status); + + if ( U_FAILURE(status) ) { + delete calData; + return; + } + + // return if interval patterns for skeleton not found + if ( intervalPatterns == NULL ) { + status = U_MISSING_RESOURCE_ERROR; + delete calData; + return; + } + + const UChar* pattern; + const char* key; + int32_t ptLength; + int32_t ptnNum = ures_getSize(intervalPatterns); + int32_t ptnIndex; + for ( ptnIndex = 0; ptnIndex < ptnNum; ++ptnIndex ) { + pattern = ures_getNextString(intervalPatterns, &ptLength, &key, + &status); + if ( U_FAILURE(status) ) { + delete calData; + return; + } + + UCalendarDateFields calendarField = UCAL_FIELD_COUNT; + if ( !uprv_strcmp(key, "y") ) { + calendarField = UCAL_YEAR; + } else if ( !uprv_strcmp(key, "M") ) { + calendarField = UCAL_MONTH; + } else if ( !uprv_strcmp(key, "d") ) { + calendarField = UCAL_DATE; + } else if ( !uprv_strcmp(key, "a") ) { + calendarField = UCAL_AM_PM; + } else if ( !uprv_strcmp(key, "h") ) { + calendarField = UCAL_HOUR; + } else if ( !uprv_strcmp(key, "m") ) { + calendarField = UCAL_MINUTE; + } + if ( calendarField != UCAL_FIELD_COUNT ) { + setIntervalPatternInternally(skeleton, calendarField, pattern,status); + } + } + ures_close(intervalPatterns); + } + delete calData; +} + + + +void +DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status) { + int8_t index = (int8_t)calendarFieldToIntervalIndex(lrgDiffCalUnit,status); + if ( U_FAILURE(status) ) { + return; + } + UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton)); + UBool emptyHash = false; + if ( patternsOfOneSkeleton == NULL ) { + patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX]; + emptyHash = true; + } + + patternsOfOneSkeleton[index] = intervalPattern; + if ( emptyHash == TRUE ) { + fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status); + } +} + + + +void +DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton, + int32_t* skeletonFieldWidth) { + const int8_t PATTERN_CHAR_BASE = 0x41; + int32_t i; + for ( i = 0; i < skeleton.length(); ++i ) { + // it is an ASCII char in skeleton + int8_t ch = (int8_t)skeleton.charAt(i); + ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE]; + } +} + + + +UBool +DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth, + char patternLetter) { + if ( patternLetter == 'M' ) { + if ( fieldWidth <= 2 && anotherFieldWidth > 2 || + fieldWidth > 2 && anotherFieldWidth <= 2 ) { + return true; + } + } + return false; +} + + + +const UnicodeString* +DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton, + int8_t& bestMatchDistanceInfo) const { +#ifdef DTITVINF_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + skeleton.extract(0, skeleton.length(), result, "UTF-8"); + sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + + int32_t inputSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int32_t skeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const int32_t DIFFERENT_FIELD = 0x1000; + const int32_t STRING_NUMERIC_DIFFERENCE = 0x100; + const int32_t BASE = 0x41; + + // hack for 'v' and 'z'. + // resource bundle only have time skeletons ending with 'v', + // but not for time skeletons ending with 'z'. + UBool replaceZWithV = false; + const UnicodeString* inputSkeleton = &skeleton; + UnicodeString copySkeleton; + if ( skeleton.indexOf('z') != -1 ) { + copySkeleton = skeleton; + copySkeleton.findAndReplace("z", "v"); + inputSkeleton = ©Skeleton; + replaceZWithV = true; + } + + parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth); + int32_t bestDistance = MAX_POSITIVE_INT; + const UnicodeString* bestSkeleton = NULL; + + // 0 means exact the same skeletons; + // 1 means having the same field, but with different length, + // 2 means only z/v differs + // -1 means having different field. + bestMatchDistanceInfo = 0; + int8_t fieldLength = sizeof(skeletonFieldWidth)/sizeof(skeletonFieldWidth[0]); + + int32_t pos = -1; + const UHashElement* elem = NULL; + while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) { + const UHashTok keyTok = elem->key; + UnicodeString* skeleton = (UnicodeString*)keyTok.pointer; +#ifdef DTITVINF_DEBUG + skeleton->extract(0, skeleton->length(), result, "UTF-8"); + sprintf(mesg, "available skeletons: skeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + // clear skeleton field width + int8_t i; + for ( i = 0; i < fieldLength; ++i ) { + skeletonFieldWidth[i] = 0; + } + parseSkeleton(*skeleton, skeletonFieldWidth); + // calculate distance + int32_t distance = 0; + int8_t fieldDifference = 1; + for ( i = 0; i < fieldLength; ++i ) { + int32_t inputFieldWidth = inputSkeletonFieldWidth[i]; + int32_t fieldWidth = skeletonFieldWidth[i]; + if ( inputFieldWidth == fieldWidth ) { + continue; + } + if ( inputFieldWidth == 0 ) { + fieldDifference = -1; + distance += DIFFERENT_FIELD; + } else if ( fieldWidth == 0 ) { + fieldDifference = -1; + distance += DIFFERENT_FIELD; + } else if (stringNumeric(inputFieldWidth, fieldWidth, + (char)(i+BASE) ) ) { + distance += STRING_NUMERIC_DIFFERENCE; + } else { + distance += (inputFieldWidth > fieldWidth) ? + (inputFieldWidth - fieldWidth) : + (fieldWidth - inputFieldWidth); + } + } + if ( distance < bestDistance ) { + bestSkeleton = skeleton; + bestDistance = distance; + bestMatchDistanceInfo = fieldDifference; + } + if ( distance == 0 ) { + bestMatchDistanceInfo = 0; + break; + } + } + if ( replaceZWithV && bestMatchDistanceInfo != -1 ) { + bestMatchDistanceInfo = 2; + } + return bestSkeleton; +} + + + +DateIntervalInfo::IntervalPatternIndex +DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field, + UErrorCode& status) { + IntervalPatternIndex index = kIPI_ERA; + switch ( field ) { + case UCAL_ERA: + break; + case UCAL_YEAR: + index = kIPI_YEAR; + break; + case UCAL_MONTH: + index = kIPI_MONTH; + break; + case UCAL_DATE: + case UCAL_DAY_OF_WEEK: + //case UCAL_DAY_OF_MONTH: + index = kIPI_DATE; + break; + case UCAL_AM_PM: + index = kIPI_AM_PM; + break; + case UCAL_HOUR: + case UCAL_HOUR_OF_DAY: + index = kIPI_HOUR; + break; + case UCAL_MINUTE: + index = kIPI_MINUTE; + break; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return index; +} + + + +void +DateIntervalInfo::deleteHash(Hashtable* hTable) +{ + if ( hTable == NULL ) { + return; + } + int32_t pos = -1; + const UHashElement* element = NULL; + while ( (element = hTable->nextElement(pos)) != NULL ) { + const UHashTok keyTok = element->key; + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + delete[] value; + } + delete fIntervalPatterns; +} + + + +Hashtable* +DateIntervalInfo::initHash(UErrorCode& status) { + + Hashtable* hTable; + if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + hTable->setValueCompartor(hashTableValueComparator); + return hTable; +} + + +UBool +DateIntervalInfo::hashTableValueComparator(UHashTok val1, UHashTok val2) { + const UnicodeString* pattern1 = (UnicodeString*)val1.pointer; + const UnicodeString* pattern2 = (UnicodeString*)val2.pointer; + UBool ret = TRUE; + int8_t i; + for ( i = 0; i < kIPI_MAX_INDEX && ret == TRUE; ++i ) { + ret = (pattern1[i] == pattern2[i]); + } + return ret; +} + + + +void +DateIntervalInfo::copyHash(const Hashtable* source, + Hashtable* target, + UErrorCode& status) { + int32_t pos = -1; + const UHashElement* element = NULL; + if ( source ) { + while ( (element = source->nextElement(pos)) != NULL ) { + const UHashTok keyTok = element->key; + const UnicodeString* key = (UnicodeString*)keyTok.pointer; + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX]; + int8_t i; + for ( i = 0; i < kIPI_MAX_INDEX; ++i ) { + copy[i] = value[i]; + } + target->put(UnicodeString(*key), copy, status); + if ( U_FAILURE(status) ) { + return; + } + } + } +} + + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index e7aa24a9028..4e190cca724 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -65,6 +65,8 @@ U_NAMESPACE_BEGIN +static const UChar PATTERN_CHAR_BASE = 0x40; + /** * Last-resort string to use for "GMT" when constructing time zone strings. */ @@ -580,6 +582,38 @@ SimpleDateFormat::format(const Formattable& obj, //---------------------------------------------------------------------- +/* Map calendar field into calendar field level. + * the larger the level, the smaller the field unit. + * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10, + * UCAL_MONTH level is 20. + */ +const int32_t +SimpleDateFormat::fgCalendarFieldToLevel[] = +{ + /*GyM*/ 0, 10, 20, + /*wW*/ 20, 30, + /*dDEF*/ 30, 20, 30, 30, + /*ahHm*/ 40, 50, 50, 60, + /*sS..*/ 70, 80, + /*z?Y*/ 0, 0, 10, + /*eug*/ 30, 10, 0, + /*A*/ 40 +}; + + +const int32_t +SimpleDateFormat::fgPatternCharToLevel[] = { + // A B C D E F G H I J K L M N O + -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, -1, + // P Q R S T U V W X Y Z + -1, 20, -1, 80, -1, -1, 0, 30, -1, 10, 0, -1, -1, -1, -1, -1, + // a b c d e f g h i j k l m n o + -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1, + // p q r s t u v w x y z + -1, 20, -1, 70, -1, 10, 0, 20, -1, 10, 0, -1, -1, -1, -1, -1 +}; + + // Map index into pattern character string to Calendar field number. const UCalendarDateFields SimpleDateFormat::fgPatternIndexToCalendarField[] = @@ -2427,6 +2461,67 @@ void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) initializeDefaultCentury(); // we need a new century (possibly) } + +//---------------------------------------------------------------------- + + +UBool +SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const { + return isFieldUnitIgnored(fPattern, field); +} + + +UBool +SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, + UCalendarDateFields field) { + int32_t fieldLevel = fgCalendarFieldToLevel[field]; + int32_t level; + UChar ch; + UBool inQuote = FALSE; + UChar prevCh = 0; + int32_t count = 0; + + for (int32_t i = 0; i < pattern.length(); ++i) { + ch = pattern[i]; + if (ch != prevCh && count > 0) { + level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; + if ( fieldLevel <= level ) { + return FALSE; + } + count = 0; + } + if (ch == QUOTE) { + if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) { + ++i; + } else { + inQuote = ! inQuote; + } + } + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + prevCh = ch; + ++count; + } + } + if ( count > 0 ) { + // last item + level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; + if ( fieldLevel <= level ) { + return FALSE; + } + } + return TRUE; +} + + + +const Locale& +SimpleDateFormat::getSmpFmtLocale(void) const { + return fLocale; +} + + + U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/unicode/datefmt.h b/icu4c/source/i18n/unicode/datefmt.h index 0022a99791b..23a3afb09c9 100644 --- a/icu4c/source/i18n/unicode/datefmt.h +++ b/icu4c/source/i18n/unicode/datefmt.h @@ -1,6 +1,6 @@ /* ******************************************************************************** - * Copyright (C) 1997-2007, International Business Machines + * Copyright (C) 1997-2008, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************** * @@ -413,6 +413,33 @@ public: */ static DateFormat* U_EXPORT2 createInstance(void); + /** + * Create a date/time formatter from skeleton and a given locale. + * + * Users are encouraged to use the skeleton macros defined in udat.h. + * For example, MONTH_DOW_DAY_LONG_FORMAT, which is "MMMMEEEEd", + * and which means the pattern should have day, month, and day-of-week + * fields, and follow the long date format defined in date time pattern. + * For example, for English, the full pattern should be + * "EEEE, MMMM d". + * + * Temporarily, this is an internal API, used by DateIntevalFormat only. + * There will be a new set of APIs for the same purpose coming soon. + * After which, this API will be replaced. + * + * @param skeleton the skeleton on which date format based. + * @param adjustFieldWidth whether adjust the skeleton field width or not. + * It is used for DateTimePatternGenerator to + * adjust field width when get + * full pattern from skeleton. + * @param locale the given locale. + * @return a simple date formatter which the caller owns. + * @internal ICU 4.0 + */ + static DateFormat* U_EXPORT2 createInstance(const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale); + /** * Creates a time formatter with the given formatting style for the given * locale. diff --git a/icu4c/source/i18n/unicode/dtitvfmt.h b/icu4c/source/i18n/unicode/dtitvfmt.h new file mode 100644 index 00000000000..cac55920abf --- /dev/null +++ b/icu4c/source/i18n/unicode/dtitvfmt.h @@ -0,0 +1,1303 @@ +/******************************************************************************** +* Copyright (C) 2008, International Business Machines Corporation and others. All Rights Reserved. +******************************************************************************* +* +* File DTITVFMT.H +* +******************************************************************************* +*/ + +#ifndef DTITVFMT_H__ +#define DTITVFMT_H__ + + +/** + * \file + * \brief C++ API: Format and parse date interval in a language-independent manner. + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/utypes.h" +#include "unicode/ucal.h" +#include "unicode/smpdtfmt.h" +#include "unicode/dtintrv.h" +#include "unicode/dtitvinf.h" + +U_NAMESPACE_BEGIN + + + +/** + * DateIntervalFormat is a class for formatting and parsing date + * intervals in a language-independent manner. + * + *

+ * Date interval means from one date to another date, + * for example, from "Jan 11, 2008" to "Jan 18, 2008". + * We introduced class DateInterval to represent it. + * DateInterval is a pair of UDate, which is + * the standard milliseconds since 24:00 GMT, Jan 1, 1970. + * + *

+ * DateIntervalFormat formats a DateInterval into + * text as compactly as possible. + * For example, the date interval format from "Jan 11, 2008" to "Jan 18,. 2008" + * is "Jan 11-18, 2008" for English. + * And it parses text into DateInterval, + * although initially, parsing is not supported. + * + *

+ * There is no structural information in date time patterns. + * For any punctuations and string literals inside a date time pattern, + * we do not know whether it is just a separator, or a prefix, or a suffix. + * Without such information, so, it is difficult to generate a sub-pattern + * (or super-pattern) by algorithm. + * So, formatting a DateInterval is pattern-driven. It is very + * similar to formatting in SimpleDateFormat. + * We introduce class DateIntervalInfo to save date interval + * patterns, similar to date time pattern in SimpleDateFormat. + * + *

+ * Logically, the interval patterns are mappings + * from (skeleton, the_largest_different_calendar_field) + * to (date_interval_pattern). + * + *

+ * A skeleton + *

    + *
  1. + * only keeps the field pattern letter and ignores all other parts + * in a pattern, such as space, punctuations, and string literals. + *
  2. + * hides the order of fields. + *
  3. + * might hide a field's pattern letter length. + * + * For those non-digit calendar fields, the pattern letter length is + * important, such as MMM, MMMM, and MMMMM; EEE and EEEE, + * and the field's pattern letter length is honored. + * + * For the digit calendar fields, such as M or MM, d or dd, yy or yyyy, + * the field pattern length is ignored and the best match, which is defined + * in date time patterns, will be returned without honor the field pattern + * letter length in skeleton. + *
+ * + *

+ * There is a set of pre-defined static skeleton strings. + * The skeletons defined consist of the desired calendar field set + * (for example, DAY, MONTH, YEAR) and the format length (long, medium, short) + * used in date time patterns. + * + * For example, skeleton YEAR_MONTH_MEDIUM_FORMAT consists month and year, + * and it's corresponding full pattern is medium format date pattern. + * So, the skeleton is "yMMM", for English, the full pattern is "MMM yyyy", + * which is the format by removing DATE from medium date format. + * + * For example, skeleton YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT consists day, month, + * year, and day-of-week, and it's corresponding full pattern is the medium + * format date pattern. So, the skeleton is "yMMMEEEd", for English, + * the full pattern is "EEE, MMM d, yyyy", which is the medium date format + * plus day-of-week. + * + *

+ * The calendar fields we support for interval formatting are: + * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute. + * Those calendar fields can be defined in the following order: + * year > month > date > hour (in day) > minute + * + * The largest different calendar fields between 2 calendars is the + * first different calendar field in above order. + * + * For example: the largest different calendar fields between "Jan 10, 2007" + * and "Feb 20, 2008" is year. + * + *

+ * There are pre-defined interval patterns for those pre-defined skeletons + * in locales' resource files. + * For example, for a skeleton YEAR_MONTH_DAY_MEDIUM_FORMAT, which is "yMMMd", + * in en_US, if the largest different calendar field between date1 and date2 + * is "year", the date interval pattern is "MMM d, yyyy - MMM d, yyyy", + * such as "Jan 10, 2007 - Jan 10, 2008". + * If the largest different calendar field between date1 and date2 is "month", + * the date interval pattern is "MMM d - MMM d, yyyy", + * such as "Jan 10 - Feb 10, 2007". + * If the largest different calendar field between date1 and date2 is "day", + * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007". + * + * For date skeleton, the interval patterns when year, or month, or date is + * different are defined in resource files. + * For time skeleton, the interval patterns when am/pm, or hour, or minute is + * different are defined in resource files. + * + *

+ * If a skeleton is not found in a locale's DateIntervalInfo, which means + * the interval patterns for the skeleton is not defined in resource file, + * the interval pattern will falls back to the interval "fallback" pattern + * defined in resource file. + * If the interval "fallback" pattern is not defined, the default fall-back + * is "{date0} - {data1}". + * + *

+ * For the combination of date and time, + * The rule to genearte interval patterns are: + *

    + *
  • + * 1) when the year, month, or day differs, falls back to fall-back + * interval pattern, which mostly is the concatenate the two original + * expressions with a separator between, + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 11, 2007 10:10am" is + * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" + *
  • + * 2) otherwise, present the date followed by the range expression + * for the time. + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 10, 2007 11:10am" is "Jan 10, 2007 10:10 am - 11:10am" + *
+ * + * + *

+ * If two dates are the same, the interval pattern is the single date pattern. + * For example, interval pattern from "Jan 10, 2007" to "Jan 10, 2007" is + * "Jan 10, 2007". + * + * Or if the presenting fields between 2 dates have the exact same values, + * the interval pattern is the single date pattern. + * For example, if user only requests year and month, + * the interval pattern from "Jan 10, 2007" to "Jan 20, 2007" is "Jan 2007". + * + *

+ * DateIntervalFormat needs the following information for correct + * formatting: time zone, calendar type, pattern, date format symbols, + * and date interval patterns. + * It can be instantiated in several ways: + *

    + *
  • + * 1. create a date interval instance based on default or given locale plus + * date format style FULL, or LONG, or MEDIUM, or SHORT. + * 2. create a time interval instance based on default or given locale plus + * time format style FULL, or LONG, or MEDIUM, or SHORT. + * 3. create date and time interval instance based on default or given locale + * plus data and time format style + * 4. create an instance using default or given locale plus default skeleton, + * which is "dMyhm" + * 5. create an instance using default or given locale plus given skeleton. + * Users are encouraged to created date interval formatter this way and + * to use the pre-defined skeleton macros, such as + * YEAR_MONTH_SHORT_FORMAT, which consists the calendar fields and + * the format style. + * 6. create an instance using default or given locale plus given skeleton + * plus a given DateIntervalInfo. + * This factory method is for powerful users who want to provide their own + * interval patterns. + * Locale provides the timezone, calendar, and format symbols information. + * Local plus skeleton provides full pattern information. + * DateIntervalInfo provides the date interval patterns. + *
  • + * + *

    + * For the calendar field pattern letter, such as G, y, M, d, a, h, H, m, s etc. + * DateIntervalFormat uses the same syntax as that of + * DateTime format. + * + *

    + * Code Sample: general usage + *

    + * \code
    + *   // the date interval object which the DateIntervalFormat formats on
    + *   // and parses into
    + *   DateInterval*  dtInterval = new DateInterval(1000*3600*24, 1000*3600*24*2);
    + *   UErrorCode status = U_ZERO_ERROR;
    + *   DateIntervalFormat* dtIntervalFmt = DateIntervalFormat::createInstance(
    + *                           YEAR_MONTH_DAY_FULL_FORMAT, 
    + *                           FALSE, Locale("en", "GB", ""), status);
    + *   UnicodeUnicodeString dateIntervalString;
    + *   FieldPosition pos = 0;
    + *   // formatting
    + *   dtIntervalFmt->format(dtInterval, dateIntervalUnicodeString, pos, status);
    + *   delete dtIntervalFmt;
    + * \endcode
    + * 
    + */ + +class U_I18N_API DateIntervalFormat : public Format { +public: + + /** + * Construct a DateIntervalFormat from default locale and + * default date time instance. + * + * This is a convenient override of getDateTimeIntervalInstance() with + * the date and time style value as DEFAULT. + * + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createInstance(UErrorCode& status); + + + /** + * Construct a DateIntervalFormat using given locale and + * default date time instance. + * + * This is a convenient override of getDateTimeIntervalInstance() with + * the date and time style value as DEFAULT. + * + * @param locale the given locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter which the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createInstance(const Locale& locale, + UErrorCode& status); + + + /** + * Construct a DateIntervalFormat using default locale. + * + * This is a convenient override of + * getDateIntervalInstance(int, ULocale) + * with the locale value as default locale. + * + * @param style The given date formatting style. For example, + * SHORT for "M/d/yy" in the US locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createDateIntervalInstance( + DateFormat::EStyle style, + UErrorCode& status); + + /** + * Construct a DateIntervalFormat using given locale. + * + * The interval pattern is based on the date format only. + * For full date format, interval pattern is based on skeleton "EEEEdMMMMy". + * For long date format, interval pattern is based on skeleton "dMMMMy". + * For medium date format, interval pattern is based on skeleton "dMMMy". + * For short date format, interval pattern is based on skeleton "dMy". + * + * @param style The given date formatting style. For example, + * SHORT for "M/d/yy" in the US locale. + * @param locale The given locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createDateIntervalInstance( + DateFormat::EStyle style, + const Locale& locale, + UErrorCode& status); + + + /** + * Construct a DateIntervalFormat using default locale. + * The interval pattern is based on the time format only. + * + * This is the convenient override of getDateTimeIntervalInstance() + * with the date style as NONE and a given time style. + * + * @param style The given time formatting style. For example, + * SHORT for "h:mm a" in the US locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createTimeIntervalInstance( + DateFormat::EStyle style, + UErrorCode& status); + + + + /** + * Construct a DateIntervalFormat using given locale. + * The interval pattern is based on the time format only. + * + * This is the convenient override of getDateTimeIntervalInstance() + * with the date style as NONE and a given time style. + * + * @param style The given time formatting style. For example, + * SHORT for "h:mm a" in the US locale. + * @param locale The given locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createTimeIntervalInstance( + DateFormat::EStyle style, + const Locale& locale, + UErrorCode& status); + + /** + * Construct a DateIntervalFormat using default locale. + * The interval pattern is based on the date/time format. + * + * This is a convenient override of + * getDateTimeIntervalInstance(int, int, ULocale) + * with the locale value as default locale. + * + * @param dateStyle The given date formatting style. For example, + * SHORT for "M/d/yy" in the US locale. + * @param timeStyle The given time formatting style. For example, + * SHORT for "h:mm a" in the US locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createDateTimeIntervalInstance( + DateFormat::EStyle dateStyle, + DateFormat::EStyle timeStyle, + UErrorCode& status); + + + /** + * Construct a DateIntervalFormat using given locale. + * The interval pattern is based on the date/time format. + * + * The interval pattern is based on the date/time format. + * + * @param dateStyle The given date formatting style. For example, + * SHORT for "M/d/yy" in the US locale. + * @param timeStyle The given time formatting style. For example, + * SHORT for "h:mm a" in the US locale. + * @param locale The given locale. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createDateTimeIntervalInstance( + DateFormat::EStyle dateStyle, + DateFormat::EStyle timeStyle, + const Locale& locale, + UErrorCode& status); + + + /** + * Construct a DateIntervalFormat from skeleton and the default locale. + * + * This is a convenient override of + * createInstance(UnicodeString skeleton, UBool adjustFieldWidth, ULocale locale) + * with the value of locale as default locale. + * + * @param skeleton the skeleton on which interval format based. + * @param adjustFieldWidth whether adjust the skeleton field width or not + * It is used for DateTimePatternGenerator on + * whether to adjust field width when get + * full pattern from skeleton + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createInstance( + const UnicodeString& skeleton, + UBool adjustFieldWidth, + UErrorCode& status); + + /** + * Construct a DateIntervalFormat from skeleton and a given locale. + * + * Following are the skeletons (defined in udate.h) having predefined + * interval patterns in resource files. + * Users are encouraged to use those macros. + * For example: + * DateIntervalFormat::createInstance(MONTH_DAY_FULL_FORMAT, FALSE, status) + * + * + * #define YEAR_MONTH_DOW_DAY_LONG_FORMAT "yMMMMEEEEd" + * #define YEAR_MONTH_DAY_LONG_FORMAT "yMMMMd" + * #define MONTH_DAY_LONG_FORMAT "MMMMd" + * #define YEAR_MONTH_LONG_FORMAT "yMMMM" + * #define MONTH_DOW_DAY_LONG_FORMAT "MMMMEEEEd" + * #define YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT "yMMMEEEd" + * #define YEAR_MONTH_DAY_MEDIUM_FORMAT "yMMMd" + * #define MONTH_DAY_MEDIUM_FORMAT "MMMd" + * #define YEAR_MONTH_MEDIUM_FORMAT "yMMM" + * #define MONTH_DOW_DAY_MEDIUM_FORMAT "MMMEEEd" + * #define YEAR_MONTH_DOW_DAY_SHORT_FORMAT "yMEEEd" + * #define YEAR_MONTH_DAY_SHORT_FORMAT "yMd" + * #define MONTH_DAY_SHORT_FORMAT "Md" + * #define YEAR_MONTH_SHORT_FORMAT "yM" + * #define MONTH_DOW_DAY_SHORT_FORMAT "MEEEd" + * #define DAY_ONLY_SHORT_FORMAT "d" + * #define DOW_DAY_SHORT_FORMAT "EEEd" + * #define YEAR_ONLY_SHORT_FORMAT "y" + * #define MONTH_ONLY_SHORT_FORMAT "M" + * #define MONTH_ONLY_MEDIUM_FORMAT "MMM" + * #define MONTH_ONLY_LONG_FORMAT "MMMM" + * #define HOUR_MINUTE_FORMAT "hm" + * #define HOUR_MINUTE_GENERAL_TZ_FORMAT "hmv" + * #define HOUR_MINUTE_DAYLIGNT_TZ_FORMAT "hmz" + * #define HOUR_ONLY_FORMAT "h" + * #define HOUR_GENERAL_TZ_FORMAT "hv" + * #define HOUR_DAYLIGNT_TZ_FORMAT "hz" + * + * Those skeletons have pre-defined interval patterns in resource files. + * Users are encouraged to use them. + * For example: + * DateIntervalFormat.createInstance(MONTH_DAY_FULL_FORMAT, false, loc); + * + * The given Locale provides the interval patterns. + * For example, for en_GB, if skeleton is YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT, + * which is "yMMMEEEd", + * the interval patterns defined in resource file to above skeleton are: + * "EEE, d MMM, yyyy - EEE, d MMM, yyyy" for year differs, + * "EEE, d MMM - EEE, d MMM, yyyy" for month differs, + * "EEE, d - EEE, d MMM, yyyy" for day differs, + * @param skeleton the skeleton on which interval format based. + * @param adjustFieldWidth whether adjust the skeleton field width or not + * It is used for DateTimePatternGenerator on + * whether to adjust field width when get + * full pattern from skeleton + * @param locale the given locale + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + + static DateIntervalFormat* U_EXPORT2 createInstance( + const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale, + UErrorCode& status); + + + + + /** + * Construct a DateIntervalFormat from skeleton + * DateIntervalInfo, and default locale. + * + * This is a convenient override of + * createInstance(UnicodeString skeleton, UBool adjustFieldWidth, + * ULocale locale, DateIntervalInfo dtitvinf) + * with the locale value as default locale. + * + * Note: the DateIntervalFormat takes ownership of + * DateIntervalInfo objects. + * Caller should not delete them. + * + * @param skeleton the skeleton on which interval format based. + * @param adjustFieldWidth whether adjust the skeleton field width or not + * It is used for DateTimePatternGenerator on + * whether to adjust field width when get + * full pattern from skeleton + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createInstance( + const UnicodeString& skeleton, + UBool adjustFieldWidth, + DateIntervalInfo* dtitvinf, + UErrorCode& status); + + /** + * Construct a DateIntervalFormat from skeleton + * a DateIntervalInfo, and the given locale. + * + * Following are the skeletons (defined in udate.h) having predefined + * interval patterns in resource files. + * Users are encouraged to use those macros. + * For example: + * DateIntervalFormat::createInstance(MONTH_DAY_FULL_FORMAT, FALSE, status) + * + * #define YEAR_MONTH_DOW_DAY_LONG_FORMAT "yMMMMEEEEd" + * #define YEAR_MONTH_DAY_LONG_FORMAT "yMMMMd" + * #define MONTH_DAY_LONG_FORMAT "MMMMd" + * #define YEAR_MONTH_LONG_FORMAT "yMMMM" + * #define MONTH_DOW_DAY_LONG_FORMAT "MMMMEEEEd" + * #define YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT "yMMMEEEd" + * #define YEAR_MONTH_DAY_MEDIUM_FORMAT "yMMMd" + * #define MONTH_DAY_MEDIUM_FORMAT "MMMd" + * #define YEAR_MONTH_MEDIUM_FORMAT "yMMM" + * #define MONTH_DOW_DAY_MEDIUM_FORMAT "MMMEEEd" + * #define YEAR_MONTH_DOW_DAY_SHORT_FORMAT "yMEEEd" + * #define YEAR_MONTH_DAY_SHORT_FORMAT "yMd" + * #define MONTH_DAY_SHORT_FORMAT "Md" + * #define YEAR_MONTH_SHORT_FORMAT "yM" + * #define MONTH_DOW_DAY_SHORT_FORMAT "MEEEd" + * #define DAY_ONLY_SHORT_FORMAT "d" + * #define DOW_DAY_SHORT_FORMAT "EEEd" + * #define YEAR_ONLY_SHORT_FORMAT "y" + * #define MONTH_ONLY_SHORT_FORMAT "M" + * #define MONTH_ONLY_MEDIUM_FORMAT "MMM" + * #define MONTH_ONLY_LONG_FORMAT "MMMM" + * #define HOUR_MINUTE_FORMAT "hm" + * #define HOUR_MINUTE_GENERAL_TZ_FORMAT "hmv" + * #define HOUR_MINUTE_DAYLIGNT_TZ_FORMAT "hmz" + * #define HOUR_ONLY_FORMAT "h" + * #define HOUR_GENERAL_TZ_FORMAT "hv" + * #define HOUR_DAYLIGNT_TZ_FORMAT "hz" + * + * Those skeletons have pre-defined interval patterns in resource files. + * Users are encouraged to use them. + * For example: + * DateIntervalFormat.createInstance(MONTH_DAY_FULL_FORMAT, false, loc,itvinf); + * + * the DateIntervalInfo provides the interval patterns. + * + * User are encouraged to set default interval pattern in DateIntervalInfo + * as well, if they want to set other interval patterns ( instead of + * reading the interval patterns from resource files). + * When the corresponding interval pattern for a largest calendar different + * field is not found ( if user not set it ), interval format fallback to + * the default interval pattern. + * If user does not provide default interval pattern, it fallback to + * "{date0} - {date1}" + * + * Note: the DateIntervalFormat takes ownership of + * DateIntervalInfo objects. + * Caller should not delete them. + * + * @param skeleton the skeleton on which interval format based. + * @param adjustFieldWidth whether adjust the skeleton field width or not + * It is used for DateTimePatternGenerator on + * whether to adjust field width when get + * full pattern from skeleton + * @param locale the given locale + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param status output param set to success/failure code on exit + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 createInstance( + const UnicodeString& skeleton, + UBool adjustFieldWidth, + const Locale& locale, + DateIntervalInfo* dtitvinf, + UErrorCode& status); + + /** + * Destructor. + * @draft ICU 4.0 + */ + virtual ~DateIntervalFormat(); + + /** + * Clone this Format object polymorphically. The caller owns the result and + * should delete it when done. + * @return A copy of the object. + * @draft ICU 4.0 + */ + 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 4.0 + */ + virtual UBool operator==(const Format& other) const; + + /** + * Return true if the given Format objects are not 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 not semantically equal. + * @draft ICU 4.0 + */ + UBool operator!=(const Format& other) const; + + /** + * Format an object to produce a string. This method handles Formattable + * objects with a DateInterval type. + * If a the Formattable object type is not a DateInterval, + * then it returns a failing UErrorCode. + * + * @param obj The object to format. + * Must be a DateInterval. + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @param status Output param filled with success/failure status. + * @return Reference to 'appendTo' parameter. + * @draft ICU 4.0 + */ + virtual UnicodeString& format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const ; + + + + /** + * Format a DateInterval to produce a string. + * + * @param dtInterval DateInterval to be formatted. + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @param status Output param filled with success/failure status. + * @return Reference to 'appendTo' parameter. + * @draft ICU 4.0 + */ + UnicodeString& format(const DateInterval* dtInterval, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const ; + + + /** + * Format 2 Calendars to produce a string. + * + * Note: "fromCalendar" and "toCalendar" are not const, + * since calendar is not const in SimpleDateFormat::format(Calendar&), + * + * @param fromCalendar calendar set to the from date in date interval + * to be formatted into date interval stirng + * @param toCalendar calendar set to the to date in date interval + * to be formatted into date interval stirng + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @param status Output param filled with success/failure status. + * Caller needs to make sure it is SUCCESS + * at the function entrance + * @return Reference to 'appendTo' parameter. + * @draft ICU 4.0 + */ + UnicodeString& format(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const ; + + /** + * Parse a string to produce an object. This methods handles parsing of + * date time interval strings into Formattable objects with + * DateInterval type, which is a pair of UDate. + *

    + * In ICU 4.0, date interval format is not supported. + *

    + * Before calling, set parse_pos.index to the offset you want to start + * parsing at in the source. After calling, parse_pos.index is the end of + * the text you parsed. If error occurs, index is unchanged. + *

    + * When parsing, leading whitespace is discarded (with a successful parse), + * while trailing whitespace is left as is. + *

    + * See Format::parseObject() for more. + * + * @param source The string to be parsed into an object. + * @param result Formattable to be set to the parse result. + * If parse fails, return contents are undefined. + * @param parse_pos The position to start parsing at. Upon return + * this param is set to the position after the + * last character successfully parsed. If the + * source is not parsed successfully, this param + * will remain unchanged. + * @return A newly created Formattable* object, or NULL + * on failure. The caller owns this and should + * delete it when done. + * @draft ICU 4.0 + */ + virtual void parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& parse_pos) const; + + + /** + * Gets the date time interval patterns. + * @return a copy of the date time interval patterns associated with + * this date interval formatter. + * @draft ICU 4.0 + */ + const DateIntervalInfo* getDateIntervalInfo(void) const; + + + /** + * Set the date time interval patterns. + * @param newIntervalPatterns the given interval patterns to copy. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void setDateIntervalInfo(const DateIntervalInfo& newIntervalPatterns, + UErrorCode& status); + + /** + * Set the date time interval patterns. + * The caller no longer owns the DateIntervalInfo object and + * should not delete it after making this call. + * @param newIntervalPatterns the given interval patterns to copy. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void adoptDateIntervalInfo(DateIntervalInfo* newIntervalPatterns, + UErrorCode& status); + + + /** + * Gets the date formatter + * @return a copy of the date formatter associated with + * this date interval formatter. + * @draft ICU 4.0 + */ + const DateFormat* getDateFormat(void) const; + + + /** + * Set the date formatter. + * @param newDateFormat the given date formatter to copy. + * caller needs to make sure that + * it is a SimpleDateFormatter. + * @param status Output param set to success/failure code. + * caller needs to make sure it is SUCCESS + * at the function entrance. + * @draft ICU 4.0 + */ + void setDateFormat(const DateFormat& newDateFormat, UErrorCode& status); + + /** + * Set the date formatter. + * The caller no longer owns the DateFormat object and + * should not delete it after making this call. + * @param newDateFormat the given date formatter to copy. + * caller needs to make sure that + * it is a SimpleDateFormatter. + * @param status Output param set to success/failure code. + * caller needs to make sure it is SUCCESS + * at the function entrance. + * @draft ICU 4.0 + */ + void adoptDateFormat(DateFormat* newDateFormat, UErrorCode& status); + + + /** + * 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 4.0 + */ + 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 4.0 + */ + virtual UClassID getDynamicClassID(void) const; + +protected: + + /** + * Copy constructor. + * @draft ICU 4.0 + */ + DateIntervalFormat(const DateIntervalFormat&); + + /** + * Assignment operator. + * @draft ICU 4.0 + */ + DateIntervalFormat& operator=(const DateIntervalFormat&); + +private: + + /** + * Save the interval pattern information. + * Interval pattern consists of 2 single date patterns and the separator. + * For example, interval pattern "MMM d - MMM d, yyyy" consists + * a single date pattern "MMM d", another single date pattern "MMM d, yyyy", + * and a separator "-". + * The pattern is divided into 2 parts. For above example, + * the first part is "MMM d - ", and the second part is "MMM d, yyyy". + * Also, the first date appears in an interval pattern could be + * the earlier date or the later date. + * And such information is saved in the interval pattern as well. + * FIXME: do I need to define an inner class + */ + typedef struct PatternInfo { + UnicodeString firstPart; + UnicodeString secondPart; + /** + * Whether the first date in interval pattern is later date or not. + * Fallback format set the default ordering. + * And for a particular interval pattern, the order can be + * overriden by prefixing the interval pattern with "latestFirst:" or + * "earliestFirst:" + * For example, given 2 date, Jan 10, 2007 to Feb 10, 2007. + * if the fallback format is "{0} - {1}", + * and the pattern is "d MMM - d MMM yyyy", the interval format is + * "10 Jan - 10 Feb, 2007". + * If the pattern is "latestFirst:d MMM - d MMM yyyy", + * the interval format is "10 Feb - 10 Jan, 2007" + */ + UBool laterDateFirst; + } PatternInfo; + + + /** + * default constructor + * @draft ICU 4.0 + */ + DateIntervalFormat(); + + /** + * Construct a DateIntervalFormat from DateFormat and a DateIntervalInfo. + * + * This is the convenient override of + * DateIntervalFormat(DateFormat, DateIntervalInfo, UnicodeString) + * with the UnicodeString value as null. + * + * @param dtfmt the SimpleDateFormat object to be adopted. + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + DateIntervalFormat(DateFormat* dtfmt, DateIntervalInfo* dtItvInfo, + UErrorCode& status); + + + /** + * Construct a DateIntervalFormat from DateFormat, + * a DateIntervalInfo, and skeleton. + * DateFormat provides the timezone, calendar, + * full pattern, and date format symbols information. + * It should be a SimpleDateFormat object which + * has a pattern in it. + * the DateIntervalInfo provides the interval patterns. + * + * Note: the DateIntervalFormat takes ownership of both + * DateFormat and DateIntervalInfo objects. + * Caller should not delete them. + * + * @param dtfmt the SimpleDateFormat object to be adopted. + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param skeleton the skeleton of the date formatter + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + DateIntervalFormat(DateFormat* dtfmt, DateIntervalInfo* dtItvInfo, + const UnicodeString* skeleton, UErrorCode& status); + + + + /** + * Construct a DateIntervalFormat from DateFormat + * and a DateIntervalInfo. + * + * It is a wrapper of the constructor. + * + * @param dtfmt the DateFormat object to be adopted. + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param status Output param set to success/failure code. + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 create(DateFormat* dtfmt, + DateIntervalInfo* dtitvinf, + UErrorCode& status); + + + + /** + * Construct a DateIntervalFormat from DateFormat + * and a DateIntervalInfo. + * + * It is a wrapper of the constructor. + * + * @param dtfmt the DateFormat object to be adopted. + * @param dtitvinf the DateIntervalInfo object to be adopted. + * @param skeleton the skeleton of this formatter. + * @param status Output param set to success/failure code. + * @return a date time interval formatter whick the caller owns. + * @draft ICU 4.0 + */ + static DateIntervalFormat* U_EXPORT2 create(DateFormat* dtfmt, + DateIntervalInfo* dtitvinf, + const UnicodeString* skeleton, + UErrorCode& status); + + + /** + * Below are for generating interval patterns locale to the formatter + */ + + + /** + * Format 2 Calendars using fall-back interval pattern + * + * The full pattern used in this fall-back format is the + * full pattern of the date formatter. + * + * @param fromCalendar calendar set to the from date in date interval + * to be formatted into date interval stirng + * @param toCalendar calendar set to the to date in date interval + * to be formatted into date interval stirng + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param pos On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @param status output param set to success/failure code on exit + * @return Reference to 'appendTo' parameter. + * @draft ICU 4.0 + */ + UnicodeString& fallbackFormat(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const; + + + + /** + * Initialize interval patterns locale to this formatter + * + * This code is a bit complicated since + * 1. the interval patterns saved in resource bundle files are interval + * patterns based on date or time only. + * It does not have interval patterns based on both date and time. + * Interval patterns on both date and time are algorithm generated. + * + * For example, it has interval patterns on skeleton "dMy" and "hm", + * but it does not have interval patterns on skeleton "dMyhm". + * + * The rule to genearte interval patterns for both date and time skeleton are + * 1) when the year, month, or day differs, concatenate the two original + * expressions with a separator between, + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 11, 2007 10:10am" is + * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" + * + * 2) otherwise, present the date followed by the range expression + * for the time. + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 10, 2007 11:10am" is + * "Jan 10, 2007 10:10 am - 11:10am" + * + * 2. even a pattern does not request a certion calendar field, + * the interval pattern needs to include such field if such fields are + * different between 2 dates. + * For example, a pattern/skeleton is "hm", but the interval pattern + * includes year, month, and date when year, month, and date differs. + * + * + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void initializePattern(UErrorCode& status); + + + + /** + * Set fall back interval pattern given a calendar field, + * a skeleton, and a date time pattern generator. + * @param field the largest different calendar field + * @param skeleton a skeleton + * @param dtpng date time pattern generator + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void setFallbackPattern(UCalendarDateFields field, + const UnicodeString& skeleton, + DateTimePatternGenerator* dtpng, + UErrorCode& status); + + + + /** + * get separated date and time skeleton from a combined skeleton. + * + * The difference between date skeleton and normalizedDateSkeleton are: + * 1. both 'y' and 'd' are appeared only once in normalizeDateSkeleton + * 2. 'E' and 'EE' are normalized into 'EEE' + * 3. 'MM' is normalized into 'M' + * + ** the difference between time skeleton and normalizedTimeSkeleton are: + * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton, + * 2. 'a' is omitted in normalized time skeleton. + * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized time + * skeleton + * + * + * @param skeleton given combined skeleton. + * @param date Output parameter for date only skeleton. + * @param normalizedDate Output parameter for normalized date only + * + * @param time Output parameter for time only skeleton. + * @param normalizedTime Output parameter for normalized time only + * skeleton. + * + * @draft ICU 4.0 + */ + static void U_EXPORT2 getDateTimeSkeleton(const UnicodeString& skeleton, + UnicodeString& date, + UnicodeString& normalizedDate, + UnicodeString& time, + UnicodeString& normalizedTime); + + + + /** + * Generate date or time interval pattern from resource, + * and set them into the interval pattern locale to this formatter. + * + * It needs to handle the following: + * 1. need to adjust field width. + * For example, the interval patterns saved in DateIntervalInfo + * includes "dMMMy", but not "dMMMMy". + * Need to get interval patterns for dMMMMy from dMMMy. + * Another example, the interval patterns saved in DateIntervalInfo + * includes "hmv", but not "hmz". + * Need to get interval patterns for "hmz' from 'hmv' + * + * 2. there might be no pattern for 'y' differ for skeleton "Md", + * in order to get interval patterns for 'y' differ, + * need to look for it from skeleton 'yMd' + * + * @param dateSkeleton normalized date skeleton + * @param timeSkeleton normalized time skeleton + * @return whether the resource is found for the skeleton. + * TRUE if interval pattern found for the skeleton, + * FALSE otherwise. + * @draft ICU 4.0 + */ + UBool setSeparateDateTimePtn(const UnicodeString& dateSkeleton, + const UnicodeString& timeSkeleton); + + + + + /** + * Generate interval pattern from existing resource + * + * It not only save the interval patterns, + * but also return the extended skeleton and its best match skeleton. + * + * @param field largest different calendar field + * @param skeleton skeleton + * @param bestSkeleton the best match skeleton which has interval pattern + * defined in resource + * @param differenceInfo the difference between skeleton and best skeleton + * 0 means the best matched skeleton is the same as input skeleton + * 1 means the fields are the same, but field width are different + * 2 means the only difference between fields are v/z, + * -1 means there are other fields difference + * + * @param extendedSkeleton extended skeleton + * @param extendedBestSkeleton extended best match skeleton + * @return whether the interval pattern is found + * through extending skeleton or not. + * TRUE if interval pattern is found by + * extending skeleton, FALSE otherwise. + * @draft ICU 4.0 + */ + UBool setIntervalPattern(UCalendarDateFields field, + const UnicodeString* skeleton, + const UnicodeString* bestSkeleton, + int8_t differenceInfo, + UnicodeString* extendedSkeleton = NULL, + UnicodeString* extendedBestSkeleton = NULL); + + /** + * Adjust field width in best match interval pattern to match + * the field width in input skeleton. + * + * TODO (xji) make a general solution + * The adjusting rule can be: + * 1. always adjust + * 2. never adjust + * 3. default adjust, which means adjust according to the following rules + * 3.1 always adjust string, such as MMM and MMMM + * 3.2 never adjust between string and numeric, such as MM and MMM + * 3.3 always adjust year + * 3.4 do not adjust 'd', 'h', or 'm' if h presents + * 3.5 do not adjust 'M' if it is numeric(?) + * + * Since date interval format is well-formed format, + * date and time skeletons are normalized previously, + * till this stage, the adjust here is only "adjust strings, such as MMM + * and MMMM, EEE and EEEE. + * + * @param inputSkeleton the input skeleton + * @param bestMatchSkeleton the best match skeleton + * @param bestMatchIntervalpattern the best match interval pattern + * @param differenceInfo the difference between 2 skeletons + * 1 means only field width differs + * 2 means v/z exchange + * @param adjustedIntervalPattern adjusted interval pattern + * @draft ICU 4.0 + */ + static void U_EXPORT2 adjustFieldWidth( + const UnicodeString& inputSkeleton, + const UnicodeString& bestMatchSkeleton, + const UnicodeString& bestMatchIntervalPattern, + int8_t differenceInfo, + UnicodeString& adjustedIntervalPattern); + + /** + * Concat a single date pattern with a time interval pattern, + * set it into the intervalPatterns, while field is time field. + * This is used to handle time interval patterns on skeleton with + * both time and date. Present the date followed by + * the range expression for the time. + * @param format date and time format + * @param formatLen format string length + * @param datePattern date pattern + * @param field time calendar field: AM_PM, HOUR, MINUTE + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void concatSingleDate2TimeInterval(const UChar* format, + int32_t formatLen, + const UnicodeString& datePattern, + UCalendarDateFields field, + UErrorCode& status); + + /** + * check whether a calendar field present in a skeleton. + * @param field calendar field need to check + * @param skeleton given skeleton on which to check the calendar field + * @return true if field present in a skeleton. + * @draft ICU 4.0 + */ + static UBool U_EXPORT2 fieldExistsInSkeleton(UCalendarDateFields field, + const UnicodeString& skeleton); + + + /** + * Split interval patterns into 2 part. + * @param intervalPattern interval pattern + * @return the index in interval pattern which split the pattern into 2 part + * @draft ICU 4.0 + */ + static int32_t U_EXPORT2 splitPatternInto2Part(const UnicodeString& intervalPattern); + + + /** + * Break interval patterns as 2 part and save them into pattern info. + * @param field calendar field + * @param intervalPattern interval pattern + * @internal ICU 4.0 + */ + void setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern); + + + /** + * Break interval patterns as 2 part and save them into pattern info. + * @param field calendar field + * @param intervalPattern interval pattern + * @param laterDateFirst whether later date appear first in interval pattern + * @internal ICU 4.0 + */ + void setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern, + UBool laterDateFirst); + + + /** + * Set pattern information. + * + * @param field calendar field + * @param firstPart the first part in interval pattern + * @param secondPart the second part in interval pattern + * @param laterDateFirst whether the first date in intervalPattern + * is earlier date or later date + * @internal ICU 4.0 + */ + void setPatternInfo(UCalendarDateFields field, + const UnicodeString* firstPart, + const UnicodeString* secondpart, + UBool laterDateFirst); + + + // from calendar field to pattern letter + static const UChar fgCalendarFieldToPatternLetter[]; + + + /** + * The interval patterns for this locale. + */ + DateIntervalInfo* fInfo; + + /** + * The DateFormat object used to format single pattern + */ + SimpleDateFormat* fDateFormat; + + /** + * The 2 calendars with the from and to date. + * could re-use the calendar in fDateFormat, + * but keeping 2 calendars make it clear and clean. + */ + Calendar* fFromCalendar; + Calendar* fToCalendar; + + /** + * Following are interval information relavent (locale) to this formatter. + */ + UnicodeString fSkeleton; + PatternInfo fIntervalPatterns[DateIntervalInfo::kIPI_MAX_INDEX]; +}; + + + + + + +inline UBool +DateIntervalFormat::operator!=(const Format& other) const { + return !operator==(other); +} + +inline const DateIntervalInfo* +DateIntervalFormat::getDateIntervalInfo() const { + return fInfo; +} + + +inline void +DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, + UErrorCode& status) { + delete fInfo; + fInfo = new DateIntervalInfo(newItvPattern); + if ( fDateFormat ) { + initializePattern(status); + } +} + + +inline void +DateIntervalFormat::adoptDateIntervalInfo(DateIntervalInfo* newItvPattern, + UErrorCode& status) { + delete fInfo; + fInfo = newItvPattern; + if ( fDateFormat ) { + initializePattern(status); + } +} + + +inline const DateFormat* +DateIntervalFormat::getDateFormat() const { + return fDateFormat; +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif // _DTITVFMT_H__ +//eof diff --git a/icu4c/source/i18n/unicode/dtitvinf.h b/icu4c/source/i18n/unicode/dtitvinf.h new file mode 100644 index 00000000000..648145bc0e3 --- /dev/null +++ b/icu4c/source/i18n/unicode/dtitvinf.h @@ -0,0 +1,541 @@ +/* + ******************************************************************************* + * Copyright (C) 2008, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + * + * File DTITVINF.H + * + ******************************************************************************* + */ + +#ifndef __DTITVINF_H__ +#define __DTITVINF_H__ + +/** + * \file + * \brief C++ API: Date/Time interval patterns for formatting date/time interval + */ + +#if !UCONFIG_NO_FORMATTING + +#include "hash.h" +#include "gregoimp.h" +#include "uresimp.h" +#include "unicode/utypes.h" +#include "unicode/udat.h" +#include "unicode/locid.h" +#include "unicode/ucal.h" +#include "unicode/dtptngen.h" +//#include "dtitv_impl.h" + + + +U_NAMESPACE_BEGIN + + + +/** + * DateIntervalInfo is a public class for encapsulating localizable + * date time interval patterns. It is used by DateIntervalFormat. + * + *

    + * Logically, the interval patterns are mappings + * from (skeleton, the_largest_different_calendar_field) + * to (date_interval_pattern). + * + *

    + * A skeleton + *

      + *
    1. + * only keeps the field pattern letter and ignores all other parts + * in a pattern, such as space, punctuations, and string literals. + *
    2. + * hides the order of fields. + *
    3. + * might hide a field's pattern letter length. + * + * For those non-digit calendar fields, the pattern letter length is + * important, such as MMM, MMMM, and MMMMM; EEE and EEEE, + * and the field's pattern letter length is honored. + * + * For the digit calendar fields, such as M or MM, d or dd, yy or yyyy, + * the field pattern length is ignored and the best match, which is defined + * in date time patterns, will be returned without honor the field pattern + * letter length in skeleton. + *
    + * + *

    + * There is a set of pre-defined static skeleton strings. + * The skeletons defined consist of the desired calendar field set + * (for example, DAY, MONTH, YEAR) and the format length (long, medium, short) + * used in date time patterns. + * + * For example, skeleton YEAR_MONTH_MEDIUM_FORMAT consists month and year, + * and it's corresponding full pattern is medium format date pattern. + * So, the skeleton is "yMMM", for English, the full pattern is "MMM yyyy", + * which is the format by removing DATE from medium date format. + * + * For example, skeleton YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT consists day, month, + * year, and day-of-week, and it's corresponding full pattern is the medium + * format date pattern. So, the skeleton is "yMMMEEEd", for English, + * the full pattern is "EEE, MMM d, yyyy", which is the medium date format + * plus day-of-week. + * + *

    + * The calendar fields we support for interval formatting are: + * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute. + * Those calendar fields can be defined in the following order: + * year > month > date > am-pm > hour > minute + * + * The largest different calendar fields between 2 calendars is the + * first different calendar field in above order. + * + * For example: the largest different calendar fields between "Jan 10, 2007" + * and "Feb 20, 2008" is year. + * + *

    + * There are pre-defined interval patterns for those pre-defined skeletons + * in locales' resource files. + * For example, for a skeleton YEAR_MONTH_DAY_MEDIUM_FORMAT, which is "yMMMd", + * in en_US, if the largest different calendar field between date1 and date2 + * is "year", the date interval pattern is "MMM d, yyyy - MMM d, yyyy", + * such as "Jan 10, 2007 - Jan 10, 2008". + * If the largest different calendar field between date1 and date2 is "month", + * the date interval pattern is "MMM d - MMM d, yyyy", + * such as "Jan 10 - Feb 10, 2007". + * If the largest different calendar field between date1 and date2 is "day", + * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007". + * + * For date skeleton, the interval patterns when year, or month, or date is + * different are defined in resource files. + * For time skeleton, the interval patterns when am/pm, or hour, or minute is + * different are defined in resource files. + * + * + *

    + * There are 2 dates in interval pattern. For most locales, the first date + * in an interval pattern is the earlier date. There might be a locale in which + * the first date in an interval pattern is the later date. + * We use fallback format for the default order for the locale. + * For example, if the fallback format is "{0} - {1}", it means + * the first date in the interval pattern for this locale is earlier date. + * If the fallback format is "{1} - {0}", it means the first date is the + * later date. + * For a paticular interval pattern, the default order can be overriden + * by prefixing "latestFirst:" or "earliestFirst:" to the interval pattern. + * For example, if the fallback format is "{0}-{1}", + * but for skeleton "yMMMd", the interval pattern when day is different is + * "latestFirst:d-d MMM yy", it means by default, the first date in interval + * pattern is the earlier date. But for skeleton "yMMMd", when day is different, + * the first date in "d-d MMM yy" is the later date. + * + *

    + * The recommended way to create a DateIntervalFormat object is to pass in + * the locale. + * By using a Locale parameter, the DateIntervalFormat object is + * initialized with the pre-defined interval patterns for a given or + * default locale. + *

    + * Users can also create DateIntervalFormat object + * by supplying their own interval patterns. + * It provides flexibility for powerful usage. + * + *

    + * After a DateIntervalInfo object is created, clients may modify + * the interval patterns using setIntervalPattern function as so desired. + * Currently, users can only set interval patterns when the following + * calendar fields are different: ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, + * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, and MINUTE. + * Interval patterns when other calendar fields are different is not supported. + *

    + * DateIntervalInfo objects are clonable. + * When clients obtain a DateIntervalInfo object, + * they can feel free to modify it as necessary. + *

    + * DateIntervalInfo are not expected to be subclassed. + * Data for a calendar is loaded out of resource bundles. + * To ICU 4.0, date interval patterns are only supported in Gregorian calendar. + * @draft ICU 4.0 +**/ + +class U_I18N_API DateIntervalInfo : public UObject { +public: + /** + * Default constructor. + * It does not initialize any interval patterns. + * It should be followed by setFallbackIntervalPattern() and + * setIntervalPattern(), + * and is recommended to be used only for powerful users who + * wants to create their own interval patterns and use them to create + * date interval formatter. + * @param status output param set to success/failure code on exit + * @internal ICU 4.0 + */ + DateIntervalInfo(UErrorCode& status); + + + /** + * Construct DateIntervalInfo for the given locale, + * @param locale the interval patterns are loaded from the Gregorian + * calendar data in this locale. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + DateIntervalInfo(const Locale& locale, UErrorCode& status); + + + /** + * Copy constructor. + * @draft ICU 4.0 + */ + DateIntervalInfo(const DateIntervalInfo&); + + /** + * Assignment operator + * @draft ICU 4.0 + */ + DateIntervalInfo& operator=(const DateIntervalInfo&); + + /** + * Clone this object polymorphically. + * The caller owns the result and should delete it when done. + * @return a copy of the object + * @draft ICU4.0 + */ + DateIntervalInfo* clone(void) const; + + /** + * Destructor. + * It is virtual to be safe, but it is not designed to be subclassed. + * @draft ICU 4.0 + */ + virtual ~DateIntervalInfo(); + + + /** + * Return true if another object is semantically equal to this one. + * + * @param other the DateIntervalInfo object to be compared with. + * @return true if other is semantically equal to this. + * @stable ICU 4.0 + */ + UBool operator==(const DateIntervalInfo& other) const; + + /** + * Return true if another object is semantically unequal to this one. + * + * @param other the DateIntervalInfo object to be compared with. + * @return true if other is semantically unequal to this. + * @stable ICU 4.0 + */ + UBool operator!=(const DateIntervalInfo& other) const; + + + + /** + * Provides a way for client to build interval patterns. + * User could construct DateIntervalInfo by providing + * a list of patterns. + *

    + * For example: + *

    +     * UErrorCode status = U_ZERO_ERROR;
    +     * DateIntervalInfo dIntervalInfo = new DateIntervalInfo();
    +     * dIntervalInfo->setIntervalPattern("yMd", UCAL_YEAR, "'from' yyyy-M-d 'to' yyyy-M-d", status); 
    +     * dIntervalInfo->setIntervalPattern("yMMMd", UCAL_MONTH, "'from' yyyy MMM d 'to' MMM d", status);
    +     * dIntervalInfo->setIntervalPattern("yMMMd", UCAL_DAY, "yyyy MMM d-d", status, status);
    +     * dIntervalInfo->setFallbackIntervalPattern("{0} ~ {1}");
    +     * 
    + * + * Restriction: + * Currently, users can only set interval patterns when the following + * calendar fields are different: ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, + * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, and MINUTE. + * Interval patterns when other calendar fields are different are + * not supported. + * + * @param skeleton the skeleton on which interval pattern based + * @param lrgDiffCalUnit the largest different calendar unit. + * @param intervalPattern the interval pattern on the largest different + * calendar unit. + * For example, if lrgDiffCalUnit is + * "year", the interval pattern for en_US when year + * is different could be "'from' yyyy 'to' yyyy". + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void setIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status); + + /** + * Get the interval pattern given the largest different calendar field. + * @param skeleton the skeleton + * @param field the largest different calendar field + * @param status output param set to success/failure code on exit + * @return interval pattern + * @draft ICU 4.0 + */ + const UnicodeString* getIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields field, + UErrorCode& status) const; + + /** + * Get the fallback interval pattern. + * @return fallback interval pattern + * @draft ICU 4.0 + */ + const UnicodeString& getFallbackIntervalPattern() const; + + + /** + * Set the fallback interval pattern. + * Fall-back interval pattern is get from locale resource. + * If a user want to set their own fall-back interval pattern, + * they can do so by calling the following method. + * For users who construct DateIntervalInfo() by default constructor, + * all interval patterns ( including fall-back ) are not set, + * those users need to call setIntervalPattern() to set their own + * interval patterns, and call setFallbackIntervalPattern() to set + * their own fall-back interval patterns. If a certain interval pattern + * ( for example, the interval pattern when 'year' is different ) is not + * found, fall-back pattern will be used. + * For those users who set all their patterns ( instead of calling + * non-defaul constructor to let constructor get those patterns from + * locale ), if they do not set the fall-back interval pattern, + * it will be fall-back to '{date0} - {date1}'. + * + * @param fallbackPattern fall-back interval pattern. + * @draft ICU 4.0 + */ + void setFallbackIntervalPattern(const UnicodeString& fallbackPattern); + + + /* Get default order + * return default date ordering in interval pattern + * @draft ICU 4.0 + */ + UBool getDefaultOrder() const; + + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 4.0 + */ + virtual UClassID getDynamicClassID() const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 4.0 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + +private: + /** + * DateIntervalFormat will need access to + * getBestSkeleton(), parseSkeleton(), enum IntervalPatternIndex, + * and calendarFieldToPatternIndex(). + * + * Instead of making above public, + * make DateIntervalFormat a friend of DateIntervalInfo. + */ + friend class DateIntervalFormat; + + /** + * Following is for saving the interval patterns. + * We only support interval patterns on + * ERA, YEAR, MONTH, DAY, AM_PM, HOUR, and MINUTE + */ + enum IntervalPatternIndex + { + kIPI_ERA, + kIPI_YEAR, + kIPI_MONTH, + kIPI_DATE, + kIPI_AM_PM, + kIPI_HOUR, + kIPI_MINUTE, + kIPI_MAX_INDEX + }; + + /** + * Initialize the DateIntervalInfo from locale + * @param locale the given locale. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void initializeData(const Locale& locale, UErrorCode& status); + + + /* Set Interval pattern. + * + * It sets interval pattern into the hash map. + * + * @param skeleton skeleton on which the interval pattern based + * @param lrgDiffCalUnit the largest different calendar unit. + * @param intervalPattern the interval pattern on the largest different + * calendar unit. + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void setIntervalPatternInternally(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status); + + + /**given an input skeleton, get the best match skeleton + * which has pre-defined interval pattern in resource file. + * Also return the difference between the input skeleton + * and the best match skeleton. + * + * TODO (xji): set field weight or + * isolate the funtionality in DateTimePatternGenerator + * @param skeleton input skeleton + * @param bestMatchDistanceInfo the difference between input skeleton + * and best match skeleton. + * 0, if there is exact match for input skeleton + * 1, if there is only field width difference between + * the best match and the input skeleton + * 2, the only field difference is 'v' and 'z' + * -1, if there is calendar field difference between + * the best match and the input skeleton + * @return best match skeleton + * @draft ICU 4.0 + */ + const UnicodeString* getBestSkeleton(const UnicodeString& skeleton, + int8_t& bestMatchDistanceInfo) const; + + + /** + * Parse skeleton, save each field's width. + * It is used for looking for best match skeleton, + * and adjust pattern field width. + * @param skeleton skeleton to be parsed + * @param skeletonFieldWidth parsed skeleton field width + * @draft ICU 4.0 + */ + static void U_EXPORT2 parseSkeleton(const UnicodeString& skeleton, + int32_t* skeletonFieldWidth); + + + /** + * Check whether one field width is numeric while the other is string. + * + * TODO (xji): make it general + * + * @param fieldWidth one field width + * @param anotherFieldWidth another field width + * @param patternLetter pattern letter char + * @return true if one field width is numeric and the other is string, + * false otherwise. + * @draft ICU 4.0 + */ + static UBool U_EXPORT2 stringNumeric(int32_t fieldWidth, + int32_t anotherFieldWidth, + char patternLetter); + + + /** + * Convert calendar field to the interval pattern index in + * hash table. + * + * Since we only support the following calendar fields: + * ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK, + * AM_PM, HOUR, HOUR_OF_DAY, and MINUTE, + * We reserve only 4 interval patterns for a skeleton. + * + * @param field calendar field + * @param status output param set to success/failure code on exit + * @return interval pattern index in hash table + * @draft ICU 4.0 + */ + static IntervalPatternIndex U_EXPORT2 calendarFieldToIntervalIndex( + UCalendarDateFields field, + UErrorCode& status); + + + /** + * delete hash table (of type fIntervalPatterns). + * + * @param hTable hash table to be deleted + * @draft ICU 4.0 + */ + void deleteHash(Hashtable* hTable); + + + /** + * initialize hash table (of type fIntervalPatterns). + * + * @param status output param set to success/failure code on exit + * @return hash table initialized + * @draft ICU 4.0 + */ + Hashtable* initHash(UErrorCode& status); + + + + /** + * copy hash table (of type fIntervalPatterns). + * + * @param source the source to copy from + * @param target the target to copy to + * @param status output param set to success/failure code on exit + * @draft ICU 4.0 + */ + void copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status); + + + + /** + * set hash table value comparator + * + * @param val1 one value in comparison + * @param val2 the other value in comparison + * @return TRUE if 2 values are the same, FALSE otherwise + */ + static UBool U_EXPORT2 hashTableValueComparator(UHashTok val1, UHashTok val2); + + + // data members + // fallback interval pattern + UnicodeString fFallbackIntervalPattern; + // default order + UBool fFirstDateInPtnIsLaterDate; + + // HashMap + // HashMap( skeleton, pattern[largest_different_field] ) + Hashtable* fIntervalPatterns; + +};// end class DateIntervalInfo + + +inline UBool +DateIntervalInfo::operator!=(const DateIntervalInfo& other) const { + return !operator==(other); +} + + +inline UBool +DateIntervalInfo::getDefaultOrder() const { + return fFirstDateInPtnIsLaterDate; +} + + +inline const UnicodeString& +DateIntervalInfo::getFallbackIntervalPattern() const { + return fFallbackIntervalPattern; +} + + +U_NAMESPACE_END + +#endif + +#endif + diff --git a/icu4c/source/i18n/unicode/smpdtfmt.h b/icu4c/source/i18n/unicode/smpdtfmt.h index 7bd9e2f4051..a5d0eadaba6 100644 --- a/icu4c/source/i18n/unicode/smpdtfmt.h +++ b/icu4c/source/i18n/unicode/smpdtfmt.h @@ -606,6 +606,43 @@ public: */ virtual void adoptCalendar(Calendar* calendarToAdopt); + /** + * Check whether the 'field' is smaller than all the fields covered in + * pattern, return TRUE if it is. The sequence of calendar field, + * from large to small is: ERA, YEAR, MONTH, DATE, AM_PM, HOUR, MINUTE,... + * @param field the calendar field need to check against + * @return TRUE if the 'field' is smaller than all the fields + * covered in pattern. FALSE otherwise. + * @internal ICU 4.0 + */ + UBool isFieldUnitIgnored(UCalendarDateFields field) const; + + + /** + * Check whether the 'field' is smaller than all the fields covered in + * pattern, return TRUE if it is. The sequence of calendar field, + * from large to small is: ERA, YEAR, MONTH, DATE, AM_PM, HOUR, MINUTE,... + * @param pattern the pattern to check against + * @param field the calendar field need to check against + * @return TRUE if the 'field' is smaller than all the fields + * covered in pattern. FALSE otherwise. + * @internal ICU 4.0 + */ + static UBool isFieldUnitIgnored(const UnicodeString& pattern, + UCalendarDateFields field); + + + + /** + * Get the locale of this simple date formatter. + * It is used in DateIntervalFormat. + * + * @return locale in this simple date formatter + * @internal ICU 4.0 + */ + const Locale& getSmpFmtLocale(void) const; + + private: friend class DateFormat; @@ -826,6 +863,15 @@ private: */ static const UDateFormatField fgPatternIndexToDateFormatField[]; + /** + * Used to map Calendar field to field level. + * The larger the level, the smaller the field unit. + * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10, + * UCAL_MONTH level is 20. + */ + static const int32_t fgCalendarFieldToLevel[]; + static const int32_t fgPatternCharToLevel[]; + /** * The formatting pattern for this formatter. */ diff --git a/icu4c/source/i18n/unicode/udat.h b/icu4c/source/i18n/unicode/udat.h index 4bdd614d2f3..40c61bee41c 100644 --- a/icu4c/source/i18n/unicode/udat.h +++ b/icu4c/source/i18n/unicode/udat.h @@ -171,6 +171,94 @@ typedef enum UDateFormatStyle { } UDateFormatStyle; + +/** + * Below are a set of pre-defined skeletons. + * They have pre-defined interval patterns in resource files. + * Users are encouraged to use them in date interval format factory methods. + * + *

    + * We choose to use predefined skeleton string instead of skeleton enum because + * we need to keep consistency between DateFormat and DateIntervalFormat + * factory methods. + * It is not good to introduce another set of enum for skeleton while having + * UDateFormatStyle for full pattern. + * And it is not good to mix the set of enum for skeleton into UDateFormatStyle. + * So, a new set of pre-defined skeleton is introduced below. + *

    + * + * A skeleton + *

      + *
    • + * 1. only keeps the field pattern letter and ignores all other parts + * in a pattern, such as space, punctuations, and string literals. + *
    • + * 2. hides the order of fields. + *
    • + * 3. might hide a field's pattern letter length. + * + * For those non-digit calendar fields, the pattern letter length is + * important, such as MMM, MMMM, and MMMMM; EEE and EEEE, + * and the field's pattern letter length is honored. + * + * For the digit calendar fields, such as M or MM, d or dd, yy or yyyy, + * the field pattern length is ignored and the best match, which is defined + * in date time patterns, will be returned without honor the field pattern + * letter length in skeleton. + *
    + * + *

    + * For example, given skeleton YEAR_MONTH_DAY_SHORT_FORMAT, which is "yMd", + * for English, the full pattern is "M/d/yy", which is the short format + * of date pattern having DAY, MONTH, and YEAR. + * + *

    + * The skeletons defined below consists of the desired calendar field set + * (for example, DAY, MONTH, YEAR) and the format length (long, medium, short) + * used in date time patterns. + * + * For example, skeleton YEAR_MONTH_MEDIUM_FORMAT consists month and year, + * and it's corresponding full pattern is medium format date pattern. + * So, the skeleton is "yMMM", for English, the full pattern is "MMM yyyy", + * which is the format by removing DATE from medium date format. + * + * For example, skeleton YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT consists day, month, + * year, and day-of-week, and it's corresponding full pattern is the medium + * format date pattern. So, the skeleton is "yMMMEEEd", for English, + * the full pattern is "EEE, MMM d, yyyy", which is the medium date format + * plus day-of-week. + * + * @draft ICU 4.0 + */ + +#define YEAR_MONTH_DOW_DAY_LONG_FORMAT "yMMMMEEEEd" +#define YEAR_MONTH_DAY_LONG_FORMAT "yMMMMd" +#define MONTH_DAY_LONG_FORMAT "MMMMd" +#define YEAR_MONTH_LONG_FORMAT "yMMMM" +#define MONTH_DOW_DAY_LONG_FORMAT "MMMMEEEEd" +#define YEAR_MONTH_DOW_DAY_MEDIUM_FORMAT "yMMMEEEd" +#define YEAR_MONTH_DAY_MEDIUM_FORMAT "yMMMd" +#define MONTH_DAY_MEDIUM_FORMAT "MMMd" +#define YEAR_MONTH_MEDIUM_FORMAT "yMMM" +#define MONTH_DOW_DAY_MEDIUM_FORMAT "MMMEEEd" +#define YEAR_MONTH_DOW_DAY_SHORT_FORMAT "yMEEEd" +#define YEAR_MONTH_DAY_SHORT_FORMAT "yMd" +#define MONTH_DAY_SHORT_FORMAT "Md" +#define YEAR_MONTH_SHORT_FORMAT "yM" +#define MONTH_DOW_DAY_SHORT_FORMAT "MEEEd" +#define DAY_ONLY_SHORT_FORMAT "d" +#define DOW_DAY_SHORT_FORMAT "EEEd" +#define YEAR_ONLY_SHORT_FORMAT "y" +#define MONTH_ONLY_SHORT_FORMAT "M" +#define MONTH_ONLY_MEDIUM_FORMAT "MMM" +#define MONTH_ONLY_LONG_FORMAT "MMMM" +#define HOUR_MINUTE_FORMAT "hm" +#define HOUR_MINUTE_GENERAL_TZ_FORMAT "hmv" +#define HOUR_MINUTE_DAYLIGNT_TZ_FORMAT "hmz" +#define HOUR_ONLY_FORMAT "h" +#define HOUR_GENERAL_TZ_FORMAT "hv" +#define HOUR_DAYLIGNT_TZ_FORMAT "hz" + /** * FieldPosition and UFieldPosition selectors for format fields * defined by DateFormat and UDateFormat. diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index 097cba5ef4a..ba522d3dad5 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -56,7 +56,7 @@ jamotest.o srchtest.o reptest.o regextst.o \ itrbnf.o itrbnfrt.o itrbnfp.o ucaconf.o icusvtst.o \ uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o incaltst.o \ calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \ -windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o +windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o DEPS = $(OBJECTS:.o=.d) diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp new file mode 100644 index 00000000000..86f49b545e3 --- /dev/null +++ b/icu4c/source/test/intltest/dtifmtts.cpp @@ -0,0 +1,563 @@ + +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 1997-2008, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + + +//FIXME: how to define it in compiler time +#define DTIFMTTS_DEBUG 1 + + +#ifdef DTIFMTTS_DEBUG +#include +#endif + +#include "cstring.h" +#include "dtifmtts.h" +#include "unicode/dtintrv.h" +#include "unicode/dtitvinf.h" +#include "unicode/dtitvfmt.h" + + +#ifdef DTIFMTTS_DEBUG +//#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } +#define PRINTMESG(msg) { std::cout << msg; } +#endif + +#define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) + + +// This is an API test, not a unit test. It doesn't test very many cases, and doesn't +// try to test the full functionality. It just calls each function in the class and +// verifies that it works on a basic level. + +void DateIntervalFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) { + if (exec) logln("TestSuite DateIntervalFormat"); + switch (index) { + // TODO: uncomment. comment out temporarily + //TESTCASE(0, testAPI); + //TESTCASE(1, testFormat); + default: name = ""; break; + } +} + +/** + * Test various generic API methods of DateIntervalFormat for API coverage. + */ +void DateIntervalFormatTest::testAPI() { + + // ======= Test create instance with default local + UErrorCode status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with defaule locale"); + + DateIntervalFormat* dtitvfmt = DateIntervalFormat::createInstance(status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (default) - exitting"); + return; + } else { + delete dtitvfmt; + } + + // ======= Test create instance with given locale + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with given locale"); + + dtitvfmt = DateIntervalFormat::createInstance(Locale::getGerman(), status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (given locale) - exitting"); + return; + } else { + delete dtitvfmt; + } + + /* ====== Test create date interval instance with default locale and + * ====== date format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create date instance with default locale and date format style"); + + dtitvfmt = DateIntervalFormat::createDateIntervalInstance(DateFormat::kFull, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (default local + date style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + /* ====== Test create date interval instance with given locale and + * ====== date format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create date instance with given locale and date format style"); + + dtitvfmt = DateIntervalFormat::createDateIntervalInstance(DateFormat::kFull, Locale::getFrench(), status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (local + date style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create time interval instance with default locale and + * ====== time format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create time instance with default locale and time format style"); + + dtitvfmt = DateIntervalFormat::createTimeIntervalInstance(DateFormat::kLong, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (default local + time style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + /* ====== Test create time interval instance with given locale and + * ====== time format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create time instance with given locale and time format style"); + + dtitvfmt = DateIntervalFormat::createTimeIntervalInstance(DateFormat::kLong, Locale::getItalian(), status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (local + time style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + /* ====== Test create date time interval instance with default locale and + * ====== date time format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create date time instance with iven locale and date time format style"); + + dtitvfmt = DateIntervalFormat::createDateTimeIntervalInstance(DateFormat::kMedium, DateFormat::kShort, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (default locale + date time style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create date time interval instance with given locale and + * ====== date time format style + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create date time instance with given locale and date time format style"); + + dtitvfmt = DateIntervalFormat::createDateTimeIntervalInstance(DateFormat::kMedium, DateFormat::kShort, Locale::getItalian(), status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (local + date time style) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create interval instance with default locale and skeleton + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with default locale and skeleton"); + + dtitvfmt = DateIntervalFormat::createInstance(YEAR_MONTH_DAY_LONG_FORMAT, FALSE, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (skeleton + default locale) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create interval instance with given locale and skeleton + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with given locale and skeleton"); + + dtitvfmt = DateIntervalFormat::createInstance(YEAR_MONTH_DAY_LONG_FORMAT, FALSE, Locale::getJapanese(), status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (skeleton + locale) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create interval instance with dateIntervalInfo and skeleton + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with dateIntervalInfo and skeleton"); + + DateIntervalInfo* dtitvinf = new DateIntervalInfo(Locale::getSimplifiedChinese(), status); + + dtitvfmt = DateIntervalFormat::createInstance("EEEdMMMyhms", FALSE, dtitvinf, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (skeleton + DateIntervalInfo + default locale) - exitting"); + return; + } else { + delete dtitvfmt; + } + + + /* ====== Test create interval instance with dateIntervalInfo and skeleton + */ + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat create instance with dateIntervalInfo and skeleton"); + + dtitvinf = new DateIntervalInfo(Locale::getSimplifiedChinese(), status); + + dtitvfmt = DateIntervalFormat::createInstance("EEEdMMMyhms", FALSE, Locale::getSimplifiedChinese(), dtitvinf, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (skeleton + DateIntervalInfo + locale) - exitting"); + return; + } + // not deleted, test clone + + + // ====== Test clone() + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat clone"); + + DateIntervalFormat* another = (DateIntervalFormat*)dtitvfmt->clone(); + if ( (*another) != (*dtitvfmt) ) { + dataerrln("ERROR: clone failed"); + } + + + // ====== Test getDateIntervalInfo, setDateIntervalInfo, adoptDateIntervalInfo + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat getDateIntervalInfo"); + const DateIntervalInfo* inf = another->getDateIntervalInfo(); + dtitvfmt->setDateIntervalInfo(*inf, status); + const DateIntervalInfo* anotherInf = dtitvfmt->getDateIntervalInfo(); + if ( (*inf) != (*anotherInf) || U_FAILURE(status) ) { + dataerrln("ERROR: getDateIntervalInfo/setDateIntervalInfo failed"); + } + + status = U_ZERO_ERROR; + DateIntervalInfo* nonConstInf = inf->clone(); + dtitvfmt->adoptDateIntervalInfo(nonConstInf, status); + anotherInf = dtitvfmt->getDateIntervalInfo(); + if ( (*inf) != (*anotherInf) || U_FAILURE(status) ) { + dataerrln("ERROR: adoptDateIntervalInfo failed"); + } + + // ====== Test getDateFormat, setDateFormat, adoptDateFormat + + status = U_ZERO_ERROR; + logln("Testing DateIntervalFormat getDateFormat"); + const DateFormat* fmt = another->getDateFormat(); + dtitvfmt->setDateFormat(*fmt, status); + const DateFormat* anotherFmt = dtitvfmt->getDateFormat(); + if ( (*fmt) != (*anotherFmt) || U_FAILURE(status) ) { + dataerrln("ERROR: getDateFormat/setDateFormat failed"); + } + + status = U_ZERO_ERROR; + DateFormat* nonConstFmt = (DateFormat*)fmt->clone(); + dtitvfmt->adoptDateFormat(nonConstFmt, status); + anotherFmt = dtitvfmt->getDateFormat(); + if ( (*fmt) != (*anotherFmt) || U_FAILURE(status) ) { + dataerrln("ERROR: adoptDateFormat failed"); + } + + + // ======= Test getStaticClassID() + + logln("Testing getStaticClassID()"); + + + if(dtitvfmt->getDynamicClassID() != DateIntervalFormat::getStaticClassID()) { + errln("ERROR: getDynamicClassID() didn't return the expected value"); + } + + delete another; + + // ====== test constructor/copy constructor and assignment + /* they are protected, no test + logln("Testing DateIntervalFormat constructor and assigment operator"); + status = U_ZERO_ERROR; + + DateFormat* constFmt = (constFmt*)dtitvfmt->getDateFormat()->clone(); + inf = dtitvfmt->getDateIntervalInfo()->clone(); + + + DateIntervalFormat* dtifmt = new DateIntervalFormat(fmt, inf, status); + if(U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateIntervalFormat (default) - exitting"); + return; + } + + DateIntervalFormat* dtifmt2 = new(dtifmt); + if ( (*dtifmt) != (*dtifmt2) ) { + dataerrln("ERROR: Could not create DateIntervalFormat (default) - exitting"); + return; + } + + DateIntervalFormat dtifmt3 = (*dtifmt); + if ( (*dtifmt) != dtifmt3 ) { + dataerrln("ERROR: Could not create DateIntervalFormat (default) - exitting"); + return; + } + + delete dtifmt2; + delete dtifmt3; + delete dtifmt; + */ + + delete dtitvfmt; + + + //====== test format in testFormat() + +} + + +/** + * Test various generic API methods of DateIntervalFormat for API coverage. + */ +void DateIntervalFormatTest::testFormat() { + + const char* DATA[] = { + "yyyy MM dd HH:mm:ss", + "2007 10 10 10:10:10", "2008 10 10 10:10:10", + "2007 10 10 10:10:10", "2007 11 10 10:10:10", + "2007 11 10 10:10:10", "2007 11 20 10:10:10", + "2007 01 10 10:00:10", "2007 01 10 14:10:10", + "2007 01 10 10:00:10", "2007 01 10 10:20:10", + "2007 01 10 10:10:10", "2007 01 10 10:10:20", + }; + + const char* testLocale[][3] = { + {"en", "", ""}, + {"zh", "", ""}, + {"de", "", ""}, + {"ar", "", ""}, + {"en", "GB", ""}, + {"fr", "", ""}, + {"it", "", ""}, + {"nl", "", ""}, + {"zh", "TW", ""}, + {"ja", "", ""}, + {"pt", "BR", ""}, + {"ru", "", ""}, + {"pl", "", ""}, + {"tr", "", ""}, + {"es", "", ""}, + {"ko", "", ""}, + {"th", "", ""}, + {"sv", "", ""}, + {"fi", "", ""}, + {"da", "", ""}, + {"pt", "PT", ""}, + {"ro", "", ""}, + {"hu", "", ""}, + {"he", "", ""}, + {"in", "", ""}, + {"cs", "", ""}, + {"el", "", ""}, + {"no", "", ""}, + {"vi", "", ""}, + {"bg", "", ""}, + {"hr", "", ""}, + {"lt", "", ""}, + {"sk", "", ""}, + {"sl", "", ""}, + {"sr", "", ""}, + {"ca", "", ""}, + {"lv", "", ""}, + {"uk", "", ""}, + {"hi", "", ""}, + }; + + + uint32_t localeIndex; + for ( localeIndex = 0; localeIndex < ARRAY_SIZE(testLocale); ++localeIndex ) { + char locName[32]; + uprv_strcpy(locName, testLocale[localeIndex][0]); + uprv_strcat(locName, testLocale[localeIndex][1]); + expect(DATA, ARRAY_SIZE(DATA), Locale(testLocale[localeIndex][0], testLocale[localeIndex][1], testLocale[localeIndex][2]), locName); + } +} + + +void DateIntervalFormatTest::expect(const char** data, int32_t data_length, + const Locale& loc, const char* locName) { + + /* + UnicodeString formatResults[] = { + }; + */ + + UnicodeString skeleton[] = { + "EEEEdMMMMy", + "dMMMMy", + "dMMMM", + "MMMMy", + "EEEEdMMMM", + "EEEdMMMy", + "dMMMy", + "dMMM", + "MMMy", + "EEEdMMM", + "EEEdMy", + "dMy", + "dM", + "My", + "EEEdM", + "d", + "EEEd", + "y", + "M", + "MMM", + "MMMM", + "hm", + "hmv", + "hmz", + "h", + "hv", + "hz", + "EEddMMyyyy", // following could be normalized + "EddMMy", + "hhmm", + "hhmmzz", + "hms", // following could not be normalized + "dMMMMMy", + "EEEEEdM", + }; + + int32_t i = 0; + UErrorCode ec = U_ZERO_ERROR; + UnicodeString str, str2; + SimpleDateFormat ref(data[i++], loc, ec); + if (!assertSuccess("construct SimpleDateFormat", ec)) return; + +#ifdef DTIFMTTS_DEBUG + char result[1000]; + char mesg[1000]; + sprintf(mesg, "locale: %s\n", locName); + PRINTMESG(mesg); +#endif + + while (iformat(&dtitv, str.remove(), pos, ec); + if (!assertSuccess("format", ec)) return; +#ifdef DTIFMTTS_DEBUG + sprintf(mesg, "date interval, style = %d\n", style); + PRINTMESG(mesg) + str.extract(0, str.length(), result, "UTF-8"); + sprintf(mesg, "interval date: %s\n", result); + PRINTMESG(mesg) +#endif + delete dtitvfmt; + } + + for ( DateFormat::EStyle style = DateFormat::kFull; + style < DateFormat::kDateOffset; + style = (DateFormat::EStyle)(style+1) ) { + DateIntervalFormat* dtitvfmt = DateIntervalFormat::createTimeIntervalInstance(style, loc, ec); + + if (!assertSuccess("createTimeInstance", ec)) return; + FieldPosition pos=0; + dtitvfmt->format(&dtitv, str.remove(), pos, ec); + if (!assertSuccess("format", ec)) return; +#ifdef DTIFMTTS_DEBUG + sprintf(mesg, "time interval, style = %d\n", style); + PRINTMESG(mesg) + str.extract(0, str.length(), result, "UTF-8"); + sprintf(mesg, "interval date: %s\n", result); + PRINTMESG(mesg) +#endif + delete dtitvfmt; + } + + for ( DateFormat::EStyle style = DateFormat::kFull; + style < DateFormat::kDateOffset; + style = (DateFormat::EStyle)(style+1) ) { + DateIntervalFormat* dtitvfmt = DateIntervalFormat::createDateTimeIntervalInstance(style, style, loc, ec); + if (!assertSuccess("createDateTimeInstance", ec)) return; + FieldPosition pos=0; + dtitvfmt->format(&dtitv, str.remove(), pos, ec); + if (!assertSuccess("format", ec)) return; +#ifdef DTIFMTTS_DEBUG + sprintf(mesg, "date time interval, style = %d\n", style); + PRINTMESG(mesg) + str.extract(0, str.length(), result, "UTF-8"); + sprintf(mesg, "interval date: %s\n", result); + PRINTMESG(mesg) +#endif + delete dtitvfmt; + } + + for ( uint32_t skeletonIndex = 0; + skeletonIndex < ARRAY_SIZE(skeleton); + ++skeletonIndex ) { + const UnicodeString& oneSkeleton = skeleton[skeletonIndex]; + DateIntervalFormat* dtitvfmt = DateIntervalFormat::createInstance(oneSkeleton, FALSE, loc, ec); + if (!assertSuccess("createInstance(skeleton)", ec)) return; + FieldPosition pos=0; + dtitvfmt->format(&dtitv, str.remove(), pos, ec); + if (!assertSuccess("format", ec)) return; +#ifdef DTIFMTTS_DEBUG + oneSkeleton.extract(0, oneSkeleton.length(), result, "UTF-8"); + sprintf(mesg, "interval by skeleton: %s\n", result); + PRINTMESG(mesg) + str.extract(0, str.length(), result, "UTF-8"); + sprintf(mesg, "interval date: %s\n", result); + PRINTMESG(mesg) +#endif + delete dtitvfmt; + } + + // test user created DateIntervalInfo + ec = U_ZERO_ERROR; + DateIntervalInfo* dtitvinf = new DateIntervalInfo(ec); + dtitvinf->setFallbackIntervalPattern("{0} --- {1}"); + dtitvinf->setIntervalPattern(YEAR_MONTH_DAY_MEDIUM_FORMAT, UCAL_MONTH, "yyyy MMM d - MMM y",ec); + if (!assertSuccess("DateIntervalInfo::setIntervalPattern", ec)) return; + dtitvinf->setIntervalPattern(YEAR_MONTH_DAY_MEDIUM_FORMAT, UCAL_HOUR_OF_DAY, "yyyy MMM d HH:mm - HH:mm", ec); + if (!assertSuccess("DateIntervalInfo::setIntervalPattern", ec)) return; + DateIntervalFormat* dtitvfmt = DateIntervalFormat::createInstance(YEAR_MONTH_DAY_MEDIUM_FORMAT, FALSE, loc, dtitvinf, ec); + if (!assertSuccess("createInstance(skeleton,dtitvinf)", ec)) return; + FieldPosition pos=0; + dtitvfmt->format(&dtitv, str.remove(), pos, ec); + if (!assertSuccess("format", ec)) return; +#ifdef DTIFMTTS_DEBUG + PRINTMESG("interval format using user defined DateIntervalInfo\n"); + str.extract(0, str.length(), result, "UTF-8"); + sprintf(mesg, "interval date: %s\n", result); + PRINTMESG(mesg) +#endif + delete dtitvfmt; + } +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/dtifmtts.h b/icu4c/source/test/intltest/dtifmtts.h new file mode 100644 index 00000000000..42439e7e8e7 --- /dev/null +++ b/icu4c/source/test/intltest/dtifmtts.h @@ -0,0 +1,41 @@ +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2008, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +#ifndef _INTLTESTDATEINTERVALFORMAT +#define _INTLTESTDATEINTERVALFORMAT + +#include "unicode/utypes.h" +#include "unicode/locid.h" + +#if !UCONFIG_NO_FORMATTING + +#include "intltest.h" + +/** + * Test basic functionality of various API functions + **/ +class DateIntervalFormatTest: public IntlTest { + void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL ); + +public: + /** + * Performs tests on many API functions, see detailed comments in source code + **/ + void testAPI(); + + /** + * test formatting + */ + void testFormat(); + +private: + void expect(const char** data, int32_t data_length, const Locale& loc, + const char* locName); +}; + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index 1ce15d3887d..26c53efc398 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2007, International Business Machines + * Copyright (c) 1997-2008, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************/ @@ -50,6 +50,7 @@ #include "tzfmttst.h" // TimeZoneFormatTest #include "plurults.h" // PluralRulesTest #include "plurfmts.h" // PluralFormatTest +#include "dtifmtts.h" // DateIntervalFormatTest #define TESTCLASS(id, TestClass) \ case id: \ @@ -117,6 +118,7 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam TESTCLASS(34,TimeZoneFormatTest); TESTCLASS(35,PluralRulesTest); TESTCLASS(36,PluralFormatTest); + TESTCLASS(37,DateIntervalFormatTest); default: name = ""; break; //needed to end loop }