]> granicus.if.org Git - icu/commitdiff
ICU-10619 Improve usability of DateFormat's leniency control & promote BooleanAttribu...
authorScott Russell <DTownSMR@gmail.com>
Tue, 14 Jan 2014 17:26:35 +0000 (17:26 +0000)
committerScott Russell <DTownSMR@gmail.com>
Tue, 14 Jan 2014 17:26:35 +0000 (17:26 +0000)
X-SVN-Rev: 34888

icu4c/source/i18n/datefmt.cpp
icu4c/source/i18n/smpdtfmt.cpp
icu4c/source/i18n/unicode/datefmt.h
icu4c/source/i18n/unicode/smpdtfmt.h
icu4c/source/i18n/unicode/udat.h
icu4c/source/test/intltest/dtfmrgts.cpp
icu4c/source/test/intltest/dtfmrgts.h

index 122f5ee0040d542b6a4253631180ec70ff96395a..dab4354b0e8961d10ab01994eea48e24c4227c4f 100644 (file)
@@ -502,6 +502,9 @@ DateFormat::setLenient(UBool lenient)
 {
     if (fCalendar != NULL) {
         fCalendar->setLenient(lenient);
+        UErrorCode status = U_ZERO_ERROR;
+               setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
+        setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
     }
 }
 
@@ -509,6 +512,29 @@ DateFormat::setLenient(UBool lenient)
 
 UBool
 DateFormat::isLenient() const
+{
+    if (fCalendar != NULL) {
+        UErrorCode status = U_ZERO_ERROR;
+        return fCalendar->isLenient()
+               && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)
+               && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status);
+    }
+    // fCalendar is rarely null
+    return FALSE;
+}
+
+void
+DateFormat::setCalendarLenient(UBool lenient)
+{
+    if (fCalendar != NULL) {
+        fCalendar->setLenient(lenient);
+    }
+}
+
+//----------------------------------------------------------------------
+
+UBool
+DateFormat::isCalendarLenient() const
 {
     if (fCalendar != NULL) {
         return fCalendar->isLenient();
@@ -517,6 +543,7 @@ DateFormat::isLenient() const
     return FALSE;
 }
 
+
 //----------------------------------------------------------------------
 
 
@@ -549,6 +576,7 @@ UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& sta
 
 //----------------------------------------------------------------------
 
+
 DateFormat& 
 DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr,
                                                                        UBool newValue,
index 2f0078d5ef4749d335a7351f75f412f5db6c271a..1e860462becca788492150eda1a48796ac17aecd 100644 (file)
@@ -247,7 +247,7 @@ SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
       fNumberFormatters(NULL),
       fOverrideList(NULL)
 {
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
     construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
     initializeDefaultCentury();
 }
@@ -265,7 +265,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 {
     fDateOverride.setToBogus();
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
     initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
     initialize(fLocale, status);
     initializeDefaultCentury();
@@ -285,7 +285,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 {
     fDateOverride.setTo(override);
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
     initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
     initialize(fLocale, status);
     initializeDefaultCentury();
@@ -308,7 +308,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 
     fDateOverride.setToBogus();
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
 
     initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
     initialize(fLocale, status);
@@ -330,7 +330,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 
     fDateOverride.setTo(override);
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
 
     initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
     initialize(fLocale, status);
@@ -355,7 +355,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 
     fDateOverride.setToBogus();
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
 
     initializeCalendar(NULL,fLocale,status);
     initialize(fLocale, status);
@@ -377,7 +377,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
 
     fDateOverride.setToBogus();
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
 
     initializeCalendar(NULL, fLocale, status);
     initialize(fLocale, status);
@@ -397,7 +397,7 @@ SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
     fNumberFormatters(NULL),
     fOverrideList(NULL)
 {
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
     construct(timeStyle, dateStyle, fLocale, status);
     if(U_SUCCESS(status)) {
       initializeDefaultCentury();
@@ -421,6 +421,7 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
     fOverrideList(NULL)
 {
     if (U_FAILURE(status)) return;
+    initializeBooleanAttributes();
     initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
     if (U_FAILURE(status))
     {
@@ -437,7 +438,6 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
 
     fDateOverride.setToBogus();
     fTimeOverride.setToBogus();
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
 
     initialize(fLocale, status);
     if(U_SUCCESS(status)) {
@@ -456,7 +456,7 @@ SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
     fOverrideList(NULL)
 {
     UErrorCode status = U_ZERO_ERROR;
-    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status).setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    initializeBooleanAttributes();
     *this = other;
 }
 
@@ -791,6 +791,18 @@ void SimpleDateFormat::initializeDefaultCentury()
   }
 }
 
+/*
+ * Initialize the boolean attributes. Separate so we can call it from all constructors.
+ */
+void SimpleDateFormat::initializeBooleanAttributes()
+{
+    UErrorCode status = U_ZERO_ERROR;
+
+    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
+    setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
+    setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+}
+
 /* Define one-century window into which to disambiguate dates using
  * two-digit years. Make public in JDK 1.2.
  */
index 4af35b7d73c92df36164cdd57db384cb986f7319..3de0adcd892cd22eed6556d0730e747e02a3747b 100644 (file)
@@ -581,26 +581,54 @@ public:
     static const Locale* U_EXPORT2 getAvailableLocales(int32_t& count);
 
     /**
-     * Returns true if the encapsulated Calendar object is set for lenient parsing.
+     * Returns whether both date/time parsing in the encapsulated Calendar object and DateFormat whitespace &
+     * numeric processing is lenient.
      * @stable ICU 2.0
      */
     virtual UBool isLenient(void) const;
 
     /**
-     * Specify whether or not date/time parsing is to be lenient. With lenient
-     * parsing, the parser may use heuristics to interpret inputs that do not
-     * precisely match this object's format. With strict parsing, inputs must
-     * match this object's format.
-     *
-     * Note: This method is specific to the encapsulated Calendar object.  DateFormat
-     * leniency aspects are controlled by setBooleanAttribute.
+     * Specifies whether date/time parsing is to be lenient.  With
+     * lenient parsing, the parser may use heuristics to interpret inputs that
+     * do not precisely match this object's format.  Without lenient parsing,
+     * inputs must match this object's format more closely.
+     * 
+     * Note: ICU 53 introduced finer grained control of leniency (and added 
+     * new control points) making the preferred method a combination of 
+     * setCalendarLenient() & setBooleanAttribute() calls. 
+     * This method supports prior functionality but may not support all 
+     * future leniency control & behavior of DateFormat. For control of pre 53 leniency,  
+     * Calendar and DateFormat whitespace & numeric tolerance, this method is safe to 
+     * use. However, mixing leniency control via this method and modification of the 
+     * newer attributes via setBooleanAttribute() may produce undesirable 
+     * results.
      *
      * @param lenient  True specifies date/time interpretation to be lenient.
      * @see Calendar::setLenient
-     * @stable ICU 2.0
+     * @stable ICU 2.0     
      */
     virtual void setLenient(UBool lenient);
 
+
+    /**
+     * Returns whether date/time parsing in the encapsulated Calendar object processing is lenient.
+     * @draft ICU 53
+     */
+    virtual UBool isCalendarLenient(void) const;
+
+
+    /**
+     * Specifies whether encapsulated Calendar date/time parsing is to be lenient.  With
+     * lenient parsing, the parser may use heuristics to interpret inputs that
+     * do not precisely match this object's format.  Without lenient parsing,
+     * inputs must match this object's format more closely.
+     * @param lenient when true, parsing is lenient
+     * @see com.ibm.icu.util.Calendar#setLenient
+     * @draft ICU 53
+     */
+    virtual void setCalendarLenient(UBool lenient);
+
+
     /**
      * Gets the calendar associated with this date/time formatter.
      * @return the calendar associated with this date/time formatter.
@@ -700,14 +728,14 @@ public:
     virtual UDisplayContext getContext(UDisplayContextType type, UErrorCode& status) const;
 
    /**
-     * Set an boolean attribute on this DateFormat.
+     * Sets an boolean attribute on this DateFormat.
      * May return U_UNSUPPORTED_ERROR if this instance does not support
      * the specified attribute.
      * @param attr the attribute to set
      * @param newvalue new value
      * @param status the error type
      * @return *this - for chaining (example: format.setAttribute(...).setAttribute(...) )
-     * @internal ICU technology preview
+     * @draft ICU 53
      */
 
     virtual DateFormat&  U_EXPORT2 setBooleanAttribute(UDateFormatBooleanAttribute attr,
@@ -715,13 +743,13 @@ public:
                                                                        UErrorCode &status);
 
     /**
-     * Get an boolean from this DateFormat
+     * Returns a boolean from this DateFormat
      * May return U_UNSUPPORTED_ERROR if this instance does not support
      * the specified attribute.
      * @param attr the attribute to set
      * @param status the error type
      * @return the attribute value. Undefined if there is an error.
-     * @internal ICU technology preview
+     * @draft ICU 53
      */
     virtual UBool U_EXPORT2 getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &status) const;
 
index bd6bdbd0745e5e8bb7cd13c1de91f7eca2c27f7e..4875615404d3f274fba45a0baf567d9616feb48c 100644 (file)
@@ -1151,6 +1151,8 @@ private:
 
     void initializeDefaultCentury(void);
 
+    void initializeBooleanAttributes(void);
+
     SimpleDateFormat(); // default constructor not implemented
 
     /**
index 4437743f6b31333c0f7e662a48cf2c8989da8b63..ccdbc73eb93a40068a9b2f01de32f2ddc01b7293 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2013, International Business Machines
+ * Copyright (C) 1996-2014, International Business Machines
  * Corporation and others. All Rights Reserved.
  *******************************************************************************
 */
@@ -837,18 +837,19 @@ udat_close(UDateFormat* format);
 
 /**
  * DateFormat boolean attributes
- * @internal ICU technology preview
+ * 
+ * @draft ICU 53
  */
 typedef enum UDateFormatBooleanAttribute {
     /**
      * indicates whether whitespace is allowed. Includes trailing dot tolerance.
-     * @internal ICU technology preview
+     * @draft ICU 53
      */
     UDAT_PARSE_ALLOW_WHITESPACE,
     /**
      * indicates tolerance of numeric data when String data may be assumed. eg: UDAT_YEAR_NAME_FIELD,
      *                 UDAT_STANDALONE_MONTH_FIELD, UDAT_DAY_OF_WEEK_FIELD
-     * @internal ICU technology preview
+     * @draft ICU 53
      */
     UDAT_PARSE_ALLOW_NUMERIC,
     /**
@@ -858,7 +859,7 @@ typedef enum UDateFormatBooleanAttribute {
     UDAT_PARSE_PARTIAL_MATCH,
     /**
      * count boolean date format constants
-     * @internal ICU technology preview
+     * @draft ICU 53
      */
     UDAT_BOOLEAN_ATTRIBUTE_COUNT
 } UDateFormatBooleanAttribute;
@@ -872,7 +873,7 @@ typedef enum UDateFormatBooleanAttribute {
  * @param attr The attribute to query; e.g. UDAT_PARSE_ALLOW_WHITESPACE.
  * @param status A pointer to an UErrorCode to receive any errors
  * @return The value of attr.
- * @internal technology preview
+ * @draft ICU 53
  */
 U_INTERNAL UBool U_EXPORT2
 udat_getBooleanAttribute(const UDateFormat* fmt, UDateFormatBooleanAttribute attr, UErrorCode* status);
@@ -885,7 +886,7 @@ udat_getBooleanAttribute(const UDateFormat* fmt, UDateFormatBooleanAttribute att
  * @param attr The attribute to set; one of UDAT_PARSE_ALLOW_WHITESPACE or UDAT_PARSE_ALLOW_NUMERIC
  * @param newValue The new value of attr.
  * @param status A pointer to an UErrorCode to receive any errors
- * @internal ICU technology preview
+ * @draft ICU 53
  */
 U_INTERNAL void U_EXPORT2
 udat_setBooleanAttribute(UDateFormat *fmt, UDateFormatBooleanAttribute attr, UBool, UErrorCode* status);
index d49c5b2f60d86c49c4c84b295418ff7b0c9180db..2534a5397f763c825470b6dea841c4ac9a87ad23 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT: 
- * Copyright (c) 1997-2013, International Business Machines Corporation and
+ * Copyright (c) 1997-2014, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************/
 
@@ -58,6 +58,7 @@ DateFormatRegressionTest::runIndexedTest( int32_t index, UBool exec, const char*
         CASE(27,Test9237)
         CASE(28,TestParsing)
         CASE(29,TestT10334)
+        CASE(30,TestT10619)
         default: name = ""; break;
     }
 }
@@ -85,6 +86,7 @@ void DateFormatRegressionTest::Test4029195(void)
     pat = sdf->toPattern(pat);
     logln("pattern: " + pat);
     UnicodeString fmtd;
+
     FieldPosition pos(FieldPosition::DONT_CARE);
     fmtd = sdf->format(today, fmtd, pos);
     logln("today: " + fmtd);
@@ -1580,6 +1582,75 @@ void DateFormatRegressionTest::TestT10334(void) {
 
 }
 
+
+typedef struct {
+    const char * locale;
+    UBool leniency;
+    UnicodeString parseString;
+    UnicodeString pattern;
+    UnicodeString expectedResult;       // null indicates expected error
+} TestDateFormatLeniencyItem;
+
+
+void DateFormatRegressionTest::TestT10619(void) {
+    const UDate july022008 = 1215000001979.0;
+    const TestDateFormatLeniencyItem items[] = {
+        /*
+            new TestDateFormatLeniencyItem(true,       "2008-Jan 02",     "yyyy-LLL. dd",         "2008-Jan. 02"),
+            new TestDateFormatLeniencyItem(false,      "2008-Jan 03",     "yyyy-LLL. dd",         null),
+            new TestDateFormatLeniencyItem(true,       "2008-Jan--04",    "yyyy-MMM' -- 'dd",     "2008-Jan -- 04"),
+            new TestDateFormatLeniencyItem(false,      "2008-Jan--05",    "yyyy-MMM' -- 'dd",     null),
+            new TestDateFormatLeniencyItem(true,       "2008-12-31",      "yyyy-mm-dd",           "2008-12-31"),
+            new TestDateFormatLeniencyItem(false,      "6 Jan 05 2008",   "eee MMM dd yyyy",      null),
+            new TestDateFormatLeniencyItem(true,       "6 Jan 05 2008",   "eee MMM dd yyyy",      "Sat Jan 05 2008"),
+        */
+        //locale    leniency    parse String                    pattern                             expected result
+        { "en",     true,       UnicodeString("2008-07 02"),    UnicodeString("yyyy-LLLL dd"),      UnicodeString("2008-July 02") },
+        { "en",     false,      UnicodeString("2008-07 02"),    UnicodeString("yyyy-LLLL dd"),      UnicodeString("") },
+        { "en",     true,       UnicodeString("2008-Jan 02"),   UnicodeString("yyyy-LLL. dd"),      UnicodeString("2008-Jan 02") },
+        { "en",     false,      UnicodeString("2008-Jan 02"),   UnicodeString("yyyy-LLL. dd"),      UnicodeString("") },
+        { "en",     true,       UnicodeString("2008-Jan--02"),  UnicodeString("yyyy-MMM' -- 'dd"),  UnicodeString("2008-Jan 02") },
+        { "en",     false,      UnicodeString("2008-Jan--02"),  UnicodeString("yyyy-MMM' -- 'dd"),  UnicodeString("") },
+        { "en",     true,       UnicodeString("6 Jan 05 2008"), UnicodeString("eee MMM dd yyyy"),   UnicodeString("Sat Jan 05 2008") },
+        { "en",     false,      UnicodeString("6 Jan 05 2008"), UnicodeString("eee MMM dd yyyy"),   UnicodeString("") },
+        // terminator
+        { NULL,     true,       UnicodeString(""),              UnicodeString(""),                  UnicodeString("") }                
+    };
+    UErrorCode status = U_ZERO_ERROR;
+    Calendar* cal = Calendar::createInstance(status);
+    if (U_FAILURE(status)) {
+        dataerrln(UnicodeString("FAIL: Unable to create Calendar for default timezone and locale."));
+    } else {
+        cal->setTime(july022008, status);
+        const TestDateFormatLeniencyItem * itemPtr;
+        for (itemPtr = items; itemPtr->locale != NULL; itemPtr++ ) {
+                                            
+           Locale locale = Locale::createFromName(itemPtr->locale);
+           status = U_ZERO_ERROR;
+           ParsePosition pos(0);
+           SimpleDateFormat * sdmft = new SimpleDateFormat(itemPtr->pattern, locale, status);
+           if (U_FAILURE(status)) {
+               dataerrln("Unable to create SimpleDateFormat - %s", u_errorName(status));
+               continue;
+           }
+           sdmft->setLenient(itemPtr->leniency);
+           sdmft->setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, itemPtr->leniency, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, itemPtr->leniency, status);
+           /*UDate d = */sdmft->parse(itemPtr->parseString, pos);
+
+           delete sdmft;
+           if(pos.getErrorIndex() > -1)
+               if(itemPtr->expectedResult.length() != 0) {
+                 errln("error: unexpected error - " + itemPtr->parseString + " - error index " + pos.getErrorIndex() + " - leniency " + itemPtr->leniency);
+                 continue;
+               } else {
+                 continue;
+               }
+        }
+    }
+    delete cal;
+
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
 //eof
index 1d5ed01b7c161ca15ff8450fe34fa2e1394e6f86..63955103881a9bc7562a2b92418c8260a3c731ca 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT: 
- * Copyright (c) 1997-2013, International Business Machines Corporation and
+ * Copyright (c) 1997-2014, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************/
 
@@ -54,6 +54,7 @@ public:
     void Test9237(void);
     void TestParsing(void);
     void TestT10334(void);
+    void TestT10619(void);
  };
 
 #endif /* #if !UCONFIG_NO_FORMATTING */