diff --git a/icu4c/source/i18n/datefmt.cpp b/icu4c/source/i18n/datefmt.cpp index e8641f43200..76ec3f48827 100644 --- a/icu4c/source/i18n/datefmt.cpp +++ b/icu4c/source/i18n/datefmt.cpp @@ -460,7 +460,12 @@ DateFormat::createInstanceForSkeleton( status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } - DateFormat *result = createInstanceForSkeleton(skeleton, locale, status); + Locale localeWithCalendar = locale; + localeWithCalendar.setKeywordValue("calendar", calendar->getType(), status); + if (U_FAILURE(status)) { + return NULL; + } + DateFormat *result = createInstanceForSkeleton(skeleton, localeWithCalendar, status); if (U_FAILURE(status)) { return NULL; } diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 8a95a02939e..b691944689c 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -1950,7 +1950,8 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, } #if !UCONFIG_NO_BREAK_ITERATION // if first field, check to see whether we need to and are able to titlecase it - if (fieldNum == 0 && u_islower(appendTo.char32At(beginOffset)) && fCapitalizationBrkIter != NULL) { + if (fieldNum == 0 && fCapitalizationBrkIter != NULL && appendTo.length() > beginOffset && + u_islower(appendTo.char32At(beginOffset))) { UBool titlecase = FALSE; switch (capitalizationContext) { case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE: diff --git a/icu4c/source/test/intltest/dtfmrgts.cpp b/icu4c/source/test/intltest/dtfmrgts.cpp index 260f8fa5236..5e04379915a 100644 --- a/icu4c/source/test/intltest/dtfmrgts.cpp +++ b/icu4c/source/test/intltest/dtfmrgts.cpp @@ -59,11 +59,12 @@ DateFormatRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* CASE(26,Test5554) CASE(27,Test9237) CASE(28,TestParsing) - CASE(29,TestT10334) - CASE(30,TestT10619) - CASE(31,TestT10855) - CASE(32,TestT10906) - CASE(33,TestT13380) + CASE(29,Test12902_yWithGregoCalInThaiLoc) + CASE(30,TestT10334) + CASE(31,TestT10619) + CASE(32,TestT10855) + CASE(33,TestT10906) + CASE(34,TestT13380) default: name = ""; break; } } @@ -1533,6 +1534,55 @@ void DateFormatRegressionTest::TestParsing(void) { delete cal; } +void DateFormatRegressionTest::Test12902_yWithGregoCalInThaiLoc(void) { + UErrorCode status = U_ZERO_ERROR; + UDate testDate = 43200000.0; // 1970-Jan-01 12:00 GMT + const char* skeleton = "y"; + // Note that in locale "th", the availableFormats for skeleton "y" differ by calendar: + // for buddhist (default calendar): y{"G y"} + // for gregorian: y{"y"} + const char* expectFormat = "1970"; // format for skeleton y in Thai locale with Gregorian calendar + UnicodeString getFmtStr; + + const TimeZone* gmtZone = TimeZone::getGMT(); + LocalPointer pureGregoCal(new GregorianCalendar(*gmtZone, Locale::getEnglish(), status)); + if (U_FAILURE(status)) { + dataerrln("Fail in new GregorianCalendar(GMT, en): %s", u_errorName(status)); + return; + } + LocalPointer df1(DateFormat::createInstanceForSkeleton(pureGregoCal.orphan(), skeleton, "th", status)); + if (U_FAILURE(status)) { + dataerrln("Fail in DateFormat::createInstanceForSkeleton for th with Grego cal: %s", u_errorName(status)); + return; + } + status = U_ZERO_ERROR; + getFmtStr.remove(); + getFmtStr = df1->format(testDate, getFmtStr); + if (U_FAILURE(status)) { + errln("Fail in DateFormat::format for th with Grego cal: %s", u_errorName(status)); + } else if (getFmtStr != expectFormat) { + char getFormat[32]; + getFmtStr.extract(0, getFmtStr.length(), getFormat, 32); + errln("Error in DateFormat::format for th with Grego cal, expect: %s, get: %s", expectFormat, getFormat); + } + + LocalPointer df2(DateFormat::createInstanceForSkeleton(skeleton, "th-u-ca-gregory", status)); + if (U_FAILURE(status)) { + dataerrln("Fail in DateFormat::createInstanceForSkeleton for th-u-ca-gregory: %s", u_errorName(status)); + return; + } + status = U_ZERO_ERROR; + getFmtStr.remove(); + getFmtStr = df2->format(testDate, getFmtStr); + if (U_FAILURE(status)) { + errln("Fail in DateFormat::format for th-u-ca-gregory: %s", u_errorName(status)); + } else if (getFmtStr != expectFormat) { + char getFormat[32]; + getFmtStr.extract(0, getFmtStr.length(), getFormat, 32); + errln("Error in DateFormat::format for th with Grego cal, expect: %s, get: %s", expectFormat, getFormat); + } +} + void DateFormatRegressionTest::TestT10334(void) { UErrorCode status = U_ZERO_ERROR; UnicodeString pattern("'--: 'EEE-WW-MMMM-yyyy"); diff --git a/icu4c/source/test/intltest/dtfmrgts.h b/icu4c/source/test/intltest/dtfmrgts.h index be8dd0f2261..305487db17c 100644 --- a/icu4c/source/test/intltest/dtfmrgts.h +++ b/icu4c/source/test/intltest/dtfmrgts.h @@ -55,6 +55,7 @@ public: void Test5554(void); void Test9237(void); void TestParsing(void); + void Test12902_yWithGregoCalInThaiLoc(void); void TestT10334(void); void TestT10619(void); void TestT10855(void); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java index d149841f026..bbc11749809 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java @@ -2196,6 +2196,9 @@ public abstract class DateFormat extends UFormat { */ public final static DateFormat getInstanceForSkeleton( Calendar cal, String skeleton, ULocale locale) { + if (cal != null) { + locale = locale.setKeywordValue("calendar", cal.getType()); + } DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale); final String bestPattern = generator.getBestPattern(skeleton); SimpleDateFormat format = new SimpleDateFormat(bestPattern, locale); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java index 7cdedd74b96..c14c2388119 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java @@ -2041,7 +2041,8 @@ public class SimpleDateFormat extends DateFormat { break; } // switch (patternCharIndex) - if (fieldNum == 0 && capitalizationContext != null && UCharacter.isLowerCase(buf.codePointAt(bufstart))) { + if (fieldNum == 0 && capitalizationContext != null && buf.length() > bufstart && + UCharacter.isLowerCase(buf.codePointAt(bufstart))) { boolean titlecase = false; switch (capitalizationContext) { case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE: diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatRegressionTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatRegressionTest.java index 350a9f4f2a4..83826ec41f3 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatRegressionTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatRegressionTest.java @@ -1238,6 +1238,38 @@ public class DateFormatRegressionTest extends TestFmwk { assertEquals("Bad date format", "Mo1 20, 2013", dangiDateStr); } + @Test + public void Test12902_yWithGregoCalInThaiLoc() { + final Date testDate = new Date(43200000); // 1970-Jan-01 12:00 GMT + final String skeleton = "y"; + // Note that in locale "th", the availableFormats for skeleton "y" differ by calendar: + // for buddhist (default calendar): y{"G y"} + // for gregorian: y{"y"} + final String expectFormat = "1970"; // format for skeleton y in Thai locale with Gregorian calendar + + GregorianCalendar pureGregorianCalendar = new GregorianCalendar(TimeZone.GMT_ZONE, ULocale.ENGLISH); + pureGregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE)); // Per original bug, but not relevant + DateFormat df1 = DateFormat.getPatternInstance(pureGregorianCalendar, skeleton, ULocale.forLanguageTag("th")); + try { + String getFormat = df1.format(testDate); + if (!getFormat.equals(expectFormat)) { + errln("Error in DateFormat.format for th with Grego cal, expect: " + expectFormat + ", get: " + getFormat); + } + } catch (Exception e) { + errln("Fail in DateFormat.format for th with Grego cal: " + e); + } + + DateFormat df2 = DateFormat.getPatternInstance(skeleton, new ULocale("th-u-ca-gregory")); + try { + String getFormat = df2.format(testDate); + if (!getFormat.equals(expectFormat)) { + errln("Error in DateFormat.format for th-u-ca-gregory, expect: " + expectFormat + ", get: " + getFormat); + } + } catch (Exception e) { + errln("Fail in DateFormat.format for th-u-ca-gregory: " + e); + } + } + @Test public void TestT10110() { try {