Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/intl/components/src/calendar/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 14 kB image not shown  

Quelle  ICU4XCalendar.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/intl/calendar/ICU4XCalendar.h"

#include "mozilla/Assertions.h"
#include "mozilla/TextUtils.h"

#include <cstring>
#include <stdint.h>
#include <type_traits>

#include "unicode/timezone.h"

#include "icu4x/diplomat_runtime.hpp"
#include "icu4x/CalendarError.hpp"

namespace mozilla::intl::calendar {

// Copied from js/src/util/Text.h
template <typename CharT>
static constexpr uint8_t AsciiDigitToNumber(CharT c) {
  using UnsignedCharT = std::make_unsigned_t<CharT>;
  auto uc = static_cast<UnsignedCharT>(c);
  return uc - '0';
}

static UniqueICU4XCalendar CreateICU4XCalendar(icu4x::capi::CalendarKind kind) {
  auto* result = icu4x::capi::icu4x_Calendar_create_mv1(kind);
  return UniqueICU4XCalendar{result};
}

static UniqueICU4XDate CreateICU4XDate(const ISODate& date,
                                       const icu4x::capi::Calendar* calendar) {
  auto result = icu4x::capi::icu4x_Date_from_iso_in_calendar_mv1(
      date.year, date.month, date.day, calendar);
  if (!result.is_ok) {
    return nullptr;
  }
  return UniqueICU4XDate{result.ok};
}

static UniqueICU4XDate CreateDateFromCodes(
    const icu4x::capi::Calendar* calendar, std::string_view era,
    int32_t eraYear, MonthCode monthCode, int32_t day) {
  auto monthCodeView = std::string_view{monthCode};
  auto date = icu4x::capi::icu4x_Date_from_codes_in_calendar_mv1(
      icu4x::diplomat::capi::DiplomatStringView{era.data(), era.length()},
      eraYear,
      icu4x::diplomat::capi::DiplomatStringView{monthCodeView.data(),
                                                monthCodeView.length()},
      day, calendar);
  if (date.is_ok) {
    return UniqueICU4XDate{date.ok};
  }
  return nullptr;
}

// Copied from js/src/builtin/temporal/Calendar.cpp
static UniqueICU4XDate CreateDateFrom(const icu4x::capi::Calendar* calendar,
                                      std::string_view era, int32_t eraYear,
                                      int32_t month, int32_t day) {
  MOZ_ASSERT(1 <= month && month <= 13);

  // Create date with month number replaced by month-code.
  auto monthCode = MonthCode{std::min(month, 12)};
  auto date = CreateDateFromCodes(calendar, era, eraYear, monthCode, day);
  if (!date) {
    return nullptr;
  }

  // If the ordinal month of |date| matches the input month, no additional
  // changes are necessary and we can directly return |date|.
  int32_t ordinal = icu4x::capi::icu4x_Date_ordinal_month_mv1(date.get());
  if (ordinal == month) {
    return date;
  }

  // Otherwise we need to handle three cases:
  // 1. The input year contains a leap month and we need to adjust the
  //    month-code.
  // 2. The thirteenth month of a year without leap months was requested.
  // 3. The thirteenth month of a year with leap months was requested.
  if (ordinal > month) {
    MOZ_ASSERT(1 < month && month <= 12);

    // This case can only happen in leap years.
    MOZ_ASSERT(icu4x::capi::icu4x_Date_months_in_year_mv1(date.get()) == 13);

    // Leap months can occur after any month in the Chinese calendar.
    //
    // Example when the fourth month is a leap month between M03 and M04.
    //
    // Month code:     M01  M02  M03  M03L  M04  M05  M06 ...
    // Ordinal month:  1    2    3    4     5    6    7

    // The month can be off by exactly one.
    MOZ_ASSERT((ordinal - month) == 1);

    // First try the case when the previous month isn't a leap month. This
    // case can only occur when |month > 2|, because otherwise we know that
    // "M01L" is the correct answer.
    if (month > 2) {
      auto previousMonthCode = MonthCode{month - 1};
      date =
          CreateDateFromCodes(calendar, era, eraYear, previousMonthCode, day);
      if (!date) {
        return nullptr;
      }
      int32_t ordinal = icu4x::capi::icu4x_Date_ordinal_month_mv1(date.get());
      if (ordinal == month) {
        return date;
      }
    }

    // Fall-through when the previous month is a leap month.
  } else {
    MOZ_ASSERT(month == 13);
    MOZ_ASSERT(ordinal == 12);

    // Years with leap months contain thirteen months.
    if (icu4x::capi::icu4x_Date_months_in_year_mv1(date.get()) != 13) {
      return nullptr;
    }

    // Fall-through to return leap month "M12L" at the end of the year.
  }

  // Finally handle the case when the previous month is a leap month.
  auto leapMonthCode = MonthCode{month - 1/* isLeapMonth= */ true};
  return CreateDateFromCodes(calendar, era, eraYear, leapMonthCode, day);
}

static ISODate ToISODate(const icu4x::capi::Date* date) {
  UniqueICU4XIsoDate isoDate{icu4x::capi::icu4x_Date_to_iso_mv1(date)};

  int32_t isoYear = icu4x::capi::icu4x_IsoDate_year_mv1(isoDate.get());
  int32_t isoMonth = icu4x::capi::icu4x_IsoDate_month_mv1(isoDate.get());
  int32_t isoDay = icu4x::capi::icu4x_IsoDate_day_of_month_mv1(isoDate.get());

  return {isoYear, isoMonth, isoDay};
}

////////////////////////////////////////////////////////////////////////////////

ICU4XCalendar::ICU4XCalendar(icu4x::capi::CalendarKind kind,
                             const icu::Locale& locale, UErrorCode& success)
    : icu::Calendar(icu::TimeZone::forLocaleOrDefault(locale), locale, success),
      kind_(kind) {}

ICU4XCalendar::ICU4XCalendar(icu4x::capi::CalendarKind kind,
                             const icu::TimeZone& timeZone,
                             const icu::Locale& locale, UErrorCode& success)
    : icu::Calendar(timeZone, locale, success), kind_(kind) {}

ICU4XCalendar::ICU4XCalendar(const ICU4XCalendar& other)
    : icu::Calendar(other), kind_(other.kind_) {}

ICU4XCalendar::~ICU4XCalendar() = default;

/**
 * Get or create the underlying ICU4X calendar.
 */

icu4x::capi::Calendar* ICU4XCalendar::getICU4XCalendar(
    UErrorCode& status) const {
  if (U_FAILURE(status)) {
    return nullptr;
  }
  if (!calendar_) {
    auto result = CreateICU4XCalendar(kind_);
    if (!result) {
      status = U_INTERNAL_PROGRAM_ERROR;
      return nullptr;
    }
    calendar_ = std::move(result);
  }
  return calendar_.get();
}

UniqueICU4XDate ICU4XCalendar::createICU4XDate(const ISODate& date,
                                               UErrorCode& status) const {
  MOZ_ASSERT(U_SUCCESS(status));

  auto* calendar = getICU4XCalendar(status);
  if (U_FAILURE(status)) {
    return nullptr;
  }

  auto dt = CreateICU4XDate(date, calendar);
  if (!dt) {
    status = U_INTERNAL_PROGRAM_ERROR;
  }
  return dt;
}

MonthCode ICU4XCalendar::monthCodeFrom(const icu4x::capi::Date* date) {
  // Storage for the largest valid month code and the terminating NUL-character.
  // DiplomatWrite doesn't have std::span version.
  // https://github.com/rust-diplomat/diplomat/issues/866
  std::string buf;
  auto writable = icu4x::diplomat::WriteFromString(buf);

  icu4x::capi::icu4x_Date_month_code_mv1(date, &writable);

  MOZ_ASSERT(buf.length() >= 3);
  MOZ_ASSERT(buf[0] == 'M');
  MOZ_ASSERT(mozilla::IsAsciiDigit(buf[1]));
  MOZ_ASSERT(mozilla::IsAsciiDigit(buf[2]));
  MOZ_ASSERT_IF(buf.length() > 3, buf[3] == 'L');

  int32_t ordinal =
      AsciiDigitToNumber(buf[1]) * 10 + AsciiDigitToNumber(buf[2]);
  bool isLeapMonth = buf.length() > 3;

  return MonthCode{ordinal, isLeapMonth};
}

////////////////////////////////////////////
// icu::Calendar implementation overrides //
////////////////////////////////////////////

const char* ICU4XCalendar::getTemporalMonthCode(UErrorCode& status) const {
  int32_t month = get(UCAL_MONTH, status);
  int32_t isLeapMonth = get(UCAL_IS_LEAP_MONTH, status);
  if (U_FAILURE(status)) {
    return nullptr;
  }

  static const char* MonthCodes[] = {
      // Non-leap months.
      "M01",
      "M02",
      "M03",
      "M04",
      "M05",
      "M06",
      "M07",
      "M08",
      "M09",
      "M10",
      "M11",
      "M12",
      "M13",

      // Leap months. (Note: There's no thirteenth leap month.)
      "M01L",
      "M02L",
      "M03L",
      "M04L",
      "M05L",
      "M06L",
      "M07L",
      "M08L",
      "M09L",
      "M10L",
      "M11L",
      "M12L",
  };

  size_t index = month + (isLeapMonth ? 12 : 0);
  if (index >= std::size(MonthCodes)) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return nullptr;
  }
  return MonthCodes[index];
}

