/* 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-EG", 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-EG", 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)
{
using namespace std::literals;
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();
const char* Dec22_1581;
const char* Jan01_1582;
const char* Jan01_1583;
if (locale ==
"en-US-u-ca-iso8601"sv) {
Dec22_1581 =
"1581 December 22";
Jan01_1582 =
"1582 January 1";
Jan01_1583 =
"1583 January 1";
}
else {
Dec22_1581 =
"December 22, 1581";
Jan01_1582 =
"January 1, 1582";
Jan01_1583 =
"January 1, 1583";
}
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(Dec22_1581));
// After default Gregorian change date, so January 1, 1583.
dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap();
ASSERT_TRUE(buffer.verboseMatches(Jan01_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(Jan01_1582));
// Still January 1, 1583.
dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap();
ASSERT_TRUE(buffer.verboseMatches(Jan01_1583));
}
}
TEST(IntlDateTimeFormat, GetTimeSeparator)
{
struct TestData {
const char* locale;
const char* numberingSystem;
const char16_t* expected;
} testData[] = {
{
"root",
"latn", u
":"},
{
"root",
"arab", u
":"},
{
"root",
"thai", u
":"},
{
"root",
"arabext", u
"٫"},
// English uses the same data as the root locale.
{
"en",
"latn", u
":"},
{
"en",
"arab", u
":"},
{
"en",
"thai", u
":"},
{
"en",
"arabext", u
"٫"},
// Spanish uses the same data as the root locale.
{
"es",
"latn", u
":"},
{
"es",
"arab", u
":"},
{
"es",
"thai", u
":"},
{
"es",
"arabext", u
"٫"},
// German (Austria) uses the same data as the root locale.
{
"de-AT",
"latn", u
":"},
{
"de-AT",
"arab", u
":"},
{
"de-AT",
"thai", u
":"},
{
"de-AT",
"arabext", u
"٫"},
// Danish has a different time separator for "latn".
{
"da",
"latn", u
"."},
{
"da",
"arab", u
":"},
{
"da",
"thai", u
"."},
{
"da",
"arabext", u
"٫"},
// Same time separator as Danish.
{
"en-DK",
"latn", u
"."},
{
"en-DK",
"arab", u
":"},
{
"en-DK",
"thai", u
"."},
{
"en-DK",
"arabext", u
"٫"},
// Norwegian overrides time separators for "arab" and "arabext".
{
"no",
"latn", u
":"},
{
"no",
"arab", u
"."},
{
"no",
"thai", u
":"},
{
"no",
"arabext", u
"."},
// Parent locale of Bokmål is Norwegian.
{
"nb",
"latn", u
":"},
{
"nb",
"arab", u
"."},
{
"nb",
"thai", u
":"},
{
"nb",
"arabext", u
"."},
// Farsi overrides the time separator for "arabext".
{
"fa",
"latn", u
":"},
{
"fa",
"arab", u
":"},
{
"fa",
"thai", u
":"},
{
"fa",
"arabext", u
":"},
};
for (
const auto& data : testData) {
TestBuffer<char16_t> timeSeparator;
auto timeSeparatorResult = DateTimeFormat::GetTimeSeparator(
MakeStringSpan(data.locale), MakeStringSpan(data.numberingSystem),
timeSeparator);
ASSERT_TRUE(timeSeparatorResult.isOk());
ASSERT_TRUE(timeSeparator.verboseMatches(data.expected));
}
}
}
// namespace mozilla::intl