tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimeZone.h (8931B)


      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 #ifndef intl_components_TimeZone_h_
      5 #define intl_components_TimeZone_h_
      6 
      7 // ICU doesn't provide a separate C API for time zone functions, but instead
      8 // requires to use UCalendar. This adds a measurable overhead when compared to
      9 // using ICU's C++ TimeZone API, therefore we prefer to use the C++ API when
     10 // possible. Due to the lack of a stable ABI in C++, it's only possible to use
     11 // the C++ API when we use our in-tree ICU copy.
     12 #if !MOZ_SYSTEM_ICU
     13 #  define MOZ_INTL_USE_ICU_CPP_TIMEZONE 1
     14 #else
     15 #  define MOZ_INTL_USE_ICU_CPP_TIMEZONE 0
     16 #endif
     17 
     18 #include <stdint.h>
     19 #include <utility>
     20 
     21 #include "unicode/ucal.h"
     22 #include "unicode/utypes.h"
     23 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
     24 #  include "unicode/locid.h"
     25 #  include "unicode/timezone.h"
     26 #  include "unicode/unistr.h"
     27 #endif
     28 
     29 #include "mozilla/Assertions.h"
     30 #include "mozilla/Casting.h"
     31 #include "mozilla/intl/ICU4CGlue.h"
     32 #include "mozilla/intl/ICUError.h"
     33 #include "mozilla/Maybe.h"
     34 #include "mozilla/Result.h"
     35 #include "mozilla/Span.h"
     36 #include "mozilla/UniquePtr.h"
     37 
     38 namespace mozilla::intl {
     39 
     40 /**
     41 * This component is a Mozilla-focused API for working with time zones in
     42 * internationalization code. It is used in coordination with other operations
     43 * such as datetime formatting.
     44 */
     45 class TimeZone final {
     46 public:
     47 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
     48  explicit TimeZone(UniquePtr<icu::TimeZone> aTimeZone)
     49      : mTimeZone(std::move(aTimeZone)) {
     50    MOZ_ASSERT(mTimeZone);
     51  }
     52 #else
     53  explicit TimeZone(UCalendar* aCalendar) : mCalendar(aCalendar) {
     54    MOZ_ASSERT(mCalendar);
     55  }
     56 #endif
     57 
     58  // Do not allow copy as this class owns the ICU resource. Move is not
     59  // currently implemented, but a custom move operator could be created if
     60  // needed.
     61  TimeZone(const TimeZone&) = delete;
     62  TimeZone& operator=(const TimeZone&) = delete;
     63 
     64 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
     65  ~TimeZone() = default;
     66 #else
     67  ~TimeZone();
     68 #endif
     69 
     70  /**
     71   * Create a TimeZone.
     72   */
     73  static Result<UniquePtr<TimeZone>, ICUError> TryCreate(
     74      Maybe<Span<const char>> aTimeZoneOverride = Nothing{});
     75 
     76  /**
     77   * A number indicating the raw offset from GMT in milliseconds.
     78   */
     79  Result<int32_t, ICUError> GetRawOffsetMs();
     80 
     81  /**
     82   * Return the daylight saving offset in milliseconds at the given UTC time.
     83   */
     84  Result<int32_t, ICUError> GetDSTOffsetMs(int64_t aUTCMilliseconds);
     85 
     86  /**
     87   * Return the local offset in milliseconds at the given UTC time.
     88   */
     89  Result<int32_t, ICUError> GetOffsetMs(int64_t aUTCMilliseconds);
     90 
     91  /**
     92   * Return the UTC offset in milliseconds at the given local time.
     93   */
     94  Result<int32_t, ICUError> GetUTCOffsetMs(int64_t aLocalMilliseconds);
     95 
     96  enum class LocalOption {
     97    /**
     98     * The input is interpreted as local time before a time zone transition.
     99     */
    100    Former,
    101 
    102    /**
    103     * The input is interpreted as local time after a time zone transition.
    104     */
    105    Latter,
    106  };
    107 
    108  /**
    109   * Return the UTC offset in milliseconds at the given local time.
    110   *
    111   * `aSkippedTime` and `aRepeatedTime` select how to interpret skipped and
    112   * repeated local times.
    113   */
    114  Result<int32_t, ICUError> GetUTCOffsetMs(int64_t aLocalMilliseconds,
    115                                           LocalOption aSkippedTime,
    116                                           LocalOption aRepeatedTime);
    117 
    118  /**
    119   * Return the previous time zone transition before the given UTC time. If no
    120   * transition was found, return Nothing.
    121   */
    122  Result<Maybe<int64_t>, ICUError> GetPreviousTransition(
    123      int64_t aUTCMilliseconds);
    124 
    125  /**
    126   * Return the next time zone transition after the given UTC time. If no
    127   * transition was found, return Nothing.
    128   */
    129  Result<Maybe<int64_t>, ICUError> GetNextTransition(int64_t aUTCMilliseconds);
    130 
    131  enum class DaylightSavings : bool { No, Yes };
    132 
    133  /**
    134   * Return the display name for this time zone.
    135   */
    136  template <typename B>
    137  ICUResult GetDisplayName(const char* aLocale,
    138                           DaylightSavings aDaylightSavings, B& aBuffer) {
    139 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
    140    icu::UnicodeString displayName;
    141    mTimeZone->getDisplayName(static_cast<bool>(aDaylightSavings),
    142                              icu::TimeZone::LONG, icu::Locale(aLocale),
    143                              displayName);
    144    return FillBuffer(displayName, aBuffer);
    145 #else
    146    return FillBufferWithICUCall(
    147        aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
    148          UCalendarDisplayNameType type =
    149              static_cast<bool>(aDaylightSavings) ? UCAL_DST : UCAL_STANDARD;
    150          return ucal_getTimeZoneDisplayName(mCalendar, type, aLocale, target,
    151                                             length, status);
    152        });
    153 #endif
    154  }
    155 
    156  /**
    157   * Return the identifier for this time zone.
    158   */
    159  template <typename B>
    160  ICUResult GetId(B& aBuffer) {
    161 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
    162    icu::UnicodeString id;
    163    mTimeZone->getID(id);
    164    return FillBuffer(id, aBuffer);
    165 #else
    166    return FillBufferWithICUCall(
    167        aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
    168          return ucal_getTimeZoneID(mCalendar, target, length, status);
    169        });
    170 #endif
    171  }
    172 
    173  /**
    174   * Fill the buffer with the system's default IANA time zone identifier, e.g.
    175   * "America/Chicago".
    176   */
    177  template <typename B>
    178  static ICUResult GetDefaultTimeZone(B& aBuffer) {
    179    return FillBufferWithICUCall(aBuffer, ucal_getDefaultTimeZone);
    180  }
    181 
    182  /**
    183   * Fill the buffer with the host system's default IANA time zone identifier,
    184   * e.g. "America/Chicago".
    185   *
    186   * NOTE: This function is not thread-safe.
    187   */
    188  template <typename B>
    189  static ICUResult GetHostTimeZone(B& aBuffer) {
    190    return FillBufferWithICUCall(aBuffer, ucal_getHostTimeZone);
    191  }
    192 
    193  /**
    194   * Set the default time zone.
    195   */
    196  static Result<bool, ICUError> SetDefaultTimeZone(Span<const char> aTimeZone);
    197 
    198  /**
    199   * Set the default time zone using the host system's time zone.
    200   *
    201   * NOTE: This function is not thread-safe.
    202   */
    203  static ICUResult SetDefaultTimeZoneFromHostTimeZone();
    204 
    205  /**
    206   * Return the tzdata version.
    207   *
    208   * The tzdata version is a string of the form "<year><release>", e.g. "2021a".
    209   */
    210  static Result<Span<const char>, ICUError> GetTZDataVersion();
    211 
    212  /**
    213   * Constant for the typical maximal length of a time zone identifier.
    214   *
    215   * At the time of this writing 32 characters fits every supported time zone:
    216   *
    217   * Intl.supportedValuesOf("timeZone")
    218   *     .reduce((acc, v) => Math.max(acc, v.length), 0)
    219   */
    220  static constexpr size_t TimeZoneIdentifierLength = 32;
    221 
    222  /**
    223   * Returns the canonical system time zone ID or the normalized custom time
    224   * zone ID for the given time zone ID.
    225   */
    226  template <typename B>
    227  static ICUResult GetCanonicalTimeZoneID(Span<const char16_t> inputTimeZone,
    228                                          B& aBuffer) {
    229    static_assert(std::is_same_v<typename B::CharType, char16_t>,
    230                  "Currently only UTF-16 buffers are supported.");
    231 
    232    if (aBuffer.capacity() == 0) {
    233      // ucal_getCanonicalTimeZoneID differs from other API calls and fails when
    234      // passed a nullptr or 0 length result. Reserve some space initially so
    235      // that a real pointer will be used in the API.
    236      if (!aBuffer.reserve(TimeZoneIdentifierLength)) {
    237        return Err(ICUError::OutOfMemory);
    238      }
    239    }
    240 
    241    return FillBufferWithICUCall(
    242        aBuffer,
    243        [&inputTimeZone](UChar* target, int32_t length, UErrorCode* status) {
    244          return ucal_getCanonicalTimeZoneID(
    245              inputTimeZone.Elements(),
    246              static_cast<int32_t>(inputTimeZone.Length()), target, length,
    247              /* isSystemID */ nullptr, status);
    248        });
    249  }
    250 
    251  /**
    252   * Return an enumeration over all time zones commonly used in the given
    253   * region.
    254   */
    255  static Result<SpanEnumeration<char>, ICUError> GetAvailableTimeZones(
    256      const char* aRegion);
    257 
    258  /**
    259   * Return an enumeration over all available time zones.
    260   */
    261  static Result<SpanEnumeration<char>, ICUError> GetAvailableTimeZones();
    262 
    263 private:
    264 #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
    265  template <typename B>
    266  static ICUResult FillBuffer(const icu::UnicodeString& aString, B& aBuffer) {
    267    int32_t length = aString.length();
    268    if (!aBuffer.reserve(AssertedCast<size_t>(length))) {
    269      return Err(ICUError::OutOfMemory);
    270    }
    271 
    272    UErrorCode status = U_ZERO_ERROR;
    273    int32_t written = aString.extract(aBuffer.data(), length, status);
    274    if (!ICUSuccessForStringSpan(status)) {
    275      return Err(ToICUError(status));
    276    }
    277    MOZ_ASSERT(written == length);
    278 
    279    aBuffer.written(written);
    280 
    281    return Ok{};
    282  }
    283 
    284  UniquePtr<icu::TimeZone> mTimeZone = nullptr;
    285 #else
    286  UCalendar* mCalendar = nullptr;
    287 #endif
    288 };
    289 
    290 }  // namespace mozilla::intl
    291 
    292 #endif