void ICU4XCalendar::setTemporalMonthCode(const char* code, UErrorCode& status) {
  if (U_FAILURE(status)) {
    return;
  }

  size_t len = std::strlen(code);
  if (len < 3 || len > 4 || code[0] != 'M' || !IsAsciiDigit(code[1]) ||
      !IsAsciiDigit(code[2]) || (len == 4 && code[3] != 'L')) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return;
  }

  int32_t month =
      AsciiDigitToNumber(code[1]) * 10 + AsciiDigitToNumber(code[2]);
  bool isLeapMonth = len == 4;

  if (month < 1 || month > 13 || (month == 13 && isLeapMonth)) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return;
  }

  // Check if this calendar supports the requested month code.
  auto monthCode = MonthCode{month, isLeapMonth};
  if (!hasMonthCode(monthCode)) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return;
  }

  set(UCAL_MONTH, monthCode.ordinal() - 1);
  set(UCAL_IS_LEAP_MONTH, int32_t(monthCode.isLeapMonth()));
}

int32_t ICU4XCalendar::internalGetMonth(int32_t defaultValue,
                                        UErrorCode& status) const {
  if (U_FAILURE(status)) {
    return 0;
  }
  if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
    return internalGet(UCAL_MONTH, defaultValue);
  }
  if (!hasLeapMonths()) {
    return internalGet(UCAL_ORDINAL_MONTH);
  }
  return internalGetMonth(status);
}

