diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index adee2d66dfb..b7d2d9fe7c3 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -30,6 +30,7 @@ #if !UCONFIG_NO_FORMATTING +#include "unicode/uchar.h" #include "unicode/gregocal.h" #include "unicode/basictz.h" #include "unicode/simpletz.h" @@ -635,8 +636,9 @@ static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { { 1, 1, 7, 7 }, // DOW_LOCAL {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY - { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY - { 0, 0, 1, 1 }, // IS_LEAP_MONTH + { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY + { 0, 0, 1, 1 }, // IS_LEAP_MONTH + { UCHAR_MIN_VALUE, UCHAR_MIN_VALUE, UCHAR_MAX_VALUE, UCHAR_MAX_VALUE }, // TIME_SEPARATOR }; // Resource bundle tags read by this class @@ -2689,6 +2691,7 @@ int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) cons case UCAL_JULIAN_DAY: case UCAL_MILLISECONDS_IN_DAY: case UCAL_IS_LEAP_MONTH: + case UCAL_TIME_SEPARATOR: return kCalendarLimits[field][limitType]; case UCAL_WEEK_OF_MONTH: diff --git a/icu4c/source/i18n/dtfmtsym.cpp b/icu4c/source/i18n/dtfmtsym.cpp index f2ac6f75c22..e03384e3a9d 100644 --- a/icu4c/source/i18n/dtfmtsym.cpp +++ b/icu4c/source/i18n/dtfmtsym.cpp @@ -23,9 +23,11 @@ #if !UCONFIG_NO_FORMATTING #include "unicode/ustring.h" +#include "unicode/localpointer.h" #include "unicode/dtfmtsym.h" #include "unicode/smpdtfmt.h" #include "unicode/msgfmt.h" +#include "unicode/numsys.h" #include "unicode/tznames.h" #include "cpputils.h" #include "umutex.h" @@ -49,18 +51,18 @@ * resource data. */ -#define PATTERN_CHARS_LEN 35 +#define PATTERN_CHARS_LEN 36 /** * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. All * locales use the same these unlocalized pattern characters. */ static const UChar gPatternChars[] = { - // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr + // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr: 0x47, 0x79, 0x4D, 0x64, 0x6B, 0x48, 0x6D, 0x73, 0x53, 0x45, 0x44, 0x46, 0x77, 0x57, 0x61, 0x68, 0x4B, 0x7A, 0x59, 0x65, 0x75, 0x67, 0x41, 0x5A, 0x76, 0x63, 0x4c, 0x51, 0x71, 0x56, - 0x55, 0x4F, 0x58, 0x78, 0x72, 0 + 0x55, 0x4F, 0x58, 0x78, 0x72, 0x3a, 0 }; /* length of an array */ @@ -201,6 +203,9 @@ static const char gNamesStandaloneTag[]="stand-alone"; static const char gNamesNumericTag[]="numeric"; static const char gAmPmMarkersTag[]="AmPmMarkers"; static const char gQuartersTag[]="quarters"; +static const char gNumberElementsTag[]="NumberElements"; +static const char gSymbolsTag[]="symbols"; +static const char gTimeSeparatorTag[]="timeSeparator"; // static const char gZoneStringsTag[]="zoneStrings"; @@ -363,6 +368,7 @@ DateFormatSymbols::copyData(const DateFormatSymbols& other) { assignArray(fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, other.fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdaysCount); assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, other.fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdaysCount); assignArray(fAmPms, fAmPmsCount, other.fAmPms, other.fAmPmsCount); + fTimeSeparator.fastCopyFrom(other.fTimeSeparator); // fastCopyFrom() - see assignArray comments assignArray(fQuarters, fQuartersCount, other.fQuarters, other.fQuartersCount); assignArray(fShortQuarters, fShortQuartersCount, other.fShortQuarters, other.fShortQuartersCount); assignArray(fStandaloneQuarters, fStandaloneQuartersCount, other.fStandaloneQuarters, other.fStandaloneQuartersCount); @@ -541,6 +547,7 @@ DateFormatSymbols::operator==(const DateFormatSymbols& other) const arrayCompare(fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount) && arrayCompare(fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount) && arrayCompare(fAmPms, other.fAmPms, fAmPmsCount) && + fTimeSeparator == other.fTimeSeparator && arrayCompare(fQuarters, other.fQuarters, fQuartersCount) && arrayCompare(fShortQuarters, other.fShortQuarters, fShortQuartersCount) && arrayCompare(fStandaloneQuarters, other.fStandaloneQuarters, fStandaloneQuartersCount) && @@ -776,6 +783,13 @@ DateFormatSymbols::getQuarters(int32_t &count, DtContextType context, DtWidthTyp return returnValue; } +UnicodeString& +DateFormatSymbols::getTimeSeparatorString(UnicodeString& result) const +{ + // fastCopyFrom() - see assignArray comments + return result.fastCopyFrom(fTimeSeparator); +} + const UnicodeString* DateFormatSymbols::getAmPmStrings(int32_t &count) const { @@ -1165,6 +1179,12 @@ DateFormatSymbols::setAmPmStrings(const UnicodeString* amPmsArray, int32_t count fAmPmsCount = count; } +void +DateFormatSymbols::setTimeSeparatorString(const UnicodeString& newTimeSeparator) +{ + fTimeSeparator = newTimeSeparator; +} + const UnicodeString** DateFormatSymbols::getZoneStrings(int32_t& rowCount, int32_t& columnCount) const { @@ -1481,6 +1501,7 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError fStandaloneNarrowWeekdaysCount=0; fAmPms = NULL; fAmPmsCount=0; + fTimeSeparator.setToBogus(); fQuarters = NULL; fQuartersCount = 0; fShortQuarters = NULL; @@ -1628,9 +1649,34 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError } ures_close(contextTransforms); } + + tempStatus = U_ZERO_ERROR; + const LocalPointer numberingSystem( + NumberingSystem::createInstance(locale, tempStatus), tempStatus); + if (U_SUCCESS(tempStatus)) { + // These functions all fail gracefully if passed NULL pointers and + // do nothing unless U_SUCCESS(tempStatus), so it's only necessary + // to check for errors once after all calls are made. + const LocalUResourceBundlePointer numberElementsData(ures_getByKeyWithFallback( + localeBundle, gNumberElementsTag, NULL, &tempStatus)); + const LocalUResourceBundlePointer nsNameData(ures_getByKeyWithFallback( + numberElementsData.getAlias(), numberingSystem->getName(), NULL, &tempStatus)); + const LocalUResourceBundlePointer symbolsData(ures_getByKeyWithFallback( + nsNameData.getAlias(), gSymbolsTag, NULL, &tempStatus)); + fTimeSeparator = ures_getUnicodeStringByKey( + symbolsData.getAlias(), gTimeSeparatorTag, &tempStatus); + if (U_FAILURE(tempStatus)) { + fTimeSeparator.setToBogus(); + } + } + ures_close(localeBundle); } + if (fTimeSeparator.isBogus()) { + fTimeSeparator.setTo(DateFormatSymbols::DEFAULT_TIME_SEPARATOR); + } + UResourceBundle *weekdaysData = NULL; // Data closed by calData UResourceBundle *abbrWeekdaysData = NULL; // Data closed by calData UResourceBundle *shorterWeekdaysData = NULL; // Data closed by calData diff --git a/icu4c/source/i18n/gregocal.cpp b/icu4c/source/i18n/gregocal.cpp index 1854ccc69e9..cab04712e81 100644 --- a/icu4c/source/i18n/gregocal.cpp +++ b/icu4c/source/i18n/gregocal.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2013, International Business Machines Corporation and +* Copyright (C) 1997-2014, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * @@ -99,6 +99,7 @@ static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = { {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // TIME_SEPARATOR }; /* diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 18a444dd84a..adf0ec79b23 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -72,7 +72,7 @@ U_NAMESPACE_BEGIN -static const UChar PATTERN_CHAR_BASE = 0x40; +static const UChar PATTERN_CHAR_BASE = 0x3a; /** * Last-resort string to use for "GMT" when constructing time zone strings. @@ -140,8 +140,9 @@ static const UDateFormatField kTimeFields[] = { UDAT_HOUR0_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD, - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD }; -static const int8_t kTimeFieldsCount = 10; + UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, + UDAT_TIME_SEPARATOR_FIELD }; +static const int8_t kTimeFieldsCount = 11; // This is a pattern-of-last-resort used when we can't load a usable pattern out @@ -210,6 +211,7 @@ static const int32_t gFieldRangeBias[] = { -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD -1, // 'r' - UDAT_RELATED_YEAR_FIELD + -1, // ':' - UDAT_TIME_SEPARATOR_FIELD }; // When calendar uses hebr numbering (i.e. he@calendar=hebrew), @@ -1030,8 +1032,9 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, inQuote = ! inQuote; } } - else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) - || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + else if ( ! inQuote && ((ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/) + || (ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch == 0x003A /*':'*/))) { // ch is a date-time pattern character to be interpreted // by subFormat(); count the number of times it is repeated prevCh = ch; @@ -1073,24 +1076,28 @@ SimpleDateFormat::fgCalendarFieldToLevel[] = /*sS*/ 70, 80, /*z?Y*/ 0, 0, 10, /*eug*/ 30, 10, 0, - /*A?.*/ 40, 0, 0 + /*A?.*/ 40, 0, 0, + /*:*/ 0 }; /* Map calendar field LETTER into calendar field level. * the larger the level, the smaller the field unit. * NOTE: if new fields adds in, the table needs to update. + * PATTERN_CHAR_BASE is the codepoint of the first entry in this array. */ const int32_t SimpleDateFormat::fgPatternCharToLevel[] = { - // A B C D E F G H I J K L M N O + // : ; < = > ? + 0, -1, -1, -1, -1, -1, + // @ 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, 0, - // P Q R S T U V W X Y Z + // P Q R S T U V W X Y Z [ \ ] ^ _ -1, 20, -1, 80, -1, 10, 0, 30, 0, 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, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1 + -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0 }; @@ -1116,6 +1123,7 @@ SimpleDateFormat::fgPatternIndexToCalendarField[] = /*O*/ UCAL_ZONE_OFFSET, /*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET, /*r*/ UCAL_EXTENDED_YEAR, + /*:*/ UCAL_TIME_SEPARATOR, }; // Map index into pattern character string to DateFormat field number @@ -1139,6 +1147,7 @@ SimpleDateFormat::fgPatternIndexToDateFormatField[] = { /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, /*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD, /*r*/ UDAT_RELATED_YEAR_FIELD, + /*:*/ UDAT_TIME_SEPARATOR_FIELD, }; //---------------------------------------------------------------------- @@ -1570,6 +1579,14 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, fSymbols->fAmPmsCount); break; + // for ":", write out the time separator string + case UDAT_TIME_SEPARATOR_FIELD: + { + UnicodeString separator; + appendTo += fSymbols->getTimeSeparatorString(separator); + } + break; + // for "h" and "hh", write out the hour, adjusting noon and midnight to show up // as "12" case UDAT_HOUR1_FIELD: @@ -1937,7 +1954,9 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& UChar ch = fPattern.charAt(i); // Handle alphabetic field characters. - if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z] + if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) + || (ch >= 0x61 && ch <= 0x7A) + || (ch == 0x3A))) { // [A-Za-z:] int32_t fieldPat = i; // Count the length of this field specifier @@ -2329,7 +2348,9 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern, for ( ; i < pattern.length(); i += 1) { UChar ch = pattern.charAt(i); - if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // unquoted [A-Za-z] + if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) + || (ch >= 0x61 && ch <= 0x7A) + || (ch == 0x3A))) { // unquoted [A-Za-z:] break; } @@ -3236,6 +3257,28 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC } return -start; } + case UDAT_TIME_SEPARATOR_FIELD: // ':' + { + static const UChar def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR; + static const UChar alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR; + + // Try matching a time separator. + int32_t count = 1; + UnicodeString data[3]; + fSymbols->getTimeSeparatorString(data[0]); + + // Add the default, if different from the locale. + if (data[0].compare(&def_sep, 1) != 0) { + data[count++].setTo(def_sep); + } + + // If lenient, add also the alternate, if different from the locale. + if (isLenient() && data[0].compare(&alt_sep, 1) != 0) { + data[count++].setTo(alt_sep); + } + + return matchString(text, start, UCAL_TIME_SEPARATOR, data, count, NULL, cal); + } default: // Handle "generic" fields @@ -3395,8 +3438,9 @@ void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, else { if (c == QUOTE) inQuote = TRUE; - else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ - || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) { + else if ((c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/) + || (c >= 0x0061 /*'a'*/ && c <= 0x007A /*'z'*/) + || (c == 0x003A /*':'*/)) { int32_t ci = from.indexOf(c); if (ci == -1) { status = U_INVALID_FORMAT_ERROR; @@ -3581,8 +3625,9 @@ SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, inQuote = ! inQuote; } } - else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) - || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + else if ( ! inQuote && ((ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/) + || (ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch == 0x003A /*':'*/))) { prevCh = ch; ++count; } diff --git a/icu4c/source/i18n/unicode/dtfmtsym.h b/icu4c/source/i18n/unicode/dtfmtsym.h index e714ce027ce..5dfe282a77b 100644 --- a/icu4c/source/i18n/unicode/dtfmtsym.h +++ b/icu4c/source/i18n/unicode/dtfmtsym.h @@ -408,6 +408,36 @@ public: */ void setAmPmStrings(const UnicodeString* ampms, int32_t count); +#ifndef U_HIDE_INTERNAL_API + /** + * This default time separator is used for formatting when the locale + * doesn't specify any time separator, and always recognized when parsing. + * @internal + */ + static const UChar DEFAULT_TIME_SEPARATOR = 0x003a; // ':' + + /** + * This alternate time separator is always recognized when parsing. + * @internal + */ + static const UChar ALTERNATE_TIME_SEPARATOR = 0x002e; // '.' + + /** + * Gets the time separator string. For example: ":". + * @param result Output param which will receive the time separator string. + * @return A reference to 'result'. + * @internal + */ + UnicodeString& getTimeSeparatorString(UnicodeString& result) const; + + /** + * Sets the time separator string. For example: ":". + * @param newTimeSeparator the new time separator string. + * @internal + */ + void setTimeSeparatorString(const UnicodeString& newTimeSeparator); +#endif /* U_HIDE_INTERNAL_API */ + #ifndef U_HIDE_DRAFT_API /** * Gets cyclic year name strings if the calendar has them, by width and context. @@ -710,6 +740,11 @@ private: UnicodeString* fAmPms; int32_t fAmPmsCount; + /** + * Time separator string. For example: ":". + */ + UnicodeString fTimeSeparator; + /** * Quarter strings. For example: "1st quarter", "2nd quarter", etc. */ diff --git a/icu4c/source/i18n/unicode/ucal.h b/icu4c/source/i18n/unicode/ucal.h index f7a1c1248e5..96e5f3690a7 100644 --- a/icu4c/source/i18n/unicode/ucal.h +++ b/icu4c/source/i18n/unicode/ucal.h @@ -422,7 +422,18 @@ enum UCalendarDateFields { * an example of this. */ UCAL_IS_LEAP_MONTH, - + + /** + * Field number indicating the separator between + * HOUR, MINUTE and SECOND. + * E.g., at 10:04 the TIME_SEPARATOR is :. + * @see #UCAL_HOUR + * @see #UCAL_MINUTE + * @see #UCAL_SECOND + * @internal + */ + UCAL_TIME_SEPARATOR, + /** * Field count * @stable ICU 2.6 diff --git a/icu4c/source/i18n/unicode/udat.h b/icu4c/source/i18n/unicode/udat.h index 217eec83c19..30bf8a54059 100644 --- a/icu4c/source/i18n/unicode/udat.h +++ b/icu4c/source/i18n/unicode/udat.h @@ -758,6 +758,13 @@ typedef enum UDateFormatField { * @internal ICU 53 */ UDAT_RELATED_YEAR_FIELD = 34, + + /** + * FieldPosition and UFieldPosition selector for ':' time separator, + * corresponding to the UCAL_TIME_SEPARATOR field. + * @internal + */ + UDAT_TIME_SEPARATOR_FIELD = 35, #endif /* U_HIDE_INTERNAL_API */ /** @@ -768,7 +775,7 @@ typedef enum UDateFormatField { * in the future. * @stable ICU 3.0 */ - UDAT_FIELD_COUNT = 35 + UDAT_FIELD_COUNT = 36 } UDateFormatField; diff --git a/icu4c/source/test/cintltst/cdattst.c b/icu4c/source/test/cintltst/cdattst.c index 006d879e368..79bad6d5c0a 100644 --- a/icu4c/source/test/cintltst/cdattst.c +++ b/icu4c/source/test/cintltst/cdattst.c @@ -673,7 +673,7 @@ static void TestSymbols() VerifygetSymbols(zhChiCal, UDAT_CYCLIC_YEARS_NARROW, 59, "\\u7678\\u4EA5"); VerifygetSymbols(zhChiCal, UDAT_ZODIAC_NAMES_ABBREVIATED, 0, "\\u9F20"); VerifygetSymbols(zhChiCal, UDAT_ZODIAC_NAMES_WIDE, 11, "\\u732A"); - VerifygetSymbols(def,UDAT_LOCALIZED_CHARS, 0, "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr"); + VerifygetSymbols(def,UDAT_LOCALIZED_CHARS, 0, "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr:"); if(result != NULL) { diff --git a/icu4c/source/test/intltest/dtfmrgts.cpp b/icu4c/source/test/intltest/dtfmrgts.cpp index 61106fcb471..2f803167617 100644 --- a/icu4c/source/test/intltest/dtfmrgts.cpp +++ b/icu4c/source/test/intltest/dtfmrgts.cpp @@ -1531,7 +1531,7 @@ void DateFormatRegressionTest::TestParsing(void) { void DateFormatRegressionTest::TestT10334(void) { UErrorCode status = U_ZERO_ERROR; - UnicodeString pattern("--: EEE-WW-MMMM-yyyy"); + UnicodeString pattern("'--: 'EEE-WW-MMMM-yyyy"); UnicodeString text("--mon-02-march-2011"); SimpleDateFormat format(pattern, status); diff --git a/icu4c/source/test/intltest/dtfmttst.cpp b/icu4c/source/test/intltest/dtfmttst.cpp index 0e9135d4b99..bb494805ee3 100644 --- a/icu4c/source/test/intltest/dtfmttst.cpp +++ b/icu4c/source/test/intltest/dtfmttst.cpp @@ -58,6 +58,7 @@ void DateFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &nam TESTCASE_AUTO(TestDateFormatZone061); TESTCASE_AUTO(TestDateFormatZone146); TESTCASE_AUTO(TestLocaleDateFormat); + TESTCASE_AUTO(TestFormattingLocaleTimeSeparator); TESTCASE_AUTO(TestWallyWedel); TESTCASE_AUTO(TestDateFormatCalendar); TESTCASE_AUTO(TestSpaceParsing); @@ -432,7 +433,7 @@ DateFormatTest::escape(UnicodeString& s) /** * This MUST be kept in sync with DateFormatSymbols.gPatternChars. */ -static const char* PATTERN_CHARS = "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr"; +static const char* PATTERN_CHARS = "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr:"; /** * A list of the names of all the fields in DateFormat. @@ -474,6 +475,7 @@ static const char* DATEFORMAT_FIELD_NAMES[] = { "TIMEZONE_ISO_FIELD", "TIMEZONE_ISO_LOCAL_FIELD", "RELATED_YEAR_FIELD", + "UDAT_TIME_SEPARATOR_FIELD", }; static const int32_t DATEFORMAT_FIELD_NAMES_LENGTH = @@ -529,22 +531,22 @@ void DateFormatTest::TestFieldPosition() { "", "1997", "August", "13", "", "", "34", "12", "", "Wednesday", "", "", "", "", "PM", "2", "", "Pacific Daylight Time", "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", + "", "", "", "", "", ":", "", "1997", "ao\\u00FBt", "13", "", "14", "34", "12", "", "mercredi", "", "", "", "", "", "", "", "heure d\\u2019\\u00E9t\\u00E9 du Pacifique", "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", + "", "", "", "", "", ":", "AD", "1997", "8", "13", "14", "14", "34", "12", "5", "Wed", "225", "2", "33", "3", "PM", "2", "2", "PDT", "1997", "4", "1997", "2450674", "52452513", "-0700", "PT", "4", "8", "3", "3", "uslax", - "1997", "GMT-7", "-07", "-07", "1997", + "1997", "GMT-7", "-07", "-07", "1997", ":", "Anno Domini", "1997", "August", "0013", "0014", "0014", "0034", "0012", "5130", "Wednesday", "0225", "0002", "0033", "0003", "PM", "0002", "0002", "Pacific Daylight Time", "1997", "Wednesday", "1997", "2450674", "52452513", "GMT-07:00", "Pacific Time", "Wednesday", "August", "3rd quarter", "3rd quarter", "Los Angeles Time", - "1997", "GMT-07:00", "-0700", "-0700","1997", + "1997", "GMT-07:00", "-0700", "-0700", "1997", ":", }; const int32_t EXPECTED_LENGTH = sizeof(EXPECTED)/sizeof(EXPECTED[0]); @@ -1313,6 +1315,44 @@ DateFormatTest::TestLocaleDateFormat() // Bug 495 delete dfFrench; } +void +DateFormatTest::TestFormattingLocaleTimeSeparator() +{ + const UDate testDate = 874266720000.; // Sun Sep 14 21:52:00 CET 1997 + logln((UnicodeString)"Date set to : " + dateToString(testDate)); + + const LocalPointer tz(TimeZone::createTimeZone("CET")); + + const LocalPointer dfArab(DateFormat::createTimeInstance( + DateFormat::SHORT, Locale("ar"))); + + const LocalPointer dfLatn(DateFormat::createTimeInstance( + DateFormat::SHORT, Locale("ar", NULL, NULL, "numbers=latn"))); + + if (dfLatn.isNull() || dfArab.isNull()) { + dataerrln("Error calling DateFormat::createTimeInstance()"); + return; + } + + dfArab->setTimeZone(*tz); + dfLatn->setTimeZone(*tz); + + const UnicodeString expectedArab = UnicodeString( + "\\u0669\\u060C\\u0665\\u0662 \\u0645", -1, US_INV).unescape(); + + const UnicodeString expectedLatn = UnicodeString( + "9:52 \\u0645", -1, US_INV).unescape(); + + UnicodeString actualArab; + UnicodeString actualLatn; + + dfArab->format(testDate, actualArab); + dfLatn->format(testDate, actualLatn); + + assertEquals("Arab", expectedArab, actualArab); + assertEquals("Latn", expectedLatn, actualLatn); +} + /** * Test DateFormat(Calendar) API */ @@ -1425,7 +1465,6 @@ void DateFormatTest::TestSpaceParsing() { "hh:mm:ss a", "12:34:56 PM", "1970 01 01 12:34:56", NULL, "12:34:56PM", "1970 01 01 12:34:56", NULL, "12.34.56PM", "1970 01 01 12:34:56", - NULL, "12-34-56 PM", "1970 01 01 12:34:56", NULL, "12 : 34 : 56 PM", "1970 01 01 12:34:56", "MM d yy 'at' hh:mm:ss a", "04/05/06 12:34:56 PM", "2006 04 05 12:34:56", diff --git a/icu4c/source/test/intltest/dtfmttst.h b/icu4c/source/test/intltest/dtfmttst.h index 2c3ef5c9950..19d7c0cb64d 100644 --- a/icu4c/source/test/intltest/dtfmttst.h +++ b/icu4c/source/test/intltest/dtfmttst.h @@ -150,6 +150,8 @@ public: // package */ virtual void TestLocaleDateFormat(void); + virtual void TestFormattingLocaleTimeSeparator(void); + virtual void TestDateFormatCalendar(void); virtual void TestSpaceParsing(void); diff --git a/icu4c/source/tools/toolutil/udbgutil.cpp b/icu4c/source/tools/toolutil/udbgutil.cpp index efad36345f5..daeb48cce85 100644 --- a/icu4c/source/tools/toolutil/udbgutil.cpp +++ b/icu4c/source/tools/toolutil/udbgutil.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 2007-2013, International Business Machines Corporation and + * Copyright (c) 2007-2014, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -106,6 +106,7 @@ static const Field names_UCalendarDateFields[] = FIELD_NAME_STR( LEN_UCAL, UCAL_JULIAN_DAY ), FIELD_NAME_STR( LEN_UCAL, UCAL_MILLISECONDS_IN_DAY ), FIELD_NAME_STR( LEN_UCAL, UCAL_IS_LEAP_MONTH ), + FIELD_NAME_STR( LEN_UCAL, UCAL_TIME_SEPARATOR ), };