DateTimeFormat.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. /*
  2. * Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #define AK_DONT_REPLACE_STD
  7. #include <AK/AllOf.h>
  8. #include <AK/Array.h>
  9. #include <AK/GenericLexer.h>
  10. #include <AK/StringBuilder.h>
  11. #include <AK/TypeCasts.h>
  12. #include <LibUnicode/DateTimeFormat.h>
  13. #include <LibUnicode/ICU.h>
  14. #include <LibUnicode/Locale.h>
  15. #include <LibUnicode/NumberFormat.h>
  16. #include <LibUnicode/PartitionRange.h>
  17. #include <stdlib.h>
  18. #include <unicode/calendar.h>
  19. #include <unicode/datefmt.h>
  20. #include <unicode/dtitvfmt.h>
  21. #include <unicode/dtptngen.h>
  22. #include <unicode/gregocal.h>
  23. #include <unicode/smpdtfmt.h>
  24. #include <unicode/timezone.h>
  25. #include <unicode/ucal.h>
  26. namespace Unicode {
  27. DateTimeStyle date_time_style_from_string(StringView style)
  28. {
  29. if (style == "full"sv)
  30. return DateTimeStyle::Full;
  31. if (style == "long"sv)
  32. return DateTimeStyle::Long;
  33. if (style == "medium"sv)
  34. return DateTimeStyle::Medium;
  35. if (style == "short"sv)
  36. return DateTimeStyle::Short;
  37. VERIFY_NOT_REACHED();
  38. }
  39. StringView date_time_style_to_string(DateTimeStyle style)
  40. {
  41. switch (style) {
  42. case DateTimeStyle::Full:
  43. return "full"sv;
  44. case DateTimeStyle::Long:
  45. return "long"sv;
  46. case DateTimeStyle::Medium:
  47. return "medium"sv;
  48. case DateTimeStyle::Short:
  49. return "short"sv;
  50. }
  51. VERIFY_NOT_REACHED();
  52. }
  53. static constexpr icu::DateFormat::EStyle icu_date_time_style(DateTimeStyle style)
  54. {
  55. switch (style) {
  56. case DateTimeStyle::Full:
  57. return icu::DateFormat::EStyle::kFull;
  58. case DateTimeStyle::Long:
  59. return icu::DateFormat::EStyle::kLong;
  60. case DateTimeStyle::Medium:
  61. return icu::DateFormat::EStyle::kMedium;
  62. case DateTimeStyle::Short:
  63. return icu::DateFormat::EStyle::kShort;
  64. }
  65. VERIFY_NOT_REACHED();
  66. }
  67. HourCycle hour_cycle_from_string(StringView hour_cycle)
  68. {
  69. if (hour_cycle == "h11"sv)
  70. return HourCycle::H11;
  71. if (hour_cycle == "h12"sv)
  72. return HourCycle::H12;
  73. if (hour_cycle == "h23"sv)
  74. return HourCycle::H23;
  75. if (hour_cycle == "h24"sv)
  76. return HourCycle::H24;
  77. VERIFY_NOT_REACHED();
  78. }
  79. StringView hour_cycle_to_string(HourCycle hour_cycle)
  80. {
  81. switch (hour_cycle) {
  82. case HourCycle::H11:
  83. return "h11"sv;
  84. case HourCycle::H12:
  85. return "h12"sv;
  86. case HourCycle::H23:
  87. return "h23"sv;
  88. case HourCycle::H24:
  89. return "h24"sv;
  90. }
  91. VERIFY_NOT_REACHED();
  92. }
  93. Optional<HourCycle> default_hour_cycle(StringView locale)
  94. {
  95. UErrorCode status = U_ZERO_ERROR;
  96. auto locale_data = LocaleData::for_locale(locale);
  97. if (!locale_data.has_value())
  98. return {};
  99. auto hour_cycle = locale_data->date_time_pattern_generator().getDefaultHourCycle(status);
  100. if (icu_failure(status))
  101. return {};
  102. switch (hour_cycle) {
  103. case UDAT_HOUR_CYCLE_11:
  104. return HourCycle::H11;
  105. case UDAT_HOUR_CYCLE_12:
  106. return HourCycle::H12;
  107. case UDAT_HOUR_CYCLE_23:
  108. return HourCycle::H23;
  109. case UDAT_HOUR_CYCLE_24:
  110. return HourCycle::H24;
  111. }
  112. VERIFY_NOT_REACHED();
  113. }
  114. static constexpr char icu_hour_cycle(Optional<HourCycle> const& hour_cycle, Optional<bool> const& hour12)
  115. {
  116. if (hour12.has_value())
  117. return *hour12 ? 'h' : 'H';
  118. if (!hour_cycle.has_value())
  119. return 'j';
  120. switch (*hour_cycle) {
  121. case HourCycle::H11:
  122. return 'K';
  123. case HourCycle::H12:
  124. return 'h';
  125. case HourCycle::H23:
  126. return 'H';
  127. case HourCycle::H24:
  128. return 'k';
  129. }
  130. VERIFY_NOT_REACHED();
  131. }
  132. CalendarPatternStyle calendar_pattern_style_from_string(StringView style)
  133. {
  134. if (style == "narrow"sv)
  135. return CalendarPatternStyle::Narrow;
  136. if (style == "short"sv)
  137. return CalendarPatternStyle::Short;
  138. if (style == "long"sv)
  139. return CalendarPatternStyle::Long;
  140. if (style == "numeric"sv)
  141. return CalendarPatternStyle::Numeric;
  142. if (style == "2-digit"sv)
  143. return CalendarPatternStyle::TwoDigit;
  144. if (style == "shortOffset"sv)
  145. return CalendarPatternStyle::ShortOffset;
  146. if (style == "longOffset"sv)
  147. return CalendarPatternStyle::LongOffset;
  148. if (style == "shortGeneric"sv)
  149. return CalendarPatternStyle::ShortGeneric;
  150. if (style == "longGeneric"sv)
  151. return CalendarPatternStyle::LongGeneric;
  152. VERIFY_NOT_REACHED();
  153. }
  154. StringView calendar_pattern_style_to_string(CalendarPatternStyle style)
  155. {
  156. switch (style) {
  157. case CalendarPatternStyle::Narrow:
  158. return "narrow"sv;
  159. case CalendarPatternStyle::Short:
  160. return "short"sv;
  161. case CalendarPatternStyle::Long:
  162. return "long"sv;
  163. case CalendarPatternStyle::Numeric:
  164. return "numeric"sv;
  165. case CalendarPatternStyle::TwoDigit:
  166. return "2-digit"sv;
  167. case CalendarPatternStyle::ShortOffset:
  168. return "shortOffset"sv;
  169. case CalendarPatternStyle::LongOffset:
  170. return "longOffset"sv;
  171. case CalendarPatternStyle::ShortGeneric:
  172. return "shortGeneric"sv;
  173. case CalendarPatternStyle::LongGeneric:
  174. return "longGeneric"sv;
  175. }
  176. VERIFY_NOT_REACHED();
  177. }
  178. // https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  179. String CalendarPattern::to_pattern() const
  180. {
  181. // What we refer to as Narrow, Short, and Long, TR-35 refers to as Narrow, Abbreviated, and Wide.
  182. StringBuilder builder;
  183. if (era.has_value()) {
  184. switch (*era) {
  185. case CalendarPatternStyle::Narrow:
  186. builder.append("GGGGG"sv);
  187. break;
  188. case CalendarPatternStyle::Short:
  189. builder.append("G"sv);
  190. break;
  191. case CalendarPatternStyle::Long:
  192. builder.append("GGGG"sv);
  193. break;
  194. default:
  195. break;
  196. }
  197. }
  198. if (year.has_value()) {
  199. switch (*year) {
  200. case CalendarPatternStyle::Numeric:
  201. builder.append("y"sv);
  202. break;
  203. case CalendarPatternStyle::TwoDigit:
  204. builder.append("yy"sv);
  205. break;
  206. default:
  207. break;
  208. }
  209. }
  210. if (month.has_value()) {
  211. switch (*month) {
  212. case CalendarPatternStyle::Numeric:
  213. builder.append("M"sv);
  214. break;
  215. case CalendarPatternStyle::TwoDigit:
  216. builder.append("MM"sv);
  217. break;
  218. case CalendarPatternStyle::Narrow:
  219. builder.append("MMMMM"sv);
  220. break;
  221. case CalendarPatternStyle::Short:
  222. builder.append("MMM"sv);
  223. break;
  224. case CalendarPatternStyle::Long:
  225. builder.append("MMMM"sv);
  226. break;
  227. default:
  228. break;
  229. }
  230. }
  231. if (weekday.has_value()) {
  232. switch (*weekday) {
  233. case CalendarPatternStyle::Narrow:
  234. builder.append("EEEEE"sv);
  235. break;
  236. case CalendarPatternStyle::Short:
  237. builder.append("E"sv);
  238. break;
  239. case CalendarPatternStyle::Long:
  240. builder.append("EEEE"sv);
  241. break;
  242. default:
  243. break;
  244. }
  245. }
  246. if (day.has_value()) {
  247. switch (*day) {
  248. case CalendarPatternStyle::Numeric:
  249. builder.append("d"sv);
  250. break;
  251. case CalendarPatternStyle::TwoDigit:
  252. builder.append("dd"sv);
  253. break;
  254. default:
  255. break;
  256. }
  257. }
  258. if (day_period.has_value()) {
  259. switch (*day_period) {
  260. case CalendarPatternStyle::Narrow:
  261. builder.append("BBBBB"sv);
  262. break;
  263. case CalendarPatternStyle::Short:
  264. builder.append("B"sv);
  265. break;
  266. case CalendarPatternStyle::Long:
  267. builder.append("BBBB"sv);
  268. break;
  269. default:
  270. break;
  271. }
  272. }
  273. if (hour.has_value()) {
  274. auto hour_cycle_symbol = icu_hour_cycle(hour_cycle, hour12);
  275. switch (*hour) {
  276. case CalendarPatternStyle::Numeric:
  277. builder.append(hour_cycle_symbol);
  278. break;
  279. case CalendarPatternStyle::TwoDigit:
  280. builder.append_repeated(hour_cycle_symbol, 2);
  281. break;
  282. default:
  283. break;
  284. }
  285. }
  286. if (minute.has_value()) {
  287. switch (*minute) {
  288. case CalendarPatternStyle::Numeric:
  289. builder.append("m"sv);
  290. break;
  291. case CalendarPatternStyle::TwoDigit:
  292. builder.append("mm"sv);
  293. break;
  294. default:
  295. break;
  296. }
  297. }
  298. if (second.has_value()) {
  299. switch (*second) {
  300. case CalendarPatternStyle::Numeric:
  301. builder.append("s"sv);
  302. break;
  303. case CalendarPatternStyle::TwoDigit:
  304. builder.append("ss"sv);
  305. break;
  306. default:
  307. break;
  308. }
  309. }
  310. if (fractional_second_digits.has_value()) {
  311. for (u8 i = 0; i < *fractional_second_digits; ++i)
  312. builder.append("S"sv);
  313. }
  314. if (time_zone_name.has_value()) {
  315. switch (*time_zone_name) {
  316. case CalendarPatternStyle::Short:
  317. builder.append("z"sv);
  318. break;
  319. case CalendarPatternStyle::Long:
  320. builder.append("zzzz"sv);
  321. break;
  322. case CalendarPatternStyle::ShortOffset:
  323. builder.append("O"sv);
  324. break;
  325. case CalendarPatternStyle::LongOffset:
  326. builder.append("OOOO"sv);
  327. break;
  328. case CalendarPatternStyle::ShortGeneric:
  329. builder.append("v"sv);
  330. break;
  331. case CalendarPatternStyle::LongGeneric:
  332. builder.append("vvvv"sv);
  333. break;
  334. default:
  335. break;
  336. }
  337. }
  338. return MUST(builder.to_string());
  339. }
  340. // https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  341. CalendarPattern CalendarPattern::create_from_pattern(StringView pattern)
  342. {
  343. GenericLexer lexer { pattern };
  344. CalendarPattern format {};
  345. while (!lexer.is_eof()) {
  346. if (lexer.next_is(is_quote)) {
  347. lexer.consume_quoted_string();
  348. continue;
  349. }
  350. auto starting_char = lexer.peek();
  351. auto segment = lexer.consume_while([&](char ch) { return ch == starting_char; });
  352. // Era
  353. if (all_of(segment, is_any_of("G"sv))) {
  354. if (segment.length() <= 3)
  355. format.era = CalendarPatternStyle::Short;
  356. else if (segment.length() == 4)
  357. format.era = CalendarPatternStyle::Long;
  358. else
  359. format.era = CalendarPatternStyle::Narrow;
  360. }
  361. // Year
  362. else if (all_of(segment, is_any_of("yYuUr"sv))) {
  363. if (segment.length() == 2)
  364. format.year = CalendarPatternStyle::TwoDigit;
  365. else
  366. format.year = CalendarPatternStyle::Numeric;
  367. }
  368. // Month
  369. else if (all_of(segment, is_any_of("ML"sv))) {
  370. if (segment.length() == 1)
  371. format.month = CalendarPatternStyle::Numeric;
  372. else if (segment.length() == 2)
  373. format.month = CalendarPatternStyle::TwoDigit;
  374. else if (segment.length() == 3)
  375. format.month = CalendarPatternStyle::Short;
  376. else if (segment.length() == 4)
  377. format.month = CalendarPatternStyle::Long;
  378. else if (segment.length() == 5)
  379. format.month = CalendarPatternStyle::Narrow;
  380. }
  381. // Weekday
  382. else if (all_of(segment, is_any_of("ecE"sv))) {
  383. if (segment.length() == 4)
  384. format.weekday = CalendarPatternStyle::Long;
  385. else if (segment.length() == 5)
  386. format.weekday = CalendarPatternStyle::Narrow;
  387. else
  388. format.weekday = CalendarPatternStyle::Short;
  389. }
  390. // Day
  391. else if (all_of(segment, is_any_of("d"sv))) {
  392. if (segment.length() == 1)
  393. format.day = CalendarPatternStyle::Numeric;
  394. else
  395. format.day = CalendarPatternStyle::TwoDigit;
  396. } else if (all_of(segment, is_any_of("DFg"sv))) {
  397. format.day = CalendarPatternStyle::Numeric;
  398. }
  399. // Day period
  400. else if (all_of(segment, is_any_of("B"sv))) {
  401. if (segment.length() == 4)
  402. format.day_period = CalendarPatternStyle::Long;
  403. else if (segment.length() == 5)
  404. format.day_period = CalendarPatternStyle::Narrow;
  405. else
  406. format.day_period = CalendarPatternStyle::Short;
  407. }
  408. // Hour
  409. else if (all_of(segment, is_any_of("hHKk"sv))) {
  410. switch (starting_char) {
  411. case 'K':
  412. format.hour_cycle = HourCycle::H11;
  413. break;
  414. case 'h':
  415. format.hour_cycle = HourCycle::H12;
  416. break;
  417. case 'H':
  418. format.hour_cycle = HourCycle::H23;
  419. break;
  420. case 'k':
  421. format.hour_cycle = HourCycle::H24;
  422. break;
  423. }
  424. if (segment.length() == 1)
  425. format.hour = CalendarPatternStyle::Numeric;
  426. else
  427. format.hour = CalendarPatternStyle::TwoDigit;
  428. }
  429. // Minute
  430. else if (all_of(segment, is_any_of("m"sv))) {
  431. if (segment.length() == 1)
  432. format.minute = CalendarPatternStyle::Numeric;
  433. else
  434. format.minute = CalendarPatternStyle::TwoDigit;
  435. }
  436. // Second
  437. else if (all_of(segment, is_any_of("s"sv))) {
  438. if (segment.length() == 1)
  439. format.second = CalendarPatternStyle::Numeric;
  440. else
  441. format.second = CalendarPatternStyle::TwoDigit;
  442. } else if (all_of(segment, is_any_of("S"sv))) {
  443. format.fractional_second_digits = static_cast<u8>(segment.length());
  444. }
  445. // Zone
  446. else if (all_of(segment, is_any_of("zV"sv))) {
  447. if (segment.length() < 4)
  448. format.time_zone_name = CalendarPatternStyle::Short;
  449. else
  450. format.time_zone_name = CalendarPatternStyle::Long;
  451. } else if (all_of(segment, is_any_of("ZOXx"sv))) {
  452. if (segment.length() < 4)
  453. format.time_zone_name = CalendarPatternStyle::ShortOffset;
  454. else
  455. format.time_zone_name = CalendarPatternStyle::LongOffset;
  456. } else if (all_of(segment, is_any_of("v"sv))) {
  457. if (segment.length() < 4)
  458. format.time_zone_name = CalendarPatternStyle::ShortGeneric;
  459. else
  460. format.time_zone_name = CalendarPatternStyle::LongGeneric;
  461. }
  462. }
  463. return format;
  464. }
  465. template<typename T, typename GetRegionalValues>
  466. static T find_regional_values_for_locale(StringView locale, GetRegionalValues&& get_regional_values)
  467. {
  468. auto has_value = [](auto const& container) {
  469. if constexpr (requires { container.has_value(); })
  470. return container.has_value();
  471. else
  472. return !container.is_empty();
  473. };
  474. if (auto regional_values = get_regional_values(locale); has_value(regional_values))
  475. return regional_values;
  476. auto return_default_values = [&]() { return get_regional_values("001"sv); };
  477. auto language = parse_unicode_language_id(locale);
  478. if (!language.has_value())
  479. return return_default_values();
  480. if (!language->region.has_value()) {
  481. if (auto maximized = add_likely_subtags(language->to_string()); maximized.has_value())
  482. language = parse_unicode_language_id(*maximized);
  483. }
  484. if (!language.has_value() || !language->region.has_value())
  485. return return_default_values();
  486. if (auto regional_values = get_regional_values(*language->region); has_value(regional_values))
  487. return regional_values;
  488. return return_default_values();
  489. }
  490. // ICU does not contain a field enumeration for "literal" partitions. Define a custom field so that we may provide a
  491. // type for those partitions.
  492. static constexpr i32 LITERAL_FIELD = -1;
  493. static constexpr StringView icu_date_time_format_field_to_string(i32 field)
  494. {
  495. switch (field) {
  496. case LITERAL_FIELD:
  497. return "literal"sv;
  498. case UDAT_ERA_FIELD:
  499. return "era"sv;
  500. case UDAT_YEAR_FIELD:
  501. case UDAT_EXTENDED_YEAR_FIELD:
  502. return "year"sv;
  503. case UDAT_YEAR_NAME_FIELD:
  504. return "yearName"sv;
  505. case UDAT_RELATED_YEAR_FIELD:
  506. return "relatedYear"sv;
  507. case UDAT_MONTH_FIELD:
  508. case UDAT_STANDALONE_MONTH_FIELD:
  509. return "month"sv;
  510. case UDAT_DAY_OF_WEEK_FIELD:
  511. case UDAT_DOW_LOCAL_FIELD:
  512. case UDAT_STANDALONE_DAY_FIELD:
  513. return "weekday"sv;
  514. case UDAT_DATE_FIELD:
  515. return "day"sv;
  516. case UDAT_AM_PM_FIELD:
  517. case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
  518. case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
  519. return "dayPeriod"sv;
  520. case UDAT_HOUR_OF_DAY1_FIELD:
  521. case UDAT_HOUR_OF_DAY0_FIELD:
  522. case UDAT_HOUR1_FIELD:
  523. case UDAT_HOUR0_FIELD:
  524. return "hour"sv;
  525. case UDAT_MINUTE_FIELD:
  526. return "minute"sv;
  527. case UDAT_SECOND_FIELD:
  528. return "second"sv;
  529. case UDAT_FRACTIONAL_SECOND_FIELD:
  530. return "fractionalSecond"sv;
  531. case UDAT_TIMEZONE_FIELD:
  532. case UDAT_TIMEZONE_RFC_FIELD:
  533. case UDAT_TIMEZONE_GENERIC_FIELD:
  534. case UDAT_TIMEZONE_SPECIAL_FIELD:
  535. case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
  536. case UDAT_TIMEZONE_ISO_FIELD:
  537. case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
  538. return "timeZoneName"sv;
  539. default:
  540. return "unknown"sv;
  541. }
  542. }
  543. static bool apply_hour_cycle_to_skeleton(icu::UnicodeString& skeleton, Optional<HourCycle> const& hour_cycle, Optional<bool> const& hour12)
  544. {
  545. auto hour_cycle_symbol = icu_hour_cycle(hour_cycle, hour12);
  546. if (hour_cycle_symbol == 'j')
  547. return false;
  548. bool changed_hour_cycle = false;
  549. bool inside_quote = false;
  550. for (i32 i = 0; i < skeleton.length(); ++i) {
  551. switch (skeleton[i]) {
  552. case '\'':
  553. inside_quote = !inside_quote;
  554. break;
  555. case 'h':
  556. case 'H':
  557. case 'k':
  558. case 'K':
  559. if (!inside_quote && static_cast<char>(skeleton[i]) != hour_cycle_symbol) {
  560. skeleton.setCharAt(i, hour_cycle_symbol);
  561. changed_hour_cycle = true;
  562. }
  563. break;
  564. default:
  565. break;
  566. }
  567. }
  568. return changed_hour_cycle;
  569. }
  570. static void apply_time_zone_to_formatter(icu::SimpleDateFormat& formatter, icu::Locale const& locale, StringView time_zone_identifier)
  571. {
  572. UErrorCode status = U_ZERO_ERROR;
  573. auto* time_zone = icu::TimeZone::createTimeZone(icu_string(time_zone_identifier));
  574. auto* calendar = icu::Calendar::createInstance(time_zone, locale, status);
  575. VERIFY(icu_success(status));
  576. if (calendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) {
  577. // https://tc39.es/ecma262/#sec-time-values-and-time-range
  578. // A time value supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds.
  579. static constexpr double ECMA_262_MINIMUM_TIME = -8.64E15;
  580. auto* gregorian_calendar = static_cast<icu::GregorianCalendar*>(calendar);
  581. gregorian_calendar->setGregorianChange(ECMA_262_MINIMUM_TIME, status);
  582. VERIFY(icu_success(status));
  583. }
  584. formatter.adoptCalendar(calendar);
  585. }
  586. static bool is_formatted_range_actually_a_range(icu::FormattedDateInterval const& formatted)
  587. {
  588. UErrorCode status = U_ZERO_ERROR;
  589. auto result = formatted.toTempString(status);
  590. if (icu_failure(status))
  591. return false;
  592. icu::ConstrainedFieldPosition position;
  593. position.constrainCategory(UFIELD_CATEGORY_DATE_INTERVAL_SPAN);
  594. auto has_range = static_cast<bool>(formatted.nextPosition(position, status));
  595. if (icu_failure(status))
  596. return false;
  597. return has_range;
  598. }
  599. class DateTimeFormatImpl : public DateTimeFormat {
  600. public:
  601. DateTimeFormatImpl(icu::Locale& locale, icu::UnicodeString const& pattern, StringView time_zone_identifier, NonnullOwnPtr<icu::SimpleDateFormat> formatter)
  602. : m_locale(locale)
  603. , m_pattern(CalendarPattern::create_from_pattern(icu_string_to_string(pattern)))
  604. , m_formatter(move(formatter))
  605. {
  606. apply_time_zone_to_formatter(*m_formatter, m_locale, time_zone_identifier);
  607. }
  608. virtual ~DateTimeFormatImpl() override = default;
  609. virtual CalendarPattern const& chosen_pattern() const override { return m_pattern; }
  610. virtual String format(double time) const override
  611. {
  612. auto formatted_time = format_impl(time);
  613. if (!formatted_time.has_value())
  614. return {};
  615. return icu_string_to_string(*formatted_time);
  616. }
  617. virtual Vector<Partition> format_to_parts(double time) const override
  618. {
  619. icu::FieldPositionIterator iterator;
  620. auto formatted_time = format_impl(time, &iterator);
  621. if (!formatted_time.has_value())
  622. return {};
  623. Vector<Partition> result;
  624. auto create_partition = [&](i32 field, i32 begin, i32 end) {
  625. Partition partition;
  626. partition.type = icu_date_time_format_field_to_string(field);
  627. partition.value = icu_string_to_string(formatted_time->tempSubStringBetween(begin, end));
  628. partition.source = "shared"sv;
  629. result.append(move(partition));
  630. };
  631. icu::FieldPosition position;
  632. i32 previous_end_index = 0;
  633. while (static_cast<bool>(iterator.next(position))) {
  634. if (previous_end_index < position.getBeginIndex())
  635. create_partition(LITERAL_FIELD, previous_end_index, position.getBeginIndex());
  636. if (position.getField() >= 0)
  637. create_partition(position.getField(), position.getBeginIndex(), position.getEndIndex());
  638. previous_end_index = position.getEndIndex();
  639. }
  640. if (previous_end_index < formatted_time->length())
  641. create_partition(LITERAL_FIELD, previous_end_index, formatted_time->length());
  642. return result;
  643. }
  644. virtual String format_range(double start, double end) const override
  645. {
  646. UErrorCode status = U_ZERO_ERROR;
  647. auto formatted = format_range_impl(start, end);
  648. if (!formatted.has_value())
  649. return {};
  650. if (!is_formatted_range_actually_a_range(*formatted))
  651. return format(start);
  652. auto formatted_time = formatted->toTempString(status);
  653. if (icu_failure(status))
  654. return {};
  655. return icu_string_to_string(formatted_time);
  656. }
  657. virtual Vector<Partition> format_range_to_parts(double start, double end) const override
  658. {
  659. UErrorCode status = U_ZERO_ERROR;
  660. auto formatted = format_range_impl(start, end);
  661. if (!formatted.has_value())
  662. return {};
  663. if (!is_formatted_range_actually_a_range(*formatted))
  664. return format_to_parts(start);
  665. auto formatted_time = formatted->toTempString(status);
  666. if (icu_failure(status))
  667. return {};
  668. icu::ConstrainedFieldPosition position;
  669. i32 previous_end_index = 0;
  670. Vector<Partition> result;
  671. Optional<PartitionRange> start_range;
  672. Optional<PartitionRange> end_range;
  673. auto create_partition = [&](i32 field, i32 begin, i32 end) {
  674. Partition partition;
  675. partition.type = icu_date_time_format_field_to_string(field);
  676. partition.value = icu_string_to_string(formatted_time.tempSubStringBetween(begin, end));
  677. if (start_range.has_value() && start_range->contains(begin))
  678. partition.source = "startRange"sv;
  679. else if (end_range.has_value() && end_range->contains(begin))
  680. partition.source = "endRange"sv;
  681. else
  682. partition.source = "shared"sv;
  683. result.append(move(partition));
  684. };
  685. while (static_cast<bool>(formatted->nextPosition(position, status)) && icu_success(status)) {
  686. if (previous_end_index < position.getStart())
  687. create_partition(LITERAL_FIELD, previous_end_index, position.getStart());
  688. if (position.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
  689. auto& range = position.getField() == 0 ? start_range : end_range;
  690. range = PartitionRange { position.getField(), position.getStart(), position.getLimit() };
  691. } else if (position.getCategory() == UFIELD_CATEGORY_DATE) {
  692. create_partition(position.getField(), position.getStart(), position.getLimit());
  693. }
  694. previous_end_index = position.getLimit();
  695. }
  696. if (previous_end_index < formatted_time.length())
  697. create_partition(LITERAL_FIELD, previous_end_index, formatted_time.length());
  698. return result;
  699. }
  700. private:
  701. Optional<icu::UnicodeString> format_impl(double time, icu::FieldPositionIterator* iterator = nullptr) const
  702. {
  703. UErrorCode status = U_ZERO_ERROR;
  704. icu::UnicodeString formatted_time;
  705. m_formatter->format(time, formatted_time, iterator, status);
  706. if (icu_failure(status))
  707. return {};
  708. return formatted_time;
  709. }
  710. Optional<icu::FormattedDateInterval> format_range_impl(double start, double end) const
  711. {
  712. UErrorCode status = U_ZERO_ERROR;
  713. if (!m_range_formatter) {
  714. icu::UnicodeString pattern;
  715. m_formatter->toPattern(pattern);
  716. auto skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
  717. if (icu_failure(status))
  718. return {};
  719. auto* formatter = icu::DateIntervalFormat::createInstance(skeleton, m_locale, status);
  720. if (icu_failure(status))
  721. return {};
  722. m_range_formatter = adopt_own(*formatter);
  723. m_range_formatter->setTimeZone(m_formatter->getTimeZone());
  724. }
  725. auto start_calendar = adopt_own(*m_formatter->getCalendar()->clone());
  726. start_calendar->setTime(start, status);
  727. if (icu_failure(status))
  728. return {};
  729. auto end_calendar = adopt_own(*m_formatter->getCalendar()->clone());
  730. end_calendar->setTime(end, status);
  731. if (icu_failure(status))
  732. return {};
  733. auto formatted = m_range_formatter->formatToValue(*start_calendar, *end_calendar, status);
  734. if (icu_failure(status))
  735. return {};
  736. return formatted;
  737. }
  738. icu::Locale& m_locale;
  739. CalendarPattern m_pattern;
  740. NonnullOwnPtr<icu::SimpleDateFormat> m_formatter;
  741. mutable OwnPtr<icu::DateIntervalFormat> m_range_formatter;
  742. };
  743. NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_date_and_time_style(
  744. StringView locale,
  745. StringView time_zone_identifier,
  746. Optional<HourCycle> const& hour_cycle,
  747. Optional<bool> const& hour12,
  748. Optional<DateTimeStyle> const& date_style,
  749. Optional<DateTimeStyle> const& time_style)
  750. {
  751. UErrorCode status = U_ZERO_ERROR;
  752. auto locale_data = LocaleData::for_locale(locale);
  753. VERIFY(locale_data.has_value());
  754. auto formatter = adopt_own(*verify_cast<icu::SimpleDateFormat>([&]() {
  755. if (date_style.has_value() && time_style.has_value()) {
  756. return icu::DateFormat::createDateTimeInstance(
  757. icu_date_time_style(*date_style), icu_date_time_style(*time_style), locale_data->locale());
  758. }
  759. if (date_style.has_value()) {
  760. return icu::DateFormat::createDateInstance(
  761. icu_date_time_style(*date_style), locale_data->locale());
  762. }
  763. if (time_style.has_value()) {
  764. return icu::DateFormat::createTimeInstance(
  765. icu_date_time_style(*time_style), locale_data->locale());
  766. }
  767. VERIFY_NOT_REACHED();
  768. }()));
  769. icu::UnicodeString pattern;
  770. formatter->toPattern(pattern);
  771. auto skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
  772. VERIFY(icu_success(status));
  773. if (apply_hour_cycle_to_skeleton(skeleton, hour_cycle, hour12)) {
  774. pattern = locale_data->date_time_pattern_generator().getBestPattern(skeleton, UDATPG_MATCH_ALL_FIELDS_LENGTH, status);
  775. VERIFY(icu_success(status));
  776. apply_hour_cycle_to_skeleton(pattern, hour_cycle, hour12);
  777. formatter = adopt_own(*new icu::SimpleDateFormat(pattern, locale_data->locale(), status));
  778. VERIFY(icu_success(status));
  779. }
  780. return adopt_own(*new DateTimeFormatImpl(locale_data->locale(), pattern, time_zone_identifier, move(formatter)));
  781. }
  782. NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_pattern_options(
  783. StringView locale,
  784. StringView time_zone_identifier,
  785. CalendarPattern const& options)
  786. {
  787. UErrorCode status = U_ZERO_ERROR;
  788. auto locale_data = LocaleData::for_locale(locale);
  789. VERIFY(locale_data.has_value());
  790. auto skeleton = icu_string(options.to_pattern());
  791. auto pattern = locale_data->date_time_pattern_generator().getBestPattern(skeleton, UDATPG_MATCH_ALL_FIELDS_LENGTH, status);
  792. VERIFY(icu_success(status));
  793. apply_hour_cycle_to_skeleton(pattern, options.hour_cycle, {});
  794. auto formatter = adopt_own(*new icu::SimpleDateFormat(pattern, locale_data->locale(), status));
  795. VERIFY(icu_success(status));
  796. return adopt_own(*new DateTimeFormatImpl(locale_data->locale(), pattern, time_zone_identifier, move(formatter)));
  797. }
  798. static constexpr Weekday icu_calendar_day_to_weekday(UCalendarDaysOfWeek day)
  799. {
  800. switch (day) {
  801. case UCAL_SUNDAY:
  802. return Weekday::Sunday;
  803. case UCAL_MONDAY:
  804. return Weekday::Monday;
  805. case UCAL_TUESDAY:
  806. return Weekday::Tuesday;
  807. case UCAL_WEDNESDAY:
  808. return Weekday::Wednesday;
  809. case UCAL_THURSDAY:
  810. return Weekday::Thursday;
  811. case UCAL_FRIDAY:
  812. return Weekday::Friday;
  813. case UCAL_SATURDAY:
  814. return Weekday::Saturday;
  815. }
  816. VERIFY_NOT_REACHED();
  817. }
  818. WeekInfo week_info_of_locale(StringView locale)
  819. {
  820. UErrorCode status = U_ZERO_ERROR;
  821. auto locale_data = LocaleData::for_locale(locale);
  822. if (!locale_data.has_value())
  823. return {};
  824. auto calendar = adopt_own_if_nonnull(icu::Calendar::createInstance(locale_data->locale(), status));
  825. if (icu_failure(status))
  826. return {};
  827. WeekInfo week_info;
  828. week_info.minimal_days_in_first_week = calendar->getMinimalDaysInFirstWeek();
  829. if (auto day = calendar->getFirstDayOfWeek(status); icu_success(status))
  830. week_info.first_day_of_week = icu_calendar_day_to_weekday(day);
  831. auto append_if_weekend = [&](auto day) {
  832. auto type = calendar->getDayOfWeekType(day, status);
  833. if (icu_failure(status))
  834. return;
  835. switch (type) {
  836. case UCAL_WEEKEND_ONSET:
  837. case UCAL_WEEKEND_CEASE:
  838. case UCAL_WEEKEND:
  839. week_info.weekend_days.append(icu_calendar_day_to_weekday(day));
  840. break;
  841. default:
  842. break;
  843. }
  844. };
  845. append_if_weekend(UCAL_SUNDAY);
  846. append_if_weekend(UCAL_MONDAY);
  847. append_if_weekend(UCAL_TUESDAY);
  848. append_if_weekend(UCAL_WEDNESDAY);
  849. append_if_weekend(UCAL_THURSDAY);
  850. append_if_weekend(UCAL_FRIDAY);
  851. append_if_weekend(UCAL_SATURDAY);
  852. return week_info;
  853. }
  854. }