/**
 * Return the current month, possibly by computing it from |UCAL_ORDINAL_MONTH|.
 */

int32_t ICU4XCalendar::internalGetMonth(UErrorCode& status) const {
  if (U_FAILURE(status)) {
    return 0;
  }
  if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
    return internalGet(UCAL_MONTH);
  }
  if (!hasLeapMonths()) {
    return internalGet(UCAL_ORDINAL_MONTH);
  }

  int32_t extendedYear = internalGet(UCAL_EXTENDED_YEAR);
  int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);

  auto* cal = getICU4XCalendar(status);
  if (U_FAILURE(status)) {
    return 0;
  }

  UniqueICU4XDate date = CreateDateFrom(cal, eraName(extendedYear),
                                        extendedYear, ordinalMonth + 11);
  if (!date) {
    status = U_INTERNAL_PROGRAM_ERROR;
    return 0;
  }

  MonthCode monthCode = monthCodeFrom(date.get());
  int32_t month = monthCode.ordinal() - 1;
  int32_t isLeapMonth = monthCode.isLeapMonth();

  auto* nonConstThis = const_cast<ICU4XCalendar*>(this);
  nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth);
  nonConstThis->internalSet(UCAL_MONTH, month);

  return month;
}

void ICU4XCalendar::add(UCalendarDateFields field, int32_t amount,
                        UErrorCode& status) {
  switch (field) {
    case UCAL_MONTH:
    case UCAL_ORDINAL_MONTH:
      if (amount != 0) {
        // Our implementation doesn't yet support this action.
        status = U_ILLEGAL_ARGUMENT_ERROR;
        break;
      }
      break;
    default:
      Calendar::add(field, amount, status);
      break;
  }
}

