tor-browser

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

DateTimeFormatUtils.cpp (7043B)


      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 
      5 #include "mozilla/Assertions.h"
      6 #include "mozilla/Try.h"
      7 
      8 #include "DateTimeFormatUtils.h"
      9 #include "mozilla/intl/ICU4CGlue.h"
     10 
     11 #include <cstring>
     12 
     13 #if !MOZ_SYSTEM_ICU
     14 #  include "calendar/ICU4XChineseCalendar.h"
     15 #  include "calendar/ICU4XDangiCalendar.h"
     16 #  include "unicode/datefmt.h"
     17 #  include "unicode/gregocal.h"
     18 #endif
     19 
     20 namespace mozilla::intl {
     21 
     22 DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName) {
     23  // See intl/icu/source/i18n/unicode/udat.h for a detailed field list.  This
     24  // switch is deliberately exhaustive: cases might have to be added/removed
     25  // if this code is compiled with a different ICU with more
     26  // UDateFormatField enum initializers.  Please guard such cases with
     27  // appropriate ICU version-testing #ifdefs, should cross-version divergence
     28  // occur.
     29  switch (fieldName) {
     30    case UDAT_ERA_FIELD:
     31      return DateTimePartType::Era;
     32 
     33    case UDAT_YEAR_FIELD:
     34    case UDAT_YEAR_WOY_FIELD:
     35    case UDAT_EXTENDED_YEAR_FIELD:
     36      return DateTimePartType::Year;
     37 
     38    case UDAT_YEAR_NAME_FIELD:
     39      return DateTimePartType::YearName;
     40 
     41    case UDAT_MONTH_FIELD:
     42    case UDAT_STANDALONE_MONTH_FIELD:
     43      return DateTimePartType::Month;
     44 
     45    case UDAT_DATE_FIELD:
     46    case UDAT_JULIAN_DAY_FIELD:
     47      return DateTimePartType::Day;
     48 
     49    case UDAT_HOUR_OF_DAY1_FIELD:
     50    case UDAT_HOUR_OF_DAY0_FIELD:
     51    case UDAT_HOUR1_FIELD:
     52    case UDAT_HOUR0_FIELD:
     53      return DateTimePartType::Hour;
     54 
     55    case UDAT_MINUTE_FIELD:
     56      return DateTimePartType::Minute;
     57 
     58    case UDAT_SECOND_FIELD:
     59      return DateTimePartType::Second;
     60 
     61    case UDAT_DAY_OF_WEEK_FIELD:
     62    case UDAT_STANDALONE_DAY_FIELD:
     63    case UDAT_DOW_LOCAL_FIELD:
     64    case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD:
     65      return DateTimePartType::Weekday;
     66 
     67    case UDAT_AM_PM_FIELD:
     68    case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
     69      return DateTimePartType::DayPeriod;
     70 
     71    case UDAT_TIMEZONE_FIELD:
     72    case UDAT_TIMEZONE_GENERIC_FIELD:
     73    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
     74      return DateTimePartType::TimeZoneName;
     75 
     76    case UDAT_FRACTIONAL_SECOND_FIELD:
     77      return DateTimePartType::FractionalSecondDigits;
     78 
     79 #ifndef U_HIDE_INTERNAL_API
     80    case UDAT_RELATED_YEAR_FIELD:
     81      return DateTimePartType::RelatedYear;
     82 #endif
     83 
     84    case UDAT_DAY_OF_YEAR_FIELD:
     85    case UDAT_WEEK_OF_YEAR_FIELD:
     86    case UDAT_WEEK_OF_MONTH_FIELD:
     87    case UDAT_MILLISECONDS_IN_DAY_FIELD:
     88    case UDAT_TIMEZONE_RFC_FIELD:
     89    case UDAT_QUARTER_FIELD:
     90    case UDAT_STANDALONE_QUARTER_FIELD:
     91    case UDAT_TIMEZONE_SPECIAL_FIELD:
     92    case UDAT_TIMEZONE_ISO_FIELD:
     93    case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
     94    case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
     95 #ifndef U_HIDE_INTERNAL_API
     96    case UDAT_TIME_SEPARATOR_FIELD:
     97 #endif
     98      // These fields are all unsupported.
     99      return DateTimePartType::Unknown;
    100 
    101 #ifndef U_HIDE_DEPRECATED_API
    102    case UDAT_FIELD_COUNT:
    103      MOZ_ASSERT_UNREACHABLE(
    104          "format field sentinel value returned by "
    105          "iterator!");
    106 #endif
    107  }
    108 
    109  MOZ_ASSERT_UNREACHABLE(
    110      "unenumerated, undocumented format field returned "
    111      "by iterator");
    112  return DateTimePartType::Unknown;
    113 }
    114 
    115 // Start of ECMAScript time.
    116 static constexpr double StartOfTime = -8.64e15;
    117 
    118 #if !MOZ_SYSTEM_ICU
    119 static bool IsGregorianLikeCalendar(const char* type) {
    120  return std::strcmp(type, "gregorian") == 0 ||
    121         std::strcmp(type, "iso8601") == 0 ||
    122         std::strcmp(type, "buddhist") == 0 ||
    123         std::strcmp(type, "japanese") == 0 || std::strcmp(type, "roc") == 0;
    124 }
    125 
    126 /**
    127 * Set the start time of the Gregorian calendar. This is useful for
    128 * ensuring the consistent use of a proleptic Gregorian calendar for ECMA-402.
    129 * https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
    130 */
    131 static Result<Ok, ICUError> SetGregorianChangeDate(
    132    icu::GregorianCalendar* gregorian) {
    133  UErrorCode status = U_ZERO_ERROR;
    134  gregorian->setGregorianChange(StartOfTime, status);
    135  if (U_FAILURE(status)) {
    136    return Err(ToICUError(status));
    137  }
    138  return Ok{};
    139 }
    140 
    141 static bool IsCalendarReplacementSupported(const char* type) {
    142  return std::strcmp(type, "chinese") == 0 || std::strcmp(type, "dangi") == 0;
    143 }
    144 
    145 static Result<UniquePtr<icu::Calendar>, ICUError> CreateCalendarReplacement(
    146    const icu::Calendar* calendar) {
    147  const char* type = calendar->getType();
    148  MOZ_ASSERT(IsCalendarReplacementSupported(type));
    149 
    150  UErrorCode status = U_ZERO_ERROR;
    151  icu::Locale locale = calendar->getLocale(ULOC_ACTUAL_LOCALE, status);
    152  locale.setKeywordValue("calendar", type, status);
    153  if (U_FAILURE(status)) {
    154    return Err(ToICUError(status));
    155  }
    156 
    157  const icu::TimeZone& timeZone = calendar->getTimeZone();
    158 
    159  UniquePtr<icu::Calendar> replacement = nullptr;
    160  if (std::strcmp(type, "chinese") == 0) {
    161    replacement.reset(
    162        new calendar::ICU4XChineseCalendar(timeZone, locale, status));
    163  } else {
    164    MOZ_ASSERT(std::strcmp(type, "dangi") == 0);
    165    replacement.reset(
    166        new calendar::ICU4XDangiCalendar(timeZone, locale, status));
    167  }
    168  if (replacement == nullptr) {
    169    return Err(ICUError::OutOfMemory);
    170  }
    171  if (U_FAILURE(status)) {
    172    return Err(ToICUError(status));
    173  }
    174 
    175  return replacement;
    176 }
    177 #endif
    178 
    179 Result<Ok, ICUError> ApplyCalendarOverride(UDateFormat* aDateFormat) {
    180 #if !MOZ_SYSTEM_ICU
    181  icu::DateFormat* df = reinterpret_cast<icu::DateFormat*>(aDateFormat);
    182  const icu::Calendar* calendar = df->getCalendar();
    183 
    184  const char* type = calendar->getType();
    185 
    186  if (IsGregorianLikeCalendar(type)) {
    187    auto* gregorian = static_cast<const icu::GregorianCalendar*>(calendar);
    188    MOZ_TRY(
    189        SetGregorianChangeDate(const_cast<icu::GregorianCalendar*>(gregorian)));
    190  } else if (IsCalendarReplacementSupported(type)) {
    191    auto replacement = CreateCalendarReplacement(calendar);
    192    if (replacement.isErr()) {
    193      return replacement.propagateErr();
    194    }
    195    df->adoptCalendar(replacement.unwrap().release());
    196  }
    197 #else
    198  UErrorCode status = U_ZERO_ERROR;
    199  UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(aDateFormat));
    200  ucal_setGregorianChange(cal, StartOfTime, &status);
    201  // An error here means the calendar is not Gregorian, and can be ignored.
    202 #endif
    203 
    204  return Ok{};
    205 }
    206 
    207 #if !MOZ_SYSTEM_ICU
    208 Result<UniquePtr<icu::Calendar>, ICUError> CreateCalendarOverride(
    209    const icu::Calendar* calendar) {
    210  const char* type = calendar->getType();
    211 
    212  if (IsGregorianLikeCalendar(type)) {
    213    UniquePtr<icu::GregorianCalendar> gregorian(
    214        static_cast<const icu::GregorianCalendar*>(calendar)->clone());
    215    if (!gregorian) {
    216      return Err(ICUError::OutOfMemory);
    217    }
    218 
    219    MOZ_TRY(SetGregorianChangeDate(gregorian.get()));
    220 
    221    return UniquePtr<icu::Calendar>{gregorian.release()};
    222  }
    223 
    224  if (IsCalendarReplacementSupported(type)) {
    225    return CreateCalendarReplacement(calendar);
    226  }
    227 
    228  return UniquePtr<icu::Calendar>{};
    229 }
    230 #endif
    231 
    232 }  // namespace mozilla::intl