TimeZone.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Array.h>
  7. #include <AK/NonnullOwnPtr.h>
  8. #include <AK/QuickSort.h>
  9. #include <LibUnicode/ICU.h>
  10. #include <LibUnicode/TimeZone.h>
  11. #include <unicode/timezone.h>
  12. #include <unicode/ucal.h>
  13. namespace Unicode {
  14. String current_time_zone()
  15. {
  16. UErrorCode status = U_ZERO_ERROR;
  17. auto time_zone = adopt_own_if_nonnull(icu::TimeZone::detectHostTimeZone());
  18. if (!time_zone)
  19. return "UTC"_string;
  20. icu::UnicodeString time_zone_id;
  21. time_zone->getID(time_zone_id);
  22. icu::UnicodeString time_zone_name;
  23. time_zone->getCanonicalID(time_zone_id, time_zone_name, status);
  24. if (icu_failure(status))
  25. return "UTC"_string;
  26. return icu_string_to_string(time_zone_name);
  27. }
  28. // https://github.com/unicode-org/icu/blob/main/icu4c/source/tools/tzcode/icuzones
  29. static constexpr bool is_legacy_non_iana_time_zone(StringView time_zone)
  30. {
  31. constexpr auto legacy_zones = to_array({
  32. "ACT"sv,
  33. "AET"sv,
  34. "AGT"sv,
  35. "ART"sv,
  36. "AST"sv,
  37. "BET"sv,
  38. "BST"sv,
  39. "Canada/East-Saskatchewan"sv,
  40. "CAT"sv,
  41. "CNT"sv,
  42. "CST"sv,
  43. "CTT"sv,
  44. "EAT"sv,
  45. "ECT"sv,
  46. "IET"sv,
  47. "IST"sv,
  48. "JST"sv,
  49. "MIT"sv,
  50. "NET"sv,
  51. "NST"sv,
  52. "PLT"sv,
  53. "PNT"sv,
  54. "PRT"sv,
  55. "PST"sv,
  56. "SST"sv,
  57. "US/Pacific-New"sv,
  58. "VST"sv,
  59. });
  60. if (time_zone.starts_with("SystemV/"sv))
  61. return true;
  62. return legacy_zones.contains_slow(time_zone);
  63. }
  64. static Vector<String> icu_available_time_zones(Optional<ByteString> const& region)
  65. {
  66. UErrorCode status = U_ZERO_ERROR;
  67. char const* icu_region = region.has_value() ? region->characters() : nullptr;
  68. auto time_zone_enumerator = adopt_own_if_nonnull(icu::TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, icu_region, nullptr, status));
  69. if (icu_failure(status))
  70. return { "UTC"_string };
  71. auto time_zones = icu_string_enumeration_to_list(move(time_zone_enumerator), [](char const* zone) {
  72. return !is_legacy_non_iana_time_zone({ zone, strlen(zone) });
  73. });
  74. quick_sort(time_zones);
  75. return time_zones;
  76. }
  77. Vector<String> const& available_time_zones()
  78. {
  79. static auto time_zones = icu_available_time_zones({});
  80. return time_zones;
  81. }
  82. Vector<String> available_time_zones_in_region(StringView region)
  83. {
  84. return icu_available_time_zones(region);
  85. }
  86. Optional<String> resolve_primary_time_zone(StringView time_zone)
  87. {
  88. UErrorCode status = U_ZERO_ERROR;
  89. icu::UnicodeString iana_id;
  90. icu::TimeZone::getIanaID(icu_string(time_zone), iana_id, status);
  91. if (icu_failure(status))
  92. return {};
  93. return icu_string_to_string(iana_id);
  94. }
  95. Optional<TimeZoneOffset> time_zone_offset(StringView time_zone, UnixDateTime time)
  96. {
  97. UErrorCode status = U_ZERO_ERROR;
  98. auto icu_time_zone = adopt_own_if_nonnull(icu::TimeZone::createTimeZone(icu_string(time_zone)));
  99. if (!icu_time_zone || *icu_time_zone == icu::TimeZone::getUnknown())
  100. return {};
  101. i32 raw_offset = 0;
  102. i32 dst_offset = 0;
  103. icu_time_zone->getOffset(static_cast<UDate>(time.milliseconds_since_epoch()), 0, raw_offset, dst_offset, status);
  104. if (icu_failure(status))
  105. return {};
  106. return TimeZoneOffset {
  107. .offset = AK::Duration::from_milliseconds(raw_offset + dst_offset),
  108. .in_dst = dst_offset == 0 ? TimeZoneOffset::InDST::No : TimeZoneOffset::InDST::Yes,
  109. };
  110. }
  111. }