1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "gtest/gtest.h"
5
6 #include "mozilla/intl/Calendar.h"
7 #include "mozilla/intl/DateTimeFormat.h"
8 #include "mozilla/intl/DateTimePart.h"
9 #include "mozilla/intl/DateTimePatternGenerator.h"
10 #include "mozilla/Span.h"
11 #include "TestBuffer.h"
12
13 #include <string_view>
14
15 namespace mozilla::intl {
16
17 // Firefox 1.0 release date.
18 const double DATE = 1032800850000.0;
19
testStyle(const char * aLocale,DateTimeFormat::StyleBag & aStyleBag)20 static UniquePtr<DateTimeFormat> testStyle(
21 const char* aLocale, DateTimeFormat::StyleBag& aStyleBag) {
22 // Always specify a time zone in the tests, otherwise it will use the system
23 // time zone which can vary between test runs.
24 auto timeZone = Some(MakeStringSpan(u"GMT+3"));
25 auto gen = DateTimePatternGenerator::TryCreate("en").unwrap();
26 return DateTimeFormat::TryCreateFromStyle(MakeStringSpan(aLocale), aStyleBag,
27 gen.get(), timeZone)
28 .unwrap();
29 }
30
TEST(IntlDateTimeFormat,Style_enUS_utf8)31 TEST(IntlDateTimeFormat, Style_enUS_utf8)
32 {
33 DateTimeFormat::StyleBag style;
34 style.date = Some(DateTimeFormat::Style::Medium);
35 style.time = Some(DateTimeFormat::Style::Medium);
36
37 auto dtFormat = testStyle("en-US", style);
38 TestBuffer<char> buffer;
39 dtFormat->TryFormat(DATE, buffer).unwrap();
40
41 ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 8:07:30 PM"));
42 }
43
TEST(IntlDateTimeFormat,Style_enUS_utf16)44 TEST(IntlDateTimeFormat, Style_enUS_utf16)
45 {
46 DateTimeFormat::StyleBag style;
47 style.date = Some(DateTimeFormat::Style::Medium);
48 style.time = Some(DateTimeFormat::Style::Medium);
49
50 auto dtFormat = testStyle("en-US", style);
51 TestBuffer<char16_t> buffer;
52 dtFormat->TryFormat(DATE, buffer).unwrap();
53
54 ASSERT_TRUE(buffer.verboseMatches(u"Sep 23, 2002, 8:07:30 PM"));
55 }
56
TEST(IntlDateTimeFormat,Style_ar_utf8)57 TEST(IntlDateTimeFormat, Style_ar_utf8)
58 {
59 DateTimeFormat::StyleBag style;
60 style.time = Some(DateTimeFormat::Style::Medium);
61
62 auto dtFormat = testStyle("ar", style);
63 TestBuffer<char> buffer;
64 dtFormat->TryFormat(DATE, buffer).unwrap();
65
66 ASSERT_TRUE(buffer.verboseMatches("٨:٠٧:٣٠ م"));
67 }
68
TEST(IntlDateTimeFormat,Style_ar_utf16)69 TEST(IntlDateTimeFormat, Style_ar_utf16)
70 {
71 DateTimeFormat::StyleBag style;
72 style.time = Some(DateTimeFormat::Style::Medium);
73
74 auto dtFormat = testStyle("ar", style);
75 TestBuffer<char16_t> buffer;
76 dtFormat->TryFormat(DATE, buffer).unwrap();
77
78 ASSERT_TRUE(buffer.verboseMatches(u"٨:٠٧:٣٠ م"));
79 }
80
TEST(IntlDateTimeFormat,Style_enUS_fallback_to_default_styles)81 TEST(IntlDateTimeFormat, Style_enUS_fallback_to_default_styles)
82 {
83 DateTimeFormat::StyleBag style;
84
85 auto dtFormat = testStyle("en-US", style);
86 TestBuffer<char> buffer;
87 dtFormat->TryFormat(DATE, buffer).unwrap();
88
89 ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 8:07:30 PM"));
90 }
91
TEST(IntlDateTimeFormat,Skeleton_enUS_utf8_in)92 TEST(IntlDateTimeFormat, Skeleton_enUS_utf8_in)
93 {
94 UniquePtr<DateTimePatternGenerator> gen = nullptr;
95 auto dateTimePatternGenerator =
96 DateTimePatternGenerator::TryCreate("en").unwrap();
97
98 UniquePtr<DateTimeFormat> dtFormat =
99 DateTimeFormat::TryCreateFromSkeleton(
100 MakeStringSpan("en-US"), MakeStringSpan("yMdhhmmss"),
101 dateTimePatternGenerator.get(), Nothing(),
102 Some(MakeStringSpan("GMT+3")))
103 .unwrap();
104 TestBuffer<char> buffer;
105 dtFormat->TryFormat(DATE, buffer).unwrap();
106
107 ASSERT_TRUE(buffer.verboseMatches("9/23/2002, 08:07:30 PM"));
108 }
109
TEST(IntlDateTimeFormat,Skeleton_enUS_utf16_in)110 TEST(IntlDateTimeFormat, Skeleton_enUS_utf16_in)
111 {
112 UniquePtr<DateTimePatternGenerator> gen = nullptr;
113 auto dateTimePatternGenerator =
114 DateTimePatternGenerator::TryCreate("en").unwrap();
115
116 UniquePtr<DateTimeFormat> dtFormat =
117 DateTimeFormat::TryCreateFromSkeleton(
118 MakeStringSpan("en-US"), MakeStringSpan(u"yMdhhmmss"),
119 dateTimePatternGenerator.get(), Nothing(),
120 Some(MakeStringSpan(u"GMT+3")))
121 .unwrap();
122 TestBuffer<char> buffer;
123 dtFormat->TryFormat(DATE, buffer).unwrap();
124
125 ASSERT_TRUE(buffer.verboseMatches("9/23/2002, 08:07:30 PM"));
126 }
127
TEST(IntlDateTimeFormat,Time_zone_IANA_identifier)128 TEST(IntlDateTimeFormat, Time_zone_IANA_identifier)
129 {
130 auto gen = DateTimePatternGenerator::TryCreate("en").unwrap();
131
132 DateTimeFormat::StyleBag style;
133 style.date = Some(DateTimeFormat::Style::Medium);
134 style.time = Some(DateTimeFormat::Style::Medium);
135
136 auto dtFormat = DateTimeFormat::TryCreateFromStyle(
137 MakeStringSpan("en-US"), style, gen.get(),
138 Some(MakeStringSpan(u"America/Chicago")))
139 .unwrap();
140 TestBuffer<char> buffer;
141 dtFormat->TryFormat(DATE, buffer).unwrap();
142 ASSERT_TRUE(buffer.verboseMatches("Sep 23, 2002, 12:07:30 PM"));
143 }
144
TEST(IntlDateTimeFormat,GetAllowedHourCycles)145 TEST(IntlDateTimeFormat, GetAllowedHourCycles)
146 {
147 auto allowed_en_US = DateTimeFormat::GetAllowedHourCycles(
148 MakeStringSpan("en"), Some(MakeStringSpan("US")))
149 .unwrap();
150
151 ASSERT_TRUE(allowed_en_US.length() == 2);
152 ASSERT_EQ(allowed_en_US[0], DateTimeFormat::HourCycle::H12);
153 ASSERT_EQ(allowed_en_US[1], DateTimeFormat::HourCycle::H23);
154
155 auto allowed_de =
156 DateTimeFormat::GetAllowedHourCycles(MakeStringSpan("de"), Nothing())
157 .unwrap();
158
159 ASSERT_TRUE(allowed_de.length() == 2);
160 ASSERT_EQ(allowed_de[0], DateTimeFormat::HourCycle::H23);
161 ASSERT_EQ(allowed_de[1], DateTimeFormat::HourCycle::H12);
162 }
163
TEST(IntlDateTimePatternGenerator,GetBestPattern)164 TEST(IntlDateTimePatternGenerator, GetBestPattern)
165 {
166 auto gen = DateTimePatternGenerator::TryCreate("en").unwrap();
167 TestBuffer<char16_t> buffer;
168
169 gen->GetBestPattern(MakeStringSpan(u"yMd"), buffer).unwrap();
170 ASSERT_TRUE(buffer.verboseMatches(u"M/d/y"));
171 }
172
TEST(IntlDateTimePatternGenerator,GetSkeleton)173 TEST(IntlDateTimePatternGenerator, GetSkeleton)
174 {
175 auto gen = DateTimePatternGenerator::TryCreate("en").unwrap();
176 TestBuffer<char16_t> buffer;
177
178 DateTimePatternGenerator::GetSkeleton(MakeStringSpan(u"M/d/y"), buffer)
179 .unwrap();
180 ASSERT_TRUE(buffer.verboseMatches(u"yMd"));
181 }
182
TEST(IntlDateTimePatternGenerator,GetPlaceholderPattern)183 TEST(IntlDateTimePatternGenerator, GetPlaceholderPattern)
184 {
185 auto gen = DateTimePatternGenerator::TryCreate("en").unwrap();
186 auto span = gen->GetPlaceholderPattern();
187 // The default date-time pattern for 'en' locale is u"{1}, {0}".
188 ASSERT_EQ(span, MakeStringSpan(u"{1}, {0}"));
189 }
190
191 // A utility function to help test the DateTimeFormat::ComponentsBag.
FormatComponents(TestBuffer<char16_t> & aBuffer,DateTimeFormat::ComponentsBag & aComponents,Span<const char> aLocale=MakeStringSpan ("en-US"))192 [[nodiscard]] bool FormatComponents(
193 TestBuffer<char16_t>& aBuffer, DateTimeFormat::ComponentsBag& aComponents,
194 Span<const char> aLocale = MakeStringSpan("en-US")) {
195 UniquePtr<DateTimePatternGenerator> gen = nullptr;
196 auto dateTimePatternGenerator =
197 DateTimePatternGenerator::TryCreate(aLocale.data()).unwrap();
198
199 auto dtFormat = DateTimeFormat::TryCreateFromComponents(
200 aLocale, aComponents, dateTimePatternGenerator.get(),
201 Some(MakeStringSpan(u"GMT+3")));
202 if (dtFormat.isErr()) {
203 fprintf(stderr, "Could not create a DateTimeFormat\n");
204 return false;
205 }
206
207 auto result = dtFormat.unwrap()->TryFormat(DATE, aBuffer);
208 if (result.isErr()) {
209 fprintf(stderr, "Could not format a DateTimeFormat\n");
210 return false;
211 }
212
213 return true;
214 }
215
TEST(IntlDateTimeFormat,Components)216 TEST(IntlDateTimeFormat, Components)
217 {
218 DateTimeFormat::ComponentsBag components{};
219
220 components.year = Some(DateTimeFormat::Numeric::Numeric);
221 components.month = Some(DateTimeFormat::Month::Numeric);
222 components.day = Some(DateTimeFormat::Numeric::Numeric);
223
224 components.hour = Some(DateTimeFormat::Numeric::Numeric);
225 components.minute = Some(DateTimeFormat::Numeric::TwoDigit);
226 components.second = Some(DateTimeFormat::Numeric::TwoDigit);
227
228 TestBuffer<char16_t> buffer;
229 ASSERT_TRUE(FormatComponents(buffer, components));
230 ASSERT_TRUE(buffer.verboseMatches(u"9/23/2002, 8:07:30 PM"));
231 }
232
TEST(IntlDateTimeFormat,Components_es_ES)233 TEST(IntlDateTimeFormat, Components_es_ES)
234 {
235 DateTimeFormat::ComponentsBag components{};
236
237 components.year = Some(DateTimeFormat::Numeric::Numeric);
238 components.month = Some(DateTimeFormat::Month::Numeric);
239 components.day = Some(DateTimeFormat::Numeric::Numeric);
240
241 components.hour = Some(DateTimeFormat::Numeric::Numeric);
242 components.minute = Some(DateTimeFormat::Numeric::TwoDigit);
243 components.second = Some(DateTimeFormat::Numeric::TwoDigit);
244
245 TestBuffer<char16_t> buffer;
246 ASSERT_TRUE(FormatComponents(buffer, components, MakeStringSpan("es-ES")));
247 ASSERT_TRUE(buffer.verboseMatches(u"23/9/2002, 20:07:30"));
248 }
249
TEST(IntlDateTimeFormat,ComponentsAll)250 TEST(IntlDateTimeFormat, ComponentsAll)
251 {
252 // Use most all of the components.
253 DateTimeFormat::ComponentsBag components{};
254
255 components.era = Some(DateTimeFormat::Text::Short);
256
257 components.year = Some(DateTimeFormat::Numeric::Numeric);
258 components.month = Some(DateTimeFormat::Month::Numeric);
259 components.day = Some(DateTimeFormat::Numeric::Numeric);
260
261 components.weekday = Some(DateTimeFormat::Text::Short);
262
263 components.hour = Some(DateTimeFormat::Numeric::Numeric);
264 components.minute = Some(DateTimeFormat::Numeric::TwoDigit);
265 components.second = Some(DateTimeFormat::Numeric::TwoDigit);
266
267 components.timeZoneName = Some(DateTimeFormat::TimeZoneName::Short);
268 components.hourCycle = Some(DateTimeFormat::HourCycle::H24);
269 components.fractionalSecondDigits = Some(3);
270
271 TestBuffer<char16_t> buffer;
272 ASSERT_TRUE(FormatComponents(buffer, components));
273 ASSERT_TRUE(buffer.verboseMatches(u"Mon, 9 23, 2002 AD, 20:07:30.000 GMT+3"));
274 }
275
TEST(IntlDateTimeFormat,ComponentsHour12Default)276 TEST(IntlDateTimeFormat, ComponentsHour12Default)
277 {
278 // Assert the behavior of the default "en-US" 12 hour time with day period.
279 DateTimeFormat::ComponentsBag components{};
280 components.hour = Some(DateTimeFormat::Numeric::Numeric);
281 components.minute = Some(DateTimeFormat::Numeric::Numeric);
282
283 TestBuffer<char16_t> buffer;
284 ASSERT_TRUE(FormatComponents(buffer, components));
285 ASSERT_TRUE(buffer.verboseMatches(u"8:07 PM"));
286 }
287
TEST(IntlDateTimeFormat,ComponentsHour24)288 TEST(IntlDateTimeFormat, ComponentsHour24)
289 {
290 // Test the behavior of using 24 hour time to override the default of
291 // hour 12 with a day period.
292 DateTimeFormat::ComponentsBag components{};
293 components.hour = Some(DateTimeFormat::Numeric::Numeric);
294 components.minute = Some(DateTimeFormat::Numeric::Numeric);
295 components.hour12 = Some(false);
296
297 TestBuffer<char16_t> buffer;
298 ASSERT_TRUE(FormatComponents(buffer, components));
299 ASSERT_TRUE(buffer.verboseMatches(u"20:07"));
300 }
301
TEST(IntlDateTimeFormat,ComponentsHour12DayPeriod)302 TEST(IntlDateTimeFormat, ComponentsHour12DayPeriod)
303 {
304 // Test the behavior of specifying a specific day period.
305 DateTimeFormat::ComponentsBag components{};
306
307 components.hour = Some(DateTimeFormat::Numeric::Numeric);
308 components.minute = Some(DateTimeFormat::Numeric::Numeric);
309 components.dayPeriod = Some(DateTimeFormat::Text::Long);
310
311 TestBuffer<char16_t> buffer;
312 ASSERT_TRUE(FormatComponents(buffer, components));
313 ASSERT_TRUE(buffer.verboseMatches(u"8:07 in the evening"));
314 }
315
ToString(uint8_t b)316 const char* ToString(uint8_t b) { return "uint8_t"; }
ToString(bool b)317 const char* ToString(bool b) { return b ? "true" : "false"; }
318
319 template <typename T>
ToString(Maybe<T> option)320 const char* ToString(Maybe<T> option) {
321 if (option) {
322 if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t>) {
323 return ToString(*option);
324 } else {
325 return DateTimeFormat::ToString(*option);
326 }
327 }
328 return "Nothing";
329 }
330
331 template <typename T>
VerboseEquals(T expected,T actual,const char * msg)332 [[nodiscard]] bool VerboseEquals(T expected, T actual, const char* msg) {
333 if (expected != actual) {
334 fprintf(stderr, "%s\n Actual: %s\nExpected: %s\n", msg, ToString(actual),
335 ToString(expected));
336 return false;
337 }
338 return true;
339 }
340
341 // A testing utility for getting nice errors when ComponentsBags don't match.
VerboseEquals(DateTimeFormat::ComponentsBag & expected,DateTimeFormat::ComponentsBag & actual)342 [[nodiscard]] bool VerboseEquals(DateTimeFormat::ComponentsBag& expected,
343 DateTimeFormat::ComponentsBag& actual) {
344 // clang-format off
345 return
346 VerboseEquals(expected.era, actual.era, "Components do not match: bag.era") &&
347 VerboseEquals(expected.year, actual.year, "Components do not match: bag.year") &&
348 VerboseEquals(expected.month, actual.month, "Components do not match: bag.month") &&
349 VerboseEquals(expected.day, actual.day, "Components do not match: bag.day") &&
350 VerboseEquals(expected.weekday, actual.weekday, "Components do not match: bag.weekday") &&
351 VerboseEquals(expected.hour, actual.hour, "Components do not match: bag.hour") &&
352 VerboseEquals(expected.minute, actual.minute, "Components do not match: bag.minute") &&
353 VerboseEquals(expected.second, actual.second, "Components do not match: bag.second") &&
354 VerboseEquals(expected.timeZoneName, actual.timeZoneName, "Components do not match: bag.timeZoneName") &&
355 VerboseEquals(expected.hour12, actual.hour12, "Components do not match: bag.hour12") &&
356 VerboseEquals(expected.hourCycle, actual.hourCycle, "Components do not match: bag.hourCycle") &&
357 VerboseEquals(expected.dayPeriod, actual.dayPeriod, "Components do not match: bag.dayPeriod") &&
358 VerboseEquals(expected.fractionalSecondDigits, actual.fractionalSecondDigits, "Components do not match: bag.fractionalSecondDigits");
359 // clang-format on
360 }
361
362 // A utility function to help test the DateTimeFormat::ComponentsBag.
ResolveComponentsBag(DateTimeFormat::ComponentsBag & aComponentsIn,DateTimeFormat::ComponentsBag * aComponentsOut,Span<const char> aLocale=MakeStringSpan ("en-US"))363 [[nodiscard]] bool ResolveComponentsBag(
364 DateTimeFormat::ComponentsBag& aComponentsIn,
365 DateTimeFormat::ComponentsBag* aComponentsOut,
366 Span<const char> aLocale = MakeStringSpan("en-US")) {
367 UniquePtr<DateTimePatternGenerator> gen = nullptr;
368 auto dateTimePatternGenerator =
369 DateTimePatternGenerator::TryCreate("en").unwrap();
370 auto dtFormat = DateTimeFormat::TryCreateFromComponents(
371 aLocale, aComponentsIn, dateTimePatternGenerator.get(),
372 Some(MakeStringSpan(u"GMT+3")));
373 if (dtFormat.isErr()) {
374 fprintf(stderr, "Could not create a DateTimeFormat\n");
375 return false;
376 }
377
378 auto result = dtFormat.unwrap()->ResolveComponents();
379 if (result.isErr()) {
380 fprintf(stderr, "Could not resolve the components\n");
381 return false;
382 }
383
384 *aComponentsOut = result.unwrap();
385 return true;
386 }
387
TEST(IntlDateTimeFormat,ResolvedComponentsDate)388 TEST(IntlDateTimeFormat, ResolvedComponentsDate)
389 {
390 DateTimeFormat::ComponentsBag input{};
391 {
392 input.year = Some(DateTimeFormat::Numeric::Numeric);
393 input.month = Some(DateTimeFormat::Month::Numeric);
394 input.day = Some(DateTimeFormat::Numeric::Numeric);
395 }
396
397 DateTimeFormat::ComponentsBag expected = input;
398
399 DateTimeFormat::ComponentsBag resolved{};
400 ASSERT_TRUE(ResolveComponentsBag(input, &resolved));
401 ASSERT_TRUE(VerboseEquals(expected, resolved));
402 }
403
TEST(IntlDateTimeFormat,ResolvedComponentsAll)404 TEST(IntlDateTimeFormat, ResolvedComponentsAll)
405 {
406 DateTimeFormat::ComponentsBag input{};
407 {
408 input.era = Some(DateTimeFormat::Text::Short);
409
410 input.year = Some(DateTimeFormat::Numeric::Numeric);
411 input.month = Some(DateTimeFormat::Month::Numeric);
412 input.day = Some(DateTimeFormat::Numeric::Numeric);
413
414 input.weekday = Some(DateTimeFormat::Text::Short);
415
416 input.hour = Some(DateTimeFormat::Numeric::Numeric);
417 input.minute = Some(DateTimeFormat::Numeric::TwoDigit);
418 input.second = Some(DateTimeFormat::Numeric::TwoDigit);
419
420 input.timeZoneName = Some(DateTimeFormat::TimeZoneName::Short);
421 input.hourCycle = Some(DateTimeFormat::HourCycle::H24);
422 input.fractionalSecondDigits = Some(3);
423 }
424
425 DateTimeFormat::ComponentsBag expected = input;
426 {
427 expected.hour = Some(DateTimeFormat::Numeric::TwoDigit);
428 expected.hourCycle = Some(DateTimeFormat::HourCycle::H24);
429 expected.hour12 = Some(false);
430 }
431
432 DateTimeFormat::ComponentsBag resolved{};
433 ASSERT_TRUE(ResolveComponentsBag(input, &resolved));
434 ASSERT_TRUE(VerboseEquals(expected, resolved));
435 }
436
TEST(IntlDateTimeFormat,ResolvedComponentsHourDayPeriod)437 TEST(IntlDateTimeFormat, ResolvedComponentsHourDayPeriod)
438 {
439 DateTimeFormat::ComponentsBag input{};
440 {
441 input.hour = Some(DateTimeFormat::Numeric::Numeric);
442 input.minute = Some(DateTimeFormat::Numeric::Numeric);
443 }
444
445 DateTimeFormat::ComponentsBag expected = input;
446 {
447 expected.minute = Some(DateTimeFormat::Numeric::TwoDigit);
448 expected.hourCycle = Some(DateTimeFormat::HourCycle::H12);
449 expected.hour12 = Some(true);
450 }
451
452 DateTimeFormat::ComponentsBag resolved{};
453 ASSERT_TRUE(ResolveComponentsBag(input, &resolved));
454 ASSERT_TRUE(VerboseEquals(expected, resolved));
455 }
456
TEST(IntlDateTimeFormat,ResolvedComponentsHour12)457 TEST(IntlDateTimeFormat, ResolvedComponentsHour12)
458 {
459 DateTimeFormat::ComponentsBag input{};
460 {
461 input.hour = Some(DateTimeFormat::Numeric::Numeric);
462 input.minute = Some(DateTimeFormat::Numeric::Numeric);
463 input.hour12 = Some(false);
464 }
465
466 DateTimeFormat::ComponentsBag expected = input;
467 {
468 expected.hour = Some(DateTimeFormat::Numeric::TwoDigit);
469 expected.minute = Some(DateTimeFormat::Numeric::TwoDigit);
470 expected.hourCycle = Some(DateTimeFormat::HourCycle::H23);
471 expected.hour12 = Some(false);
472 }
473
474 DateTimeFormat::ComponentsBag resolved{};
475 ASSERT_TRUE(ResolveComponentsBag(input, &resolved));
476 ASSERT_TRUE(VerboseEquals(expected, resolved));
477 }
478
TEST(IntlDateTimeFormat,GetOriginalSkeleton)479 TEST(IntlDateTimeFormat, GetOriginalSkeleton)
480 {
481 // Demonstrate that the original skeleton and the resolved skeleton can
482 // differ.
483 DateTimeFormat::ComponentsBag components{};
484 components.month = Some(DateTimeFormat::Month::Narrow);
485 components.day = Some(DateTimeFormat::Numeric::TwoDigit);
486
487 const char* locale = "zh-Hans-CN";
488 auto dateTimePatternGenerator =
489 DateTimePatternGenerator::TryCreate(locale).unwrap();
490
491 auto result = DateTimeFormat::TryCreateFromComponents(
492 MakeStringSpan(locale), components, dateTimePatternGenerator.get(),
493 Some(MakeStringSpan(u"GMT+3")));
494 ASSERT_TRUE(result.isOk());
495 auto dtFormat = result.unwrap();
496
497 TestBuffer<char16_t> originalSkeleton;
498 auto originalSkeletonResult = dtFormat->GetOriginalSkeleton(originalSkeleton);
499 ASSERT_TRUE(originalSkeletonResult.isOk());
500 ASSERT_TRUE(originalSkeleton.verboseMatches(u"MMMMMdd"));
501
502 TestBuffer<char16_t> pattern;
503 auto patternResult = dtFormat->GetPattern(pattern);
504 ASSERT_TRUE(patternResult.isOk());
505 ASSERT_TRUE(pattern.verboseMatches(u"M月dd日"));
506
507 TestBuffer<char16_t> resolvedSkeleton;
508 auto resolvedSkeletonResult = DateTimePatternGenerator::GetSkeleton(
509 Span(pattern.data(), pattern.length()), resolvedSkeleton);
510
511 ASSERT_TRUE(resolvedSkeletonResult.isOk());
512 ASSERT_TRUE(resolvedSkeleton.verboseMatches(u"Mdd"));
513 }
514
TEST(IntlDateTimeFormat,GetAvailableLocales)515 TEST(IntlDateTimeFormat, GetAvailableLocales)
516 {
517 using namespace std::literals;
518
519 int32_t english = 0;
520 int32_t german = 0;
521 int32_t chinese = 0;
522
523 // Since this list is dependent on ICU, and may change between upgrades, only
524 // test a subset of the available locales.
525 for (const char* locale : DateTimeFormat::GetAvailableLocales()) {
526 if (locale == "en"sv) {
527 english++;
528 } else if (locale == "de"sv) {
529 german++;
530 } else if (locale == "zh"sv) {
531 chinese++;
532 }
533 }
534
535 // Each locale should be found exactly once.
536 ASSERT_EQ(english, 1);
537 ASSERT_EQ(german, 1);
538 ASSERT_EQ(chinese, 1);
539 }
540
TEST(IntlDateTimeFormat,TryFormatToParts)541 TEST(IntlDateTimeFormat, TryFormatToParts)
542 {
543 auto dateTimePatternGenerator =
544 DateTimePatternGenerator::TryCreate("en").unwrap();
545
546 UniquePtr<DateTimeFormat> dtFormat =
547 DateTimeFormat::TryCreateFromSkeleton(
548 MakeStringSpan("en-US"), MakeStringSpan(u"yMMddHHmm"),
549 dateTimePatternGenerator.get(), Nothing(),
550 Some(MakeStringSpan(u"GMT")))
551 .unwrap();
552
553 TestBuffer<char16_t> buffer;
554 mozilla::intl::DateTimePartVector parts;
555 auto result = dtFormat->TryFormatToParts(DATE, buffer, parts);
556 ASSERT_TRUE(result.isOk());
557
558 std::u16string_view strView = buffer.get_string_view();
559 ASSERT_EQ(strView, u"09/23/2002, 17:07");
560
561 auto getSubStringView = [strView, &parts](size_t index) {
562 size_t pos = index == 0 ? 0 : parts[index - 1].mEndIndex;
563 size_t count = parts[index].mEndIndex - pos;
564 return strView.substr(pos, count);
565 };
566
567 ASSERT_EQ(parts[0].mType, DateTimePartType::Month);
568 ASSERT_EQ(getSubStringView(0), u"09");
569
570 ASSERT_EQ(parts[1].mType, DateTimePartType::Literal);
571 ASSERT_EQ(getSubStringView(1), u"/");
572
573 ASSERT_EQ(parts[2].mType, DateTimePartType::Day);
574 ASSERT_EQ(getSubStringView(2), u"23");
575
576 ASSERT_EQ(parts[3].mType, DateTimePartType::Literal);
577 ASSERT_EQ(getSubStringView(3), u"/");
578
579 ASSERT_EQ(parts[4].mType, DateTimePartType::Year);
580 ASSERT_EQ(getSubStringView(4), u"2002");
581
582 ASSERT_EQ(parts[5].mType, DateTimePartType::Literal);
583 ASSERT_EQ(getSubStringView(5), u", ");
584
585 ASSERT_EQ(parts[6].mType, DateTimePartType::Hour);
586 ASSERT_EQ(getSubStringView(6), u"17");
587
588 ASSERT_EQ(parts[7].mType, DateTimePartType::Literal);
589 ASSERT_EQ(getSubStringView(7), u":");
590
591 ASSERT_EQ(parts[8].mType, DateTimePartType::Minute);
592 ASSERT_EQ(getSubStringView(8), u"07");
593
594 ASSERT_EQ(parts.length(), 9u);
595 }
596 } // namespace mozilla::intl
597