void ICU4XCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) {
  add(static_cast<UCalendarDateFields>(field), amount, status);
}

void ICU4XCalendar::roll(UCalendarDateFields field, int32_t amount,
                         UErrorCode& status) {
  switch (field) {
    case UCAL_MONTH:
    case UCAL_ORDINAL_MONTH:
      if (amount != 0) {
        // Our implementation doesn't yet support this action.
        status = U_ILLEGAL_ARGUMENT_ERROR;
        break;
      }
      break;
    default:
      Calendar::roll(field, amount, status);
      break;
  }
}

void ICU4XCalendar::roll(EDateFields field, int32_t amount,
                         UErrorCode& status) {
  roll(static_cast<UCalendarDateFields>(field), amount, status);
}

int32_t ICU4XCalendar::handleGetExtendedYear(UErrorCode& status) {
  if (U_FAILURE(status)) {
    return 0;
  }
  if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
    return internalGet(UCAL_EXTENDED_YEAR, 1);
  }

  // We don't yet support the case when UCAL_YEAR is newer.
  status = U_UNSUPPORTED_ERROR;
  return 0;
}

int32_t ICU4XCalendar::handleGetYearLength(int32_t extendedYear,
                                           UErrorCode& status) const {
  auto* cal = getICU4XCalendar(status);
  if (U_FAILURE(status)) {
    return 0;
  }

  UniqueICU4XDate date =
      CreateDateFrom(cal, eraName(extendedYear), extendedYear, 11);
  if (!date) {
    status = U_INTERNAL_PROGRAM_ERROR;
    return 0;
  }
  return icu4x::capi::icu4x_Date_days_in_year_mv1(date.get());
}

/**
 * Return the number of days in a month.
 */

int32_t ICU4XCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
                                            UErrorCode& status) const {
  if (U_FAILURE(status)) {
    return 0;
  }

  // ICU4C supports wrap around. We don't support this case.
  if (month < 0 || month > 11) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return 0;
  }

  auto* cal = getICU4XCalendar(status);
  if (U_FAILURE(status)) {
    return 0;
  }

  bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
  auto monthCode = MonthCode{month + 1, isLeapMonth};
  UniqueICU4XDate date = CreateDateFromCodes(cal, eraName(extendedYear),
                                             extendedYear, monthCode, 1);
  if (!date) {
    status = U_INTERNAL_PROGRAM_ERROR;
    return 0;
  }

  return icu4x::capi::icu4x_Date_days_in_month_mv1(date.get());
}

/**
 * Return the start of the month as a Julian date.
 */

int64_t ICU4XCalendar::handleComputeMonthStart(int32_t extendedYear,
                                               int32_t month, UBool useMonth,
                                               UErrorCode& status) const {
  if (U_FAILURE(status)) {
    return 0;
  }

  // ICU4C supports wrap around. We don't support this case.
  if (month < 0 || month > 11) {
    status = U_ILLEGAL_ARGUMENT_ERROR;
    return 0;
  }

  auto* cal = getICU4XCalendar(status);
  if (U_FAILURE(status)) {
    return 0;
  }

  UniqueICU4XDate date{};
  if (useMonth) {
    bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
    auto monthCode = MonthCode{month + 1, isLeapMonth};
    date = CreateDateFromCodes(cal, eraName(extendedYear), extendedYear,
                               monthCode, 1);
  } else {
    date =
        CreateDateFrom(cal, eraName(extendedYear), extendedYear, month + 11);
  }
  if (!date) {
    status = U_INTERNAL_PROGRAM_ERROR;
    return 0;
  }

  auto isoDate = ToISODate(date.get());
  int32_t newMoon = MakeDay(isoDate);

  return (newMoon - 1) + kEpochStartAsJulianDay;
}

}  // namespace mozilla::intl::calendar

Messung V0.5 in Prozent
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.