DateTimeFormat.cpp 31 KB

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