]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/datefmt.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / datefmt.cpp
index 842b910ba35f258cc30127cd0992239ea34b54c4..6493b183d946e47ee39a9cce6cf4547eb161c4ee 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
  *******************************************************************************
- * Copyright (C) 1997-2008, International Business Machines Corporation and    *
+ * Copyright (C) 1997-2015, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  *
 #include "unicode/datefmt.h"
 #include "unicode/smpdtfmt.h"
 #include "unicode/dtptngen.h"
+#include "unicode/udisplaycontext.h"
 #include "reldtfmt.h"
+#include "sharedobject.h"
+#include "unifiedcache.h"
+#include "uarrsort.h"
 
 #include "cstring.h"
 #include "windtfmt.h"
 
 U_NAMESPACE_BEGIN
 
+class U_I18N_API DateFmtBestPattern : public SharedObject {
+public:
+    UnicodeString fPattern;
+
+    DateFmtBestPattern(const UnicodeString &pattern)
+            : fPattern(pattern) { }
+    ~DateFmtBestPattern();
+};
+
+DateFmtBestPattern::~DateFmtBestPattern() {
+}
+
+template<> U_I18N_API
+const DateFmtBestPattern *LocaleCacheKey<DateFmtBestPattern>::createObject(
+        const void * /*creationContext*/, UErrorCode &status) const {
+    status = U_UNSUPPORTED_ERROR;
+    return NULL;
+}
+
+class U_I18N_API DateFmtBestPatternKey : public LocaleCacheKey<DateFmtBestPattern> { 
+private:
+    UnicodeString fSkeleton;
+public:
+    DateFmtBestPatternKey(
+        const Locale &loc,
+        const UnicodeString &skeleton,
+        UErrorCode &status)
+            : LocaleCacheKey<DateFmtBestPattern>(loc),
+              fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton, status)) { }
+    DateFmtBestPatternKey(const DateFmtBestPatternKey &other) :
+            LocaleCacheKey<DateFmtBestPattern>(other),
+            fSkeleton(other.fSkeleton) { }
+    virtual ~DateFmtBestPatternKey();
+    virtual int32_t hashCode() const {
+        return (int32_t)(37u * (uint32_t)LocaleCacheKey<DateFmtBestPattern>::hashCode() + (uint32_t)fSkeleton.hashCode());
+    }
+    virtual UBool operator==(const CacheKeyBase &other) const {
+       // reflexive
+       if (this == &other) {   
+           return TRUE;
+       }
+       if (!LocaleCacheKey<DateFmtBestPattern>::operator==(other)) {
+           return FALSE;
+       }
+       // We know that this and other are of same class if we get this far.
+       const DateFmtBestPatternKey &realOther =
+               static_cast<const DateFmtBestPatternKey &>(other);
+       return (realOther.fSkeleton == fSkeleton);
+    }
+    virtual CacheKeyBase *clone() const {
+        return new DateFmtBestPatternKey(*this);
+    }
+    virtual const DateFmtBestPattern *createObject(
+            const void * /*unused*/, UErrorCode &status) const {
+        LocalPointer<DateTimePatternGenerator> dtpg(
+                    DateTimePatternGenerator::createInstance(fLoc, status));
+        if (U_FAILURE(status)) {
+            return NULL;
+        }
+  
+        LocalPointer<DateFmtBestPattern> pattern(
+                new DateFmtBestPattern(
+                        dtpg->getBestPattern(fSkeleton, status)),
+                status);
+        if (U_FAILURE(status)) {
+            return NULL;
+        }
+        DateFmtBestPattern *result = pattern.orphan();
+        result->addRef();
+        return result;
+    }
+};
+
+DateFmtBestPatternKey::~DateFmtBestPatternKey() { }
+
+
 DateFormat::DateFormat()
 :   fCalendar(0),
-    fNumberFormat(0)
+    fNumberFormat(0),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
 {
 }
 
@@ -51,7 +134,8 @@ DateFormat::DateFormat()
 DateFormat::DateFormat(const DateFormat& other)
 :   Format(other),
     fCalendar(0),
-    fNumberFormat(0)
+    fNumberFormat(0),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
 {
     *this = other;
 }
@@ -74,6 +158,8 @@ DateFormat& DateFormat::operator=(const DateFormat& other)
         } else {
           fNumberFormat = NULL;
         }
+        fBoolFlags = other.fBoolFlags;
+        fCapitalizationContext = other.fCapitalizationContext;
     }
     return *this;
 }
@@ -101,7 +187,8 @@ DateFormat::operator==(const Format& other) const
     return (this == fmt) ||
         (Format::operator==(other) &&
          fCalendar&&(fCalendar->isEquivalentTo(*fmt->fCalendar)) &&
-         (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat));
+         (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) &&
+         (fCapitalizationContext == fmt->fCapitalizationContext) );
 }
 
 //----------------------------------------------------------------------
@@ -141,14 +228,85 @@ DateFormat::format(const Formattable& obj,
 
 //----------------------------------------------------------------------
 
+UnicodeString&
+DateFormat::format(const Formattable& obj,
+                   UnicodeString& appendTo,
+                   FieldPositionIterator* posIter,
+                   UErrorCode& status) const
+{
+    if (U_FAILURE(status)) return appendTo;
+
+    // if the type of the Formattable is double or long, treat it as if it were a Date
+    UDate date = 0;
+    switch (obj.getType())
+    {
+    case Formattable::kDate:
+        date = obj.getDate();
+        break;
+    case Formattable::kDouble:
+        date = (UDate)obj.getDouble();
+        break;
+    case Formattable::kLong:
+        date = (UDate)obj.getLong();
+        break;
+    default:
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return appendTo;
+    }
+
+    // Is this right?
+    //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
+    //  status = U_ILLEGAL_ARGUMENT_ERROR;
+
+    return format(date, appendTo, posIter, status);
+}
+
+//----------------------------------------------------------------------
+
+// Default implementation for backwards compatibility, subclasses should implement.
+UnicodeString&
+DateFormat::format(Calendar& /* unused cal */,
+                   UnicodeString& appendTo,
+                   FieldPositionIterator* /* unused posIter */,
+                   UErrorCode& status) const {
+    if (U_SUCCESS(status)) {
+        status = U_UNSUPPORTED_ERROR;
+    }
+    return appendTo;
+}
+
+//----------------------------------------------------------------------
+
 UnicodeString&
 DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const {
     if (fCalendar != NULL) {
-        // Use our calendar instance
-        UErrorCode ec = U_ZERO_ERROR;
-        fCalendar->setTime(date, ec);
-        if (U_SUCCESS(ec)) {
-            return format(*fCalendar, appendTo, fieldPosition);
+        // Use a clone of our calendar instance
+        Calendar* calClone = fCalendar->clone();
+        if (calClone != NULL) {
+            UErrorCode ec = U_ZERO_ERROR;
+            calClone->setTime(date, ec);
+            if (U_SUCCESS(ec)) {
+                format(*calClone, appendTo, fieldPosition);
+            }
+            delete calClone;
+        }
+    }
+    return appendTo;
+}
+
+//----------------------------------------------------------------------
+
+UnicodeString&
+DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* posIter,
+                   UErrorCode& status) const {
+    if (fCalendar != NULL) {
+        Calendar* calClone = fCalendar->clone();
+        if (calClone != NULL) {
+            calClone->setTime(date, status);
+            if (U_SUCCESS(status)) {
+               format(*calClone, appendTo, posIter, status);
+            }
+            delete calClone;
         }
     }
     return appendTo;
@@ -161,7 +319,7 @@ DateFormat::format(UDate date, UnicodeString& appendTo) const
 {
     // Note that any error information is just lost.  That's okay
     // for this convenience method.
-    FieldPosition fpos(0);
+    FieldPosition fpos(FieldPosition::DONT_CARE);
     return format(date, appendTo, fpos);
 }
 
@@ -173,28 +331,25 @@ DateFormat::parse(const UnicodeString& text,
 {
     UDate d = 0; // Error return UDate is 0 (the epoch)
     if (fCalendar != NULL) {
-        int32_t start = pos.getIndex();
-
-        // Parse may update TimeZone used by the calendar.
-        TimeZone *tzsav = (TimeZone*)fCalendar->getTimeZone().clone();
-
-        fCalendar->clear();
-        parse(text, *fCalendar, pos);
-        if (pos.getIndex() != start) {
-            UErrorCode ec = U_ZERO_ERROR;
-            d = fCalendar->getTime(ec);
-            if (U_FAILURE(ec)) {
-                // We arrive here if fCalendar is non-lenient and there
-                // is an out-of-range field.  We don't know which field
-                // was illegal so we set the error index to the start.
-                pos.setIndex(start);
-                pos.setErrorIndex(start);
-                d = 0;
+        Calendar* calClone = fCalendar->clone();
+        if (calClone != NULL) {
+            int32_t start = pos.getIndex();
+            calClone->clear();
+            parse(text, *calClone, pos);
+            if (pos.getIndex() != start) {
+                UErrorCode ec = U_ZERO_ERROR;
+                d = calClone->getTime(ec);
+                if (U_FAILURE(ec)) {
+                    // We arrive here if fCalendar => calClone is non-lenient and
+                    // there is an out-of-range field.  We don't know which field
+                    // was illegal so we set the error index to the start.
+                    pos.setIndex(start);
+                    pos.setErrorIndex(start);
+                    d = 0;
+                }
             }
+            delete calClone;
         }
-
-        // Restore TimeZone
-        fCalendar->adoptTimeZone(tzsav);
     }
     return d;
 }
@@ -235,7 +390,7 @@ DateFormat* U_EXPORT2
 DateFormat::createTimeInstance(DateFormat::EStyle style,
                                const Locale& aLocale)
 {
-    return create(style, kNone, aLocale);
+    return createDateTimeInstance(kNone, style, aLocale);
 }
 
 //----------------------------------------------------------------------
@@ -244,13 +399,7 @@ DateFormat* U_EXPORT2
 DateFormat::createDateInstance(DateFormat::EStyle style,
                                const Locale& aLocale)
 {
-    // +4 to set the correct index for getting data out of
-    // LocaleElements.
-    if(style != kNone)
-    {
-        style = (EStyle) (style + kDateOffset);
-    }
-    return create(kNone, (EStyle) (style), aLocale);
+    return createDateTimeInstance(style, kNone, aLocale);
 }
 
 //----------------------------------------------------------------------
@@ -260,11 +409,11 @@ DateFormat::createDateTimeInstance(EStyle dateStyle,
                                    EStyle timeStyle,
                                    const Locale& aLocale)
 {
-    if(dateStyle != kNone)
-    {
-        dateStyle = (EStyle) (dateStyle + kDateOffset);
-    }
-    return create(timeStyle, dateStyle, aLocale);
+   if(dateStyle != kNone)
+   {
+       dateStyle = (EStyle) (dateStyle + kDateOffset);
+   }
+   return create(timeStyle, dateStyle, aLocale);
 }
 
 //----------------------------------------------------------------------
@@ -272,42 +421,80 @@ DateFormat::createDateTimeInstance(EStyle dateStyle,
 DateFormat* U_EXPORT2
 DateFormat::createInstance()
 {
-    return create(kShort, (EStyle) (kShort + kDateOffset), Locale::getDefault());
+    return createDateTimeInstance(kShort, kShort, Locale::getDefault());
 }
 
 //----------------------------------------------------------------------
+
+UnicodeString U_EXPORT2
+DateFormat::getBestPattern(
+        const Locale &locale,
+        const UnicodeString &skeleton,
+        UErrorCode &status) {
+    UnifiedCache *cache = UnifiedCache::getInstance(status);
+    if (U_FAILURE(status)) {
+        return UnicodeString();
+    }
+    DateFmtBestPatternKey key(locale, skeleton, status);
+    const DateFmtBestPattern *patternPtr = NULL;
+    cache->get(key, patternPtr, status);
+    if (U_FAILURE(status)) {
+        return UnicodeString();
+    }
+    UnicodeString result(patternPtr->fPattern);
+    patternPtr->removeRef();
+    return result;
+}
+
 DateFormat* U_EXPORT2
-DateFormat::createPatternInstance(const UnicodeString& skeleton,
-                                  const Locale& locale,
-                                  UErrorCode& status) 
-{
-    if ( U_FAILURE(status) ) {
+DateFormat::createInstanceForSkeleton(
+        Calendar *calendarToAdopt,
+        const UnicodeString& skeleton,
+        const Locale &locale,
+        UErrorCode &status) {
+    LocalPointer<Calendar> calendar(calendarToAdopt);
+    if (U_FAILURE(status)) {
         return NULL;
     }
-
-    DateTimePatternGenerator* dtptg = 
-               DateTimePatternGenerator::createInstance(locale, status);
-    if ( dtptg == NULL ) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        delete dtptg;
+    if (calendar.isNull()) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
         return NULL;
     }
-    if ( U_FAILURE(status) ) {
-        delete dtptg;
+    Locale localeWithCalendar = locale;
+    localeWithCalendar.setKeywordValue("calendar", calendar->getType(), status);
+    if (U_FAILURE(status)) {
         return NULL;
     }
-
-    const UnicodeString pattern = dtptg->getBestPattern(skeleton, status);
-    delete dtptg;
-    if ( U_FAILURE(status) ) {
+    DateFormat *result = createInstanceForSkeleton(skeleton, localeWithCalendar, status);
+    if (U_FAILURE(status)) {
         return NULL;
     }
-    SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status);
-    if ( U_FAILURE(status) ) {
-        delete dtfmt;
+    result->adoptCalendar(calendar.orphan());
+    return result;
+}
+
+DateFormat* U_EXPORT2
+DateFormat::createInstanceForSkeleton(
+        const UnicodeString& skeleton,
+        const Locale &locale,
+        UErrorCode &status) {
+    if (U_FAILURE(status)) {
         return NULL;
     }
-    return dtfmt;
+    LocalPointer<DateFormat> df(
+        new SimpleDateFormat(
+            getBestPattern(locale, skeleton, status),
+            locale, status),
+        status);
+    return U_SUCCESS(status) ? df.orphan() : NULL;
+}
+
+DateFormat* U_EXPORT2
+DateFormat::createInstanceForSkeleton(
+        const UnicodeString& skeleton,
+        UErrorCode &status) {
+    return createInstanceForSkeleton(
+            skeleton, Locale::getDefault(), status);
 }
 
 //----------------------------------------------------------------------
@@ -316,7 +503,7 @@ DateFormat* U_EXPORT2
 DateFormat::create(EStyle timeStyle, EStyle dateStyle, const Locale& locale)
 {
     UErrorCode status = U_ZERO_ERROR;
-#ifdef U_WINDOWS
+#if U_PLATFORM_USES_ONLY_WIN32_API
     char buffer[8];
     int32_t count = locale.getKeywordValue("compat", buffer, sizeof(buffer), status);
 
@@ -382,6 +569,8 @@ DateFormat::adoptCalendar(Calendar* newCalendar)
 void
 DateFormat::setCalendar(const Calendar& newCalendar)
 {
+    if (fCalendar && fCalendar->isEquivalentTo( newCalendar ))
+        return;
     Calendar* newCalClone = newCalendar.clone();
     if (newCalClone != NULL) {
         adoptCalendar(newCalClone);
@@ -404,6 +593,7 @@ DateFormat::adoptNumberFormat(NumberFormat* newNumberFormat)
     delete fNumberFormat;
     fNumberFormat = newNumberFormat;
     newNumberFormat->setParseIntegerOnly(TRUE);
+    newNumberFormat->setGroupingUsed(FALSE);
 }
 //----------------------------------------------------------------------
 
@@ -464,12 +654,38 @@ DateFormat::setLenient(UBool lenient)
     if (fCalendar != NULL) {
         fCalendar->setLenient(lenient);
     }
+    UErrorCode status = U_ZERO_ERROR;
+    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, lenient, status);
+    setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, lenient, status);
 }
 
 //----------------------------------------------------------------------
 
 UBool
 DateFormat::isLenient() const
+{
+    UBool lenient = TRUE;
+    if (fCalendar != NULL) {
+        lenient = fCalendar->isLenient();
+    }
+    UErrorCode status = U_ZERO_ERROR;
+    return lenient
+        && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)
+        && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status);
+}
+
+void
+DateFormat::setCalendarLenient(UBool lenient)
+{
+    if (fCalendar != NULL) {
+        fCalendar->setLenient(lenient);
+    }
+}
+
+//----------------------------------------------------------------------
+
+UBool
+DateFormat::isCalendarLenient() const
 {
     if (fCalendar != NULL) {
         return fCalendar->isLenient();
@@ -478,6 +694,61 @@ DateFormat::isLenient() const
     return FALSE;
 }
 
+
+//----------------------------------------------------------------------
+
+
+void DateFormat::setContext(UDisplayContext value, UErrorCode& status)
+{
+    if (U_FAILURE(status))
+        return;
+    if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) {
+        fCapitalizationContext = value;
+    } else {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+   }
+}
+
+
+//----------------------------------------------------------------------
+
+
+UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& status) const
+{
+    if (U_FAILURE(status))
+        return (UDisplayContext)0;
+    if (type != UDISPCTX_TYPE_CAPITALIZATION) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return (UDisplayContext)0;
+    }
+    return fCapitalizationContext;
+}
+
+
+//----------------------------------------------------------------------
+
+
+DateFormat& 
+DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr,
+                                                                       UBool newValue,
+                                                                       UErrorCode &status) {
+    if(!fBoolFlags.isValidValue(newValue)) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+    } else {
+        fBoolFlags.set(attr, newValue);
+    }
+
+    return *this;
+}
+
+//----------------------------------------------------------------------
+
+UBool 
+DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &/*status*/) const {
+
+    return static_cast<UBool>(fBoolFlags.get(attr));
+}
+
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */