Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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
#ifndef builtin_temporal_Era_h
#define builtin_temporal_Era_h
#include "mozilla/Assertions.h"
#include "mozilla/MathAlgorithms.h"
#include <initializer_list>
#include <stddef.h>
#include <stdint.h>
#include <string_view>
#include "jstypes.h"
#include "builtin/temporal/Calendar.h"
namespace js::temporal {
enum class EraCode {
// The standard era of a calendar.
Standard,
// The era before the standard era of a calendar.
Inverse,
// Named Japanese eras.
Meiji,
Taisho,
Showa,
Heisei,
Reiwa,
};
// static variables in constexpr functions requires C++23 support, so we can't
// declare the eras directly in CalendarEras.
namespace eras {
inline constexpr auto Standard = {EraCode::Standard};
inline constexpr auto StandardInverse = {EraCode::Standard, EraCode::Inverse};
inline constexpr auto Japanese = {
EraCode::Standard, EraCode::Inverse,
EraCode::Meiji, EraCode::Taisho, EraCode::Showa,
EraCode::Heisei, EraCode::Reiwa,
};
//
// Calendars which don't use eras were omitted.
namespace names {
using namespace std::literals;
// Empty placeholder.
inline constexpr auto Empty = {
""sv,
};
inline constexpr auto Buddhist = {
"be"sv,
};
inline constexpr auto Coptic = {
"am"sv,
};
inline constexpr auto EthiopianAmeteAlem = {
"aa"sv,
};
// "Intl era and monthCode" proposal follows CDLR which defines that Amete Alem
// era is used for years before the incarnation. This may not match modern
// usage, though. For the time being use a single era to check if we get any
// user reports to clarify the situation.
//
inline constexpr auto Ethiopian = {
"am"sv,
};
inline constexpr auto Gregorian = {
"ce"sv,
"ad"sv,
};
inline constexpr auto GregorianInverse = {
"bce"sv,
"bc"sv,
};
inline constexpr auto Hebrew = {
"am"sv,
};
inline constexpr auto Indian = {
"shaka"sv,
};
inline constexpr auto Islamic = {
"ah"sv,
};
inline constexpr auto IslamicInverse = {
"bh"sv,
};
inline constexpr auto JapaneseMeiji = {
"meiji"sv,
};
inline constexpr auto JapaneseTaisho = {
"taisho"sv,
};
inline constexpr auto JapaneseShowa = {
"showa"sv,
};
inline constexpr auto JapaneseHeisei = {
"heisei"sv,
};
inline constexpr auto JapaneseReiwa = {
"reiwa"sv,
};
inline constexpr auto Persian = {
"ap"sv,
};
inline constexpr auto ROC = {
"roc"sv,
};
inline constexpr auto ROCInverse = {
"broc"sv,
};
} // namespace names
} // namespace eras
constexpr auto& CalendarEras(CalendarId calendar) {
switch (calendar) {
case CalendarId::ISO8601:
case CalendarId::Buddhist:
case CalendarId::Chinese:
case CalendarId::Coptic:
case CalendarId::Dangi:
case CalendarId::Ethiopian:
case CalendarId::EthiopianAmeteAlem:
case CalendarId::Hebrew:
case CalendarId::Indian:
case CalendarId::Persian:
return eras::Standard;
case CalendarId::Gregorian:
case CalendarId::IslamicCivil:
case CalendarId::IslamicTabular:
case CalendarId::IslamicUmmAlQura:
case CalendarId::ROC:
return eras::StandardInverse;
case CalendarId::Japanese:
return eras::Japanese;
}
MOZ_CRASH("invalid calendar id");
}
/**
* Return `true` iff the calendar has an inverse era.
*/
constexpr bool CalendarEraHasInverse(CalendarId calendar) {
// More than one era implies an inverse era is used.
return CalendarEras(calendar).size() > 1;
}
/**
* CalendarSupportsEra ( calendar )
*/
constexpr bool CalendarSupportsEra(CalendarId calendar) {
switch (calendar) {
case CalendarId::ISO8601:
case CalendarId::Chinese:
case CalendarId::Dangi:
return false;
case CalendarId::Buddhist:
case CalendarId::Coptic:
case CalendarId::Ethiopian:
case CalendarId::EthiopianAmeteAlem:
case CalendarId::Hebrew:
case CalendarId::Indian:
case CalendarId::Persian:
case CalendarId::Gregorian:
case CalendarId::IslamicCivil:
case CalendarId::IslamicTabular:
case CalendarId::IslamicUmmAlQura:
case CalendarId::ROC:
case CalendarId::Japanese:
return true;
}
MOZ_CRASH("invalid calendar id");
}
constexpr auto& CalendarEraNames(CalendarId calendar, EraCode era) {
switch (calendar) {
case CalendarId::ISO8601:
case CalendarId::Chinese:
case CalendarId::Dangi:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Empty;
case CalendarId::Buddhist:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Buddhist;
case CalendarId::Coptic:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Coptic;
case CalendarId::Ethiopian:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Ethiopian;
case CalendarId::EthiopianAmeteAlem:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::EthiopianAmeteAlem;
case CalendarId::Hebrew:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Hebrew;
case CalendarId::Indian:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Indian;
case CalendarId::Persian:
MOZ_ASSERT(era == EraCode::Standard);
return eras::names::Persian;
case CalendarId::Gregorian: {
MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse);
return era == EraCode::Standard ? eras::names::Gregorian
: eras::names::GregorianInverse;
}
case CalendarId::IslamicCivil:
case CalendarId::IslamicTabular:
case CalendarId::IslamicUmmAlQura: {
MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse);
return era == EraCode::Standard ? eras::names::Islamic
: eras::names::IslamicInverse;
}
case CalendarId::Japanese: {
switch (era) {
case EraCode::Standard:
return eras::names::Gregorian;
case EraCode::Inverse:
return eras::names::GregorianInverse;
case EraCode::Meiji:
return eras::names::JapaneseMeiji;
case EraCode::Taisho:
return eras::names::JapaneseTaisho;
case EraCode::Showa:
return eras::names::JapaneseShowa;
case EraCode::Heisei:
return eras::names::JapaneseHeisei;
case EraCode::Reiwa:
return eras::names::JapaneseReiwa;
}
break;
}
case CalendarId::ROC: {
MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse);
return era == EraCode::Standard ? eras::names::ROC
: eras::names::ROCInverse;
}
}
MOZ_CRASH("invalid era");
}
constexpr auto CalendarEraName(CalendarId calendar, EraCode era) {
auto& names = CalendarEraNames(calendar, era);
MOZ_ASSERT(names.size() > 0);
return *names.begin();
}
/**
* CalendarHasMidYearEras ( calendar )
*/
constexpr bool CalendarHasMidYearEras(CalendarId calendar) {
// Steps 1-2.
//
// Japanese eras can start in the middle of the year. All other calendars
// start their eras at year boundaries. (Or don't have eras at all.)
return calendar == CalendarId::Japanese;
}
constexpr bool IsJapaneseEraName(EraCode era) {
switch (era) {
case EraCode::Standard:
case EraCode::Inverse:
return false;
case EraCode::Meiji:
case EraCode::Taisho:
case EraCode::Showa:
case EraCode::Heisei:
case EraCode::Reiwa:
return true;
}
MOZ_CRASH("invalid era");
}
struct EraYear {
EraCode era = EraCode::Standard;
int32_t year = 0;
};
constexpr EraYear CalendarEraYear(CalendarId calendar, int32_t year) {
if (year > 0 || !CalendarEraHasInverse(calendar)) {
return EraYear{EraCode::Standard, year};
}
return EraYear{EraCode::Inverse, int32_t(mozilla::Abs(year) + 1)};
}
} // namespace js::temporal
#endif /* builtin_temporal_Era_h */