ICU-10619 Improve usability of DateFormat's leniency control & promote BooleanAttribute control to draft for v53

X-SVN-Rev: 34888
This commit is contained in:
Scott Russell 2014-01-14 17:26:35 +00:00
parent 8a3afd3cb5
commit c7677154e6
7 changed files with 175 additions and 32 deletions

View 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,

View 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.
*/

View 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;

View file

@ -1151,6 +1151,8 @@ private:
void initializeDefaultCentury(void);
void initializeBooleanAttributes(void);
SimpleDateFormat(); // default constructor not implemented
/**

View 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);

View 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

View 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 */