diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /intl/components/gtest/TestDateTimeFormat.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/components/gtest/TestDateTimeFormat.cpp')
-rw-r--r-- | intl/components/gtest/TestDateTimeFormat.cpp | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/intl/components/gtest/TestDateTimeFormat.cpp b/intl/components/gtest/TestDateTimeFormat.cpp new file mode 100644 index 0000000000..057e26ea13 --- /dev/null +++ b/intl/components/gtest/TestDateTimeFormat.cpp @@ -0,0 +1,621 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gtest/gtest.h" + +#include "mozilla/intl/Calendar.h" +#include "mozilla/intl/DateTimeFormat.h" +#include "mozilla/intl/DateTimePart.h" +#include "mozilla/intl/DateTimePatternGenerator.h" +#include "mozilla/Span.h" +#include "TestBuffer.h" + +#include <string_view> + +namespace mozilla::intl { + +// Firefox 1.0 release date. +const double DATE = 1032800850000.0; + +static UniquePtr<DateTimeFormat> testStyle( + const char* aLocale, DateTimeFormat::StyleBag& aStyleBag) { + // Always specify a time zone in the tests, otherwise it will use the system + // time zone which can vary between test runs. + auto timeZone = Some(MakeStringSpan(u"GMT+3")); + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + return DateTimeFormat::TryCreateFromStyle(MakeStringSpan(aLocale), aStyleBag, + gen.get(), timeZone) + .unwrap(); +} + +TEST(IntlDateTimeFormat, Style_enUS_utf8) +{ + DateTimeFormat::StyleBag style; + style.date = Some(DateTimeFormat::Style::Medium); + style.time = Some(DateTimeFormat::Style::Medium); + + auto dtFormat = testStyle("en-US", style); + TestBuffer<char> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + + ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 8:07:30 PM")); +} + +TEST(IntlDateTimeFormat, Style_enUS_utf16) +{ + DateTimeFormat::StyleBag style; + style.date = Some(DateTimeFormat::Style::Medium); + style.time = Some(DateTimeFormat::Style::Medium); + + auto dtFormat = testStyle("en-US", style); + TestBuffer<char16_t> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + + ASSERT_TRUE(buffer.verboseMatches(u"Sep 23, 2002, 8:07:30 PM")); +} + +TEST(IntlDateTimeFormat, Style_ar_utf8) +{ + DateTimeFormat::StyleBag style; + style.time = Some(DateTimeFormat::Style::Medium); + + auto dtFormat = testStyle("ar", style); + TestBuffer<char> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + + ASSERT_TRUE(buffer.verboseMatches("٨:٠٧:٣٠ م")); +} + +TEST(IntlDateTimeFormat, Style_ar_utf16) +{ + DateTimeFormat::StyleBag style; + style.time = Some(DateTimeFormat::Style::Medium); + + auto dtFormat = testStyle("ar", style); + TestBuffer<char16_t> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + + ASSERT_TRUE(buffer.verboseMatches(u"٨:٠٧:٣٠ م")); +} + +TEST(IntlDateTimeFormat, Style_enUS_fallback_to_default_styles) +{ + DateTimeFormat::StyleBag style; + + auto dtFormat = testStyle("en-US", style); + TestBuffer<char> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + + ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 8:07:30 PM")); +} + +TEST(IntlDateTimeFormat, Time_zone_IANA_identifier) +{ + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + + DateTimeFormat::StyleBag style; + style.date = Some(DateTimeFormat::Style::Medium); + style.time = Some(DateTimeFormat::Style::Medium); + + auto dtFormat = DateTimeFormat::TryCreateFromStyle( + MakeStringSpan("en-US"), style, gen.get(), + Some(MakeStringSpan(u"America/Chicago"))) + .unwrap(); + TestBuffer<char> buffer; + dtFormat->TryFormat(DATE, buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 12:07:30 PM")); +} + +TEST(IntlDateTimeFormat, GetAllowedHourCycles) +{ + auto allowed_en_US = DateTimeFormat::GetAllowedHourCycles( + MakeStringSpan("en"), Some(MakeStringSpan("US"))) + .unwrap(); + + ASSERT_TRUE(allowed_en_US.length() == 2); + ASSERT_EQ(allowed_en_US[0], DateTimeFormat::HourCycle::H12); + ASSERT_EQ(allowed_en_US[1], DateTimeFormat::HourCycle::H23); + + auto allowed_de = + DateTimeFormat::GetAllowedHourCycles(MakeStringSpan("de"), Nothing()) + .unwrap(); + + ASSERT_TRUE(allowed_de.length() == 2); + ASSERT_EQ(allowed_de[0], DateTimeFormat::HourCycle::H23); + ASSERT_EQ(allowed_de[1], DateTimeFormat::HourCycle::H12); +} + +TEST(IntlDateTimePatternGenerator, GetBestPattern) +{ + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + TestBuffer<char16_t> buffer; + + gen->GetBestPattern(MakeStringSpan(u"yMd"), buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches(u"M/d/y")); +} + +TEST(IntlDateTimePatternGenerator, GetSkeleton) +{ + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + TestBuffer<char16_t> buffer; + + DateTimePatternGenerator::GetSkeleton(MakeStringSpan(u"M/d/y"), buffer) + .unwrap(); + ASSERT_TRUE(buffer.verboseMatches(u"yMd")); +} + +TEST(IntlDateTimePatternGenerator, GetPlaceholderPattern) +{ + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + auto span = gen->GetPlaceholderPattern(); + // The default date-time pattern for 'en' locale is u"{1}, {0}". + ASSERT_EQ(span, MakeStringSpan(u"{1}, {0}")); +} + +// A utility function to help test the DateTimeFormat::ComponentsBag. +[[nodiscard]] bool FormatComponents( + TestBuffer<char16_t>& aBuffer, DateTimeFormat::ComponentsBag& aComponents, + Span<const char> aLocale = MakeStringSpan("en-US")) { + UniquePtr<DateTimePatternGenerator> gen = nullptr; + auto dateTimePatternGenerator = + DateTimePatternGenerator::TryCreate(aLocale.data()).unwrap(); + + auto dtFormat = DateTimeFormat::TryCreateFromComponents( + aLocale, aComponents, dateTimePatternGenerator.get(), + Some(MakeStringSpan(u"GMT+3"))); + if (dtFormat.isErr()) { + fprintf(stderr, "Could not create a DateTimeFormat\n"); + return false; + } + + auto result = dtFormat.unwrap()->TryFormat(DATE, aBuffer); + if (result.isErr()) { + fprintf(stderr, "Could not format a DateTimeFormat\n"); + return false; + } + + return true; +} + +TEST(IntlDateTimeFormat, Components) +{ + DateTimeFormat::ComponentsBag components{}; + + components.year = Some(DateTimeFormat::Numeric::Numeric); + components.month = Some(DateTimeFormat::Month::Numeric); + components.day = Some(DateTimeFormat::Numeric::Numeric); + + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::TwoDigit); + components.second = Some(DateTimeFormat::Numeric::TwoDigit); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components)); + ASSERT_TRUE(buffer.verboseMatches(u"9/23/2002, 8:07:30 PM")); +} + +TEST(IntlDateTimeFormat, Components_es_ES) +{ + DateTimeFormat::ComponentsBag components{}; + + components.year = Some(DateTimeFormat::Numeric::Numeric); + components.month = Some(DateTimeFormat::Month::Numeric); + components.day = Some(DateTimeFormat::Numeric::Numeric); + + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::TwoDigit); + components.second = Some(DateTimeFormat::Numeric::TwoDigit); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components, MakeStringSpan("es-ES"))); + ASSERT_TRUE(buffer.verboseMatches(u"23/9/2002, 20:07:30")); +} + +TEST(IntlDateTimeFormat, ComponentsAll) +{ + // Use most all of the components. + DateTimeFormat::ComponentsBag components{}; + + components.era = Some(DateTimeFormat::Text::Short); + + components.year = Some(DateTimeFormat::Numeric::Numeric); + components.month = Some(DateTimeFormat::Month::Numeric); + components.day = Some(DateTimeFormat::Numeric::Numeric); + + components.weekday = Some(DateTimeFormat::Text::Short); + + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::TwoDigit); + components.second = Some(DateTimeFormat::Numeric::TwoDigit); + + components.timeZoneName = Some(DateTimeFormat::TimeZoneName::Short); + components.hourCycle = Some(DateTimeFormat::HourCycle::H24); + components.fractionalSecondDigits = Some(3); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components)); + ASSERT_TRUE(buffer.verboseMatches(u"Mon, 9 23, 2002 AD, 20:07:30.000 GMT+3")); +} + +TEST(IntlDateTimeFormat, ComponentsHour12Default) +{ + // Assert the behavior of the default "en-US" 12 hour time with day period. + DateTimeFormat::ComponentsBag components{}; + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::Numeric); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components)); + ASSERT_TRUE(buffer.verboseMatches(u"8:07 PM")); +} + +TEST(IntlDateTimeFormat, ComponentsHour24) +{ + // Test the behavior of using 24 hour time to override the default of + // hour 12 with a day period. + DateTimeFormat::ComponentsBag components{}; + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::Numeric); + components.hour12 = Some(false); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components)); + ASSERT_TRUE(buffer.verboseMatches(u"20:07")); +} + +TEST(IntlDateTimeFormat, ComponentsHour12DayPeriod) +{ + // Test the behavior of specifying a specific day period. + DateTimeFormat::ComponentsBag components{}; + + components.hour = Some(DateTimeFormat::Numeric::Numeric); + components.minute = Some(DateTimeFormat::Numeric::Numeric); + components.dayPeriod = Some(DateTimeFormat::Text::Long); + + TestBuffer<char16_t> buffer; + ASSERT_TRUE(FormatComponents(buffer, components)); + ASSERT_TRUE(buffer.verboseMatches(u"8:07 in the evening")); +} + +const char* ToString(uint8_t b) { return "uint8_t"; } +const char* ToString(bool b) { return b ? "true" : "false"; } + +template <typename T> +const char* ToString(Maybe<T> option) { + if (option) { + if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t>) { + return ToString(*option); + } else { + return DateTimeFormat::ToString(*option); + } + } + return "Nothing"; +} + +template <typename T> +[[nodiscard]] bool VerboseEquals(T expected, T actual, const char* msg) { + if (expected != actual) { + fprintf(stderr, "%s\n Actual: %s\nExpected: %s\n", msg, ToString(actual), + ToString(expected)); + return false; + } + return true; +} + +// A testing utility for getting nice errors when ComponentsBags don't match. +[[nodiscard]] bool VerboseEquals(DateTimeFormat::ComponentsBag& expected, + DateTimeFormat::ComponentsBag& actual) { + // clang-format off + return + VerboseEquals(expected.era, actual.era, "Components do not match: bag.era") && + VerboseEquals(expected.year, actual.year, "Components do not match: bag.year") && + VerboseEquals(expected.month, actual.month, "Components do not match: bag.month") && + VerboseEquals(expected.day, actual.day, "Components do not match: bag.day") && + VerboseEquals(expected.weekday, actual.weekday, "Components do not match: bag.weekday") && + VerboseEquals(expected.hour, actual.hour, "Components do not match: bag.hour") && + VerboseEquals(expected.minute, actual.minute, "Components do not match: bag.minute") && + VerboseEquals(expected.second, actual.second, "Components do not match: bag.second") && + VerboseEquals(expected.timeZoneName, actual.timeZoneName, "Components do not match: bag.timeZoneName") && + VerboseEquals(expected.hour12, actual.hour12, "Components do not match: bag.hour12") && + VerboseEquals(expected.hourCycle, actual.hourCycle, "Components do not match: bag.hourCycle") && + VerboseEquals(expected.dayPeriod, actual.dayPeriod, "Components do not match: bag.dayPeriod") && + VerboseEquals(expected.fractionalSecondDigits, actual.fractionalSecondDigits, "Components do not match: bag.fractionalSecondDigits"); + // clang-format on +} + +// A utility function to help test the DateTimeFormat::ComponentsBag. +[[nodiscard]] bool ResolveComponentsBag( + DateTimeFormat::ComponentsBag& aComponentsIn, + DateTimeFormat::ComponentsBag* aComponentsOut, + Span<const char> aLocale = MakeStringSpan("en-US")) { + UniquePtr<DateTimePatternGenerator> gen = nullptr; + auto dateTimePatternGenerator = + DateTimePatternGenerator::TryCreate("en").unwrap(); + auto dtFormat = DateTimeFormat::TryCreateFromComponents( + aLocale, aComponentsIn, dateTimePatternGenerator.get(), + Some(MakeStringSpan(u"GMT+3"))); + if (dtFormat.isErr()) { + fprintf(stderr, "Could not create a DateTimeFormat\n"); + return false; + } + + auto result = dtFormat.unwrap()->ResolveComponents(); + if (result.isErr()) { + fprintf(stderr, "Could not resolve the components\n"); + return false; + } + + *aComponentsOut = result.unwrap(); + return true; +} + +TEST(IntlDateTimeFormat, ResolvedComponentsDate) +{ + DateTimeFormat::ComponentsBag input{}; + { + input.year = Some(DateTimeFormat::Numeric::Numeric); + input.month = Some(DateTimeFormat::Month::Numeric); + input.day = Some(DateTimeFormat::Numeric::Numeric); + } + + DateTimeFormat::ComponentsBag expected = input; + + DateTimeFormat::ComponentsBag resolved{}; + ASSERT_TRUE(ResolveComponentsBag(input, &resolved)); + ASSERT_TRUE(VerboseEquals(expected, resolved)); +} + +TEST(IntlDateTimeFormat, ResolvedComponentsAll) +{ + DateTimeFormat::ComponentsBag input{}; + { + input.era = Some(DateTimeFormat::Text::Short); + + input.year = Some(DateTimeFormat::Numeric::Numeric); + input.month = Some(DateTimeFormat::Month::Numeric); + input.day = Some(DateTimeFormat::Numeric::Numeric); + + input.weekday = Some(DateTimeFormat::Text::Short); + + input.hour = Some(DateTimeFormat::Numeric::Numeric); + input.minute = Some(DateTimeFormat::Numeric::TwoDigit); + input.second = Some(DateTimeFormat::Numeric::TwoDigit); + + input.timeZoneName = Some(DateTimeFormat::TimeZoneName::Short); + input.hourCycle = Some(DateTimeFormat::HourCycle::H24); + input.fractionalSecondDigits = Some(3); + } + + DateTimeFormat::ComponentsBag expected = input; + { + expected.hour = Some(DateTimeFormat::Numeric::TwoDigit); + expected.hourCycle = Some(DateTimeFormat::HourCycle::H24); + expected.hour12 = Some(false); + } + + DateTimeFormat::ComponentsBag resolved{}; + ASSERT_TRUE(ResolveComponentsBag(input, &resolved)); + ASSERT_TRUE(VerboseEquals(expected, resolved)); +} + +TEST(IntlDateTimeFormat, ResolvedComponentsHourDayPeriod) +{ + DateTimeFormat::ComponentsBag input{}; + { + input.hour = Some(DateTimeFormat::Numeric::Numeric); + input.minute = Some(DateTimeFormat::Numeric::Numeric); + } + + DateTimeFormat::ComponentsBag expected = input; + { + expected.minute = Some(DateTimeFormat::Numeric::TwoDigit); + expected.hourCycle = Some(DateTimeFormat::HourCycle::H12); + expected.hour12 = Some(true); + } + + DateTimeFormat::ComponentsBag resolved{}; + ASSERT_TRUE(ResolveComponentsBag(input, &resolved)); + ASSERT_TRUE(VerboseEquals(expected, resolved)); +} + +TEST(IntlDateTimeFormat, ResolvedComponentsHour12) +{ + DateTimeFormat::ComponentsBag input{}; + { + input.hour = Some(DateTimeFormat::Numeric::Numeric); + input.minute = Some(DateTimeFormat::Numeric::Numeric); + input.hour12 = Some(false); + } + + DateTimeFormat::ComponentsBag expected = input; + { + expected.hour = Some(DateTimeFormat::Numeric::TwoDigit); + expected.minute = Some(DateTimeFormat::Numeric::TwoDigit); + expected.hourCycle = Some(DateTimeFormat::HourCycle::H23); + expected.hour12 = Some(false); + } + + DateTimeFormat::ComponentsBag resolved{}; + ASSERT_TRUE(ResolveComponentsBag(input, &resolved)); + ASSERT_TRUE(VerboseEquals(expected, resolved)); +} + +TEST(IntlDateTimeFormat, GetOriginalSkeleton) +{ + // Demonstrate that the original skeleton and the resolved skeleton can + // differ. + DateTimeFormat::ComponentsBag components{}; + components.month = Some(DateTimeFormat::Month::Narrow); + components.day = Some(DateTimeFormat::Numeric::TwoDigit); + + const char* locale = "zh-Hans-CN"; + auto dateTimePatternGenerator = + DateTimePatternGenerator::TryCreate(locale).unwrap(); + + auto result = DateTimeFormat::TryCreateFromComponents( + MakeStringSpan(locale), components, dateTimePatternGenerator.get(), + Some(MakeStringSpan(u"GMT+3"))); + ASSERT_TRUE(result.isOk()); + auto dtFormat = result.unwrap(); + + TestBuffer<char16_t> originalSkeleton; + auto originalSkeletonResult = dtFormat->GetOriginalSkeleton(originalSkeleton); + ASSERT_TRUE(originalSkeletonResult.isOk()); + ASSERT_TRUE(originalSkeleton.verboseMatches(u"MMMMMdd")); + + TestBuffer<char16_t> pattern; + auto patternResult = dtFormat->GetPattern(pattern); + ASSERT_TRUE(patternResult.isOk()); + ASSERT_TRUE(pattern.verboseMatches(u"M月dd日")); + + TestBuffer<char16_t> resolvedSkeleton; + auto resolvedSkeletonResult = DateTimePatternGenerator::GetSkeleton( + Span(pattern.data(), pattern.length()), resolvedSkeleton); + + ASSERT_TRUE(resolvedSkeletonResult.isOk()); + ASSERT_TRUE(resolvedSkeleton.verboseMatches(u"Mdd")); +} + +TEST(IntlDateTimeFormat, GetAvailableLocales) +{ + using namespace std::literals; + + int32_t english = 0; + int32_t german = 0; + int32_t chinese = 0; + + // Since this list is dependent on ICU, and may change between upgrades, only + // test a subset of the available locales. + for (const char* locale : DateTimeFormat::GetAvailableLocales()) { + if (locale == "en"sv) { + english++; + } else if (locale == "de"sv) { + german++; + } else if (locale == "zh"sv) { + chinese++; + } + } + + // Each locale should be found exactly once. + ASSERT_EQ(english, 1); + ASSERT_EQ(german, 1); + ASSERT_EQ(chinese, 1); +} + +TEST(IntlDateTimeFormat, TryFormatToParts) +{ + auto dateTimePatternGenerator = + DateTimePatternGenerator::TryCreate("en").unwrap(); + + DateTimeFormat::ComponentsBag components; + components.year = Some(DateTimeFormat::Numeric::Numeric); + components.month = Some(DateTimeFormat::Month::TwoDigit); + components.day = Some(DateTimeFormat::Numeric::TwoDigit); + components.hour = Some(DateTimeFormat::Numeric::TwoDigit); + components.minute = Some(DateTimeFormat::Numeric::TwoDigit); + components.hour12 = Some(false); + + UniquePtr<DateTimeFormat> dtFormat = + DateTimeFormat::TryCreateFromComponents( + MakeStringSpan("en-US"), components, dateTimePatternGenerator.get(), + Some(MakeStringSpan(u"GMT"))) + .unwrap(); + + TestBuffer<char16_t> buffer; + mozilla::intl::DateTimePartVector parts; + auto result = dtFormat->TryFormatToParts(DATE, buffer, parts); + ASSERT_TRUE(result.isOk()); + + std::u16string_view strView = buffer.get_string_view(); + ASSERT_EQ(strView, u"09/23/2002, 17:07"); + + auto getSubStringView = [strView, &parts](size_t index) { + size_t pos = index == 0 ? 0 : parts[index - 1].mEndIndex; + size_t count = parts[index].mEndIndex - pos; + return strView.substr(pos, count); + }; + + ASSERT_EQ(parts[0].mType, DateTimePartType::Month); + ASSERT_EQ(getSubStringView(0), u"09"); + + ASSERT_EQ(parts[1].mType, DateTimePartType::Literal); + ASSERT_EQ(getSubStringView(1), u"/"); + + ASSERT_EQ(parts[2].mType, DateTimePartType::Day); + ASSERT_EQ(getSubStringView(2), u"23"); + + ASSERT_EQ(parts[3].mType, DateTimePartType::Literal); + ASSERT_EQ(getSubStringView(3), u"/"); + + ASSERT_EQ(parts[4].mType, DateTimePartType::Year); + ASSERT_EQ(getSubStringView(4), u"2002"); + + ASSERT_EQ(parts[5].mType, DateTimePartType::Literal); + ASSERT_EQ(getSubStringView(5), u", "); + + ASSERT_EQ(parts[6].mType, DateTimePartType::Hour); + ASSERT_EQ(getSubStringView(6), u"17"); + + ASSERT_EQ(parts[7].mType, DateTimePartType::Literal); + ASSERT_EQ(getSubStringView(7), u":"); + + ASSERT_EQ(parts[8].mType, DateTimePartType::Minute); + ASSERT_EQ(getSubStringView(8), u"07"); + + ASSERT_EQ(parts.length(), 9u); +} + +TEST(IntlDateTimeFormat, SetStartTimeIfGregorian) +{ + DateTimeFormat::StyleBag style{}; + style.date = Some(DateTimeFormat::Style::Long); + + auto timeZone = Some(MakeStringSpan(u"UTC")); + + // Beginning of ECMAScript time. + constexpr double StartOfTime = -8.64e15; + + // Gregorian change date defaults to October 15, 1582 in ICU. Test with a date + // before the default change date, in this case January 1, 1582. + constexpr double FirstJanuary1582 = -12244089600000.0; + + // One year expressed in milliseconds. + constexpr double oneYear = (365 * 24 * 60 * 60) * 1000.0; + + // Test with and without explicit calendar. The start time of the calendar can + // only be adjusted for the Gregorian and the ISO-8601 calendar. + for (const char* locale : { + "en-US", + "en-US-u-ca-gregory", + "en-US-u-ca-iso8601", + }) { + auto gen = DateTimePatternGenerator::TryCreate(locale).unwrap(); + + auto dtFormat = DateTimeFormat::TryCreateFromStyle( + MakeStringSpan(locale), style, gen.get(), timeZone) + .unwrap(); + + TestBuffer<char> buffer; + + // Before the default Gregorian change date, so interpreted in the Julian + // calendar, which is December 22, 1581. + dtFormat->TryFormat(FirstJanuary1582, buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches("December 22, 1581")); + + // After default Gregorian change date, so January 1, 1583. + dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches("January 1, 1583")); + + // Adjust the start time to use a proleptic Gregorian calendar. + dtFormat->SetStartTimeIfGregorian(StartOfTime); + + // Now interpreted in proleptic Gregorian calendar at January 1, 1582. + dtFormat->TryFormat(FirstJanuary1582, buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches("January 1, 1582")); + + // Still January 1, 1583. + dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap(); + ASSERT_TRUE(buffer.verboseMatches("January 1, 1583")); + } +} +} // namespace mozilla::intl |