diff --git a/intl/locale/DateTimeFormat.cpp b/intl/locale/DateTimeFormat.cpp index 1e90d8cbbea3..db6b43b645ac 100644 --- a/intl/locale/DateTimeFormat.cpp +++ b/intl/locale/DateTimeFormat.cpp @@ -8,12 +8,9 @@ #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "mozilla/intl/LocaleService.h" -#include "OSPreferences.h" -#include "mozIOSPreferences.h" #include "unicode/udatpg.h" namespace mozilla { -using namespace mozilla::intl; nsCString* DateTimeFormat::mLocale = nullptr; @@ -87,115 +84,48 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector, return rv; } - // Get the date style for the formatter. - nsAutoString skeletonDate; - nsAutoString patternDate; - bool haveSkeleton = true; + // Get the date style for the formatter: + UDateFormatStyle dateStyle; switch (aDateFormatSelector) { - case kDateFormatLong: - rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleLong, - mozIOSPreferences::dateTimeFormatStyleNone, - nsDependentCString(mLocale->get()), - patternDate); - NS_ENSURE_SUCCESS(rv, rv); - haveSkeleton = false; - break; - case kDateFormatShort: - rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort, - mozIOSPreferences::dateTimeFormatStyleNone, - nsDependentCString(mLocale->get()), - patternDate); - NS_ENSURE_SUCCESS(rv, rv); - haveSkeleton = false; - break; - case kDateFormatYearMonth: - skeletonDate.AssignLiteral("yyyyMM"); - break; - case kDateFormatYearMonthLong: - skeletonDate.AssignLiteral("yyyyMMMM"); - break; - case kDateFormatMonthLong: - skeletonDate.AssignLiteral("MMMM"); - break; - case kDateFormatWeekday: - skeletonDate.AssignLiteral("EEE"); - break; - case kDateFormatNone: - haveSkeleton = false; - break; - default: - NS_ERROR("Unknown nsDateFormatSelector"); - return NS_ERROR_ILLEGAL_VALUE; + case kDateFormatLong: + dateStyle = UDAT_LONG; + break; + case kDateFormatShort: + dateStyle = UDAT_SHORT; + break; + case kDateFormatYearMonth: + case kDateFormatYearMonthLong: + case kDateFormatMonthLong: + case kDateFormatWeekday: + dateStyle = UDAT_PATTERN; + break; + case kDateFormatNone: + dateStyle = UDAT_NONE; + break; + default: + NS_ERROR("Unknown nsDateFormatSelector"); + return NS_ERROR_ILLEGAL_VALUE; } - UErrorCode status = U_ZERO_ERROR; - if (haveSkeleton) { - // Get pattern for skeleton. - UDateTimePatternGenerator* patternGenerator = udatpg_open(mLocale->get(), &status); - if (U_SUCCESS(status)) { - int32_t patternLength; - patternDate.SetLength(DATETIME_FORMAT_INITIAL_LEN); - patternLength = udatpg_getBestPattern(patternGenerator, - reinterpret_cast(skeletonDate.BeginReading()), - skeletonDate.Length(), - reinterpret_cast(patternDate.BeginWriting()), - DATETIME_FORMAT_INITIAL_LEN, - &status); - patternDate.SetLength(patternLength); - - if (status == U_BUFFER_OVERFLOW_ERROR) { - status = U_ZERO_ERROR; - udatpg_getBestPattern(patternGenerator, - reinterpret_cast(skeletonDate.BeginReading()), - skeletonDate.Length(), - reinterpret_cast(patternDate.BeginWriting()), - patternLength, - &status); - } - } - udatpg_close(patternGenerator); - } - - // Get the time style for the formatter. - nsAutoString patternTime; + // Get the time style for the formatter: + UDateFormatStyle timeStyle; switch (aTimeFormatSelector) { - case kTimeFormatSeconds: - rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone, - mozIOSPreferences::dateTimeFormatStyleLong, - nsDependentCString(mLocale->get()), - patternTime); - NS_ENSURE_SUCCESS(rv, rv); - break; - case kTimeFormatNoSeconds: - rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone, - mozIOSPreferences::dateTimeFormatStyleShort, - nsDependentCString(mLocale->get()), - patternTime); - NS_ENSURE_SUCCESS(rv, rv); - break; - case kTimeFormatNone: - break; - default: - NS_ERROR("Unknown nsTimeFormatSelector"); - return NS_ERROR_ILLEGAL_VALUE; + case kTimeFormatSeconds: + timeStyle = UDAT_MEDIUM; + break; + case kTimeFormatNoSeconds: + timeStyle = UDAT_SHORT; + break; + case kTimeFormatNone: + timeStyle = UDAT_NONE; + break; + default: + NS_ERROR("Unknown nsTimeFormatSelector"); + return NS_ERROR_ILLEGAL_VALUE; } - nsAutoString pattern; - if (patternTime.Length() == 0) { - pattern.Assign(patternDate); - } else if (patternDate.Length() == 0) { - pattern.Assign(patternTime); - } else { - OSPreferences::GetDateTimeConnectorPattern(nsDependentCString(mLocale->get()), pattern); - int32_t index = pattern.Find("{1}"); - if (index != kNotFound) - pattern.Replace(index, 3, patternDate); - index = pattern.Find("{0}"); - if (index != kNotFound) - pattern.Replace(index, 3, patternTime); - } + // generate date/time string - // Generate date/time string. nsAutoString timeZoneID(u"GMT"); if (aTimeParameters) { int32_t totalOffsetMinutes = (aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60; @@ -207,38 +137,100 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector, } } + UErrorCode status = U_ZERO_ERROR; + UDateFormat* dateTimeFormat; - if (aTimeParameters) { - dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), - reinterpret_cast(timeZoneID.BeginReading()), - timeZoneID.Length(), - reinterpret_cast(pattern.BeginReading()), - pattern.Length(), - &status); + if (dateStyle == UDAT_PATTERN) { + nsAutoString pattern; + + dateTimeFormat = udat_open(timeStyle, UDAT_NONE, mLocale->get(), nullptr, -1, nullptr, -1, &status); + + if (U_SUCCESS(status) && dateTimeFormat) { + int32_t patternLength; + if (timeStyle != UDAT_NONE) { + pattern.SetLength(DATETIME_FORMAT_INITIAL_LEN); + patternLength = udat_toPattern(dateTimeFormat, FALSE, reinterpret_cast(pattern.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, &status); + pattern.SetLength(patternLength); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + udat_toPattern(dateTimeFormat, FALSE, reinterpret_cast(pattern.BeginWriting()), patternLength, &status); + } + } + + nsAutoString skeleton; + switch (aDateFormatSelector) { + case kDateFormatYearMonth: + skeleton.AssignLiteral("yyyyMM "); + break; + case kDateFormatYearMonthLong: + skeleton.AssignLiteral("yyyyMMMM "); + break; + case kDateFormatMonthLong: + skeleton.AssignLiteral("MMMM "); + break; + case kDateFormatWeekday: + skeleton.AssignLiteral("EEE "); + break; + default: + break; + } + int32_t dateSkeletonLen = skeleton.Length(); + + if (timeStyle != UDAT_NONE) { + skeleton.SetLength(DATETIME_FORMAT_INITIAL_LEN); + int32_t skeletonLength = udatpg_getSkeleton(nullptr, reinterpret_cast(pattern.BeginReading()), patternLength, + reinterpret_cast(skeleton.BeginWriting() + dateSkeletonLen), DATETIME_FORMAT_INITIAL_LEN - dateSkeletonLen, &status); + skeleton.SetLength(dateSkeletonLen + skeletonLength); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + udatpg_getSkeleton(nullptr, reinterpret_cast(pattern.BeginReading()), patternLength, + reinterpret_cast(skeleton.BeginWriting() + dateSkeletonLen), dateSkeletonLen + skeletonLength, &status); + } + } + + UDateTimePatternGenerator* patternGenerator = udatpg_open(mLocale->get(), &status); + if (U_SUCCESS(status)) { + pattern.SetLength(DATETIME_FORMAT_INITIAL_LEN); + patternLength = udatpg_getBestPattern(patternGenerator, reinterpret_cast(skeleton.BeginReading()), skeleton.Length(), + reinterpret_cast(pattern.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, &status); + pattern.SetLength(patternLength); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + udatpg_getBestPattern(patternGenerator, reinterpret_cast(skeleton.BeginReading()), skeleton.Length(), + reinterpret_cast(pattern.BeginWriting()), patternLength, &status); + } + } + + udatpg_close(patternGenerator); + } + + udat_close(dateTimeFormat); + + if (aTimeParameters) { + dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), reinterpret_cast(timeZoneID.BeginReading()), timeZoneID.Length(), + reinterpret_cast(pattern.BeginReading()), pattern.Length(), &status); + } else { + dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), nullptr, -1, reinterpret_cast(pattern.BeginReading()), pattern.Length(), &status); + } } else { - dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), - nullptr, -1, - reinterpret_cast(pattern.BeginReading()), - pattern.Length(), - &status); + if (aTimeParameters) { + dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), reinterpret_cast(timeZoneID.BeginReading()), timeZoneID.Length(), nullptr, -1, &status); + } else { + dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status); + } } if (U_SUCCESS(status) && dateTimeFormat) { aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN); - dateTimeLen = udat_format(dateTimeFormat, aUDateTime, - reinterpret_cast(aStringOut.BeginWriting()), - DATETIME_FORMAT_INITIAL_LEN, - nullptr, - &status); + dateTimeLen = udat_format(dateTimeFormat, aUDateTime, reinterpret_cast(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status); aStringOut.SetLength(dateTimeLen); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; - udat_format(dateTimeFormat, aUDateTime, - reinterpret_cast(aStringOut.BeginWriting()), - dateTimeLen, - nullptr, - &status); + udat_format(dateTimeFormat, aUDateTime, reinterpret_cast(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status); } } diff --git a/intl/locale/OSPreferences.h b/intl/locale/OSPreferences.h index c38b3f6c301b..d9fc97d9076a 100644 --- a/intl/locale/OSPreferences.h +++ b/intl/locale/OSPreferences.h @@ -101,9 +101,6 @@ public: */ bool GetSystemLocales(nsTArray& aRetVal); - static bool GetDateTimeConnectorPattern(const nsACString& aLocale, - nsAString& aRetVal); - protected: nsTArray mSystemLocales; @@ -132,6 +129,9 @@ private: const nsACString& aLocale, nsAString& aRetVal); + bool GetDateTimeConnectorPattern(const nsACString& aLocale, + nsAString& aRetVal); + /** * This is a host environment specific method that will be implemented * separately for each platform. diff --git a/intl/locale/tests/gtest/TestDateTimeFormat.cpp b/intl/locale/tests/gtest/TestDateTimeFormat.cpp index 3a522cb32c9e..81a363837ca8 100644 --- a/intl/locale/tests/gtest/TestDateTimeFormat.cpp +++ b/intl/locale/tests/gtest/TestDateTimeFormat.cpp @@ -3,32 +3,6 @@ namespace mozilla { -// Normalise time. -static nsAutoCString nt(nsAutoCString aDatetime) -{ - nsAutoCString datetime = aDatetime; - - // Replace "January 01" with "January 1" (found on Windows). - int32_t ind = datetime.Find("January 01"); - if (ind != kNotFound) - datetime.Replace(ind, 10, "January 1"); - - // Strip trailing " GMT" (found on Mac/Linux). - ind = datetime.Find(" GMT"); - if (ind != kNotFound) - datetime.Truncate(ind); - - // Strip leading "Thursday, " or "Wednesday, " (found on Windows). - ind = datetime.Find("Thursday, "); - if (ind == 0) - datetime.Replace(0, 10, ""); - ind = datetime.Find("Wednesday, "); - if (ind == 0) - datetime.Replace(0, 11, ""); - - return datetime; -} - TEST(DateTimeFormat, FormatPRExplodedTime) { PRTime prTime = 0; PRExplodedTime prExplodedTime; @@ -39,37 +13,37 @@ TEST(DateTimeFormat, FormatPRExplodedTime) { nsAutoString formattedTime; nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("January 1, 1970, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("January 1, 1970 at 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 19, 0, 1, 0, 1970, 4, 0, { (19 * 60), 0 } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("January 1, 1970, 12:19:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("January 1, 1970 at 12:19:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 0, 7, 1, 0, 1970, 4, 0, { (6 * 60 * 60), (1 * 60 * 60) } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("January 1, 1970, 7:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("January 1, 1970 at 7:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 29, 11, 1, 0, 1970, 4, 0, { (10 * 60 * 60) + (29 * 60), (1 * 60 * 60) } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("January 1, 1970, 11:29:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("January 1, 1970 at 11:29:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 37, 23, 31, 11, 1969, 3, 364, { -(23 * 60), 0 } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("December 31, 1969, 11:37:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("December 31, 1969 at 11:37:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 0, 17, 31, 11, 1969, 3, 364, { -(7 * 60 * 60), 0 } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("December 31, 1969, 5:00:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("December 31, 1969 at 5:00:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get()); prExplodedTime = { 0, 0, 47, 14, 31, 11, 1969, 3, 364, { -((10 * 60 * 60) + (13 * 60)), (1 * 60 * 60) } }; rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("December 31, 1969, 2:47:00 PM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("December 31, 1969 at 2:47:00 PM", NS_ConvertUTF16toUTF8(formattedTime).get()); } TEST(DateTimeFormat, DateFormatSelectors) { @@ -94,11 +68,11 @@ TEST(DateTimeFormat, DateFormatSelectors) { rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatNoSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("01/1970, 12:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("01/1970, 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("01/1970, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("01/1970, 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatNone, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); @@ -106,11 +80,11 @@ TEST(DateTimeFormat, DateFormatSelectors) { rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatNoSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("Thu, 12:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("Thu 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatWeekday, kTimeFormatSeconds, &prExplodedTime, formattedTime); ASSERT_TRUE(NS_SUCCEEDED(rv)); - ASSERT_STREQ("Thu, 12:00:00 AM", nt(NS_ConvertUTF16toUTF8(formattedTime)).get()); + ASSERT_STREQ("Thu 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get()); } }