Era.h (8262B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef builtin_temporal_Era_h 8 #define builtin_temporal_Era_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/MathAlgorithms.h" 12 13 #include <initializer_list> 14 #include <stdint.h> 15 #include <string_view> 16 17 #include "jstypes.h" 18 19 #include "builtin/temporal/Calendar.h" 20 21 namespace js::temporal { 22 23 enum class EraCode { 24 // The standard era of a calendar. 25 Standard, 26 27 // The era before the standard era of a calendar. 28 Inverse, 29 30 // Named Japanese eras. 31 Meiji, 32 Taisho, 33 Showa, 34 Heisei, 35 Reiwa, 36 }; 37 38 // static variables in constexpr functions requires C++23 support, so we can't 39 // declare the eras directly in CalendarEras. 40 namespace eras { 41 inline constexpr auto Standard = {EraCode::Standard}; 42 43 inline constexpr auto StandardInverse = {EraCode::Standard, EraCode::Inverse}; 44 45 inline constexpr auto Japanese = { 46 EraCode::Standard, EraCode::Inverse, 47 48 EraCode::Meiji, EraCode::Taisho, EraCode::Showa, 49 EraCode::Heisei, EraCode::Reiwa, 50 }; 51 52 // https://tc39.es/proposal-intl-era-monthcode/#table-eras 53 // 54 // Calendars which don't use eras were omitted. 55 namespace names { 56 using namespace std::literals; 57 58 // Empty placeholder. 59 inline constexpr auto Empty = { 60 ""sv, 61 }; 62 63 inline constexpr auto Buddhist = { 64 "be"sv, 65 }; 66 67 inline constexpr auto Coptic = { 68 "am"sv, 69 }; 70 71 inline constexpr auto EthiopianAmeteAlem = { 72 "aa"sv, 73 }; 74 75 // "Intl era and monthCode" proposal follows CDLR which defines that Amete Alem 76 // era is used for years before the incarnation. This may not match modern 77 // usage, though. For the time being use a single era to check if we get any 78 // user reports to clarify the situation. 79 // 80 // CLDR bug report: https://unicode-org.atlassian.net/browse/CLDR-18739 81 inline constexpr auto Ethiopian = { 82 "am"sv, 83 }; 84 85 inline constexpr auto Gregorian = { 86 "ce"sv, 87 "ad"sv, 88 }; 89 90 inline constexpr auto GregorianInverse = { 91 "bce"sv, 92 "bc"sv, 93 }; 94 95 inline constexpr auto Hebrew = { 96 "am"sv, 97 }; 98 99 inline constexpr auto Indian = { 100 "shaka"sv, 101 }; 102 103 inline constexpr auto Islamic = { 104 "ah"sv, 105 }; 106 107 inline constexpr auto IslamicInverse = { 108 "bh"sv, 109 }; 110 111 inline constexpr auto JapaneseMeiji = { 112 "meiji"sv, 113 }; 114 115 inline constexpr auto JapaneseTaisho = { 116 "taisho"sv, 117 }; 118 119 inline constexpr auto JapaneseShowa = { 120 "showa"sv, 121 }; 122 123 inline constexpr auto JapaneseHeisei = { 124 "heisei"sv, 125 }; 126 127 inline constexpr auto JapaneseReiwa = { 128 "reiwa"sv, 129 }; 130 131 inline constexpr auto Persian = { 132 "ap"sv, 133 }; 134 135 inline constexpr auto ROC = { 136 "roc"sv, 137 }; 138 139 inline constexpr auto ROCInverse = { 140 "broc"sv, 141 }; 142 } // namespace names 143 } // namespace eras 144 145 constexpr auto& CalendarEras(CalendarId calendar) { 146 switch (calendar) { 147 case CalendarId::ISO8601: 148 case CalendarId::Buddhist: 149 case CalendarId::Chinese: 150 case CalendarId::Coptic: 151 case CalendarId::Dangi: 152 case CalendarId::Ethiopian: 153 case CalendarId::EthiopianAmeteAlem: 154 case CalendarId::Hebrew: 155 case CalendarId::Indian: 156 case CalendarId::Persian: 157 return eras::Standard; 158 159 case CalendarId::Gregorian: 160 case CalendarId::IslamicCivil: 161 case CalendarId::IslamicTabular: 162 case CalendarId::IslamicUmmAlQura: 163 case CalendarId::ROC: 164 return eras::StandardInverse; 165 166 case CalendarId::Japanese: 167 return eras::Japanese; 168 } 169 MOZ_CRASH("invalid calendar id"); 170 } 171 172 /** 173 * Return `true` iff the calendar has an inverse era. 174 */ 175 constexpr bool CalendarEraHasInverse(CalendarId calendar) { 176 // More than one era implies an inverse era is used. 177 return CalendarEras(calendar).size() > 1; 178 } 179 180 /** 181 * CalendarSupportsEra ( calendar ) 182 */ 183 constexpr bool CalendarSupportsEra(CalendarId calendar) { 184 switch (calendar) { 185 case CalendarId::ISO8601: 186 case CalendarId::Chinese: 187 case CalendarId::Dangi: 188 return false; 189 190 case CalendarId::Buddhist: 191 case CalendarId::Coptic: 192 case CalendarId::Ethiopian: 193 case CalendarId::EthiopianAmeteAlem: 194 case CalendarId::Hebrew: 195 case CalendarId::Indian: 196 case CalendarId::Persian: 197 case CalendarId::Gregorian: 198 case CalendarId::IslamicCivil: 199 case CalendarId::IslamicTabular: 200 case CalendarId::IslamicUmmAlQura: 201 case CalendarId::ROC: 202 case CalendarId::Japanese: 203 return true; 204 } 205 MOZ_CRASH("invalid calendar id"); 206 } 207 208 constexpr auto& CalendarEraNames(CalendarId calendar, EraCode era) { 209 switch (calendar) { 210 case CalendarId::ISO8601: 211 case CalendarId::Chinese: 212 case CalendarId::Dangi: 213 MOZ_ASSERT(era == EraCode::Standard); 214 return eras::names::Empty; 215 216 case CalendarId::Buddhist: 217 MOZ_ASSERT(era == EraCode::Standard); 218 return eras::names::Buddhist; 219 220 case CalendarId::Coptic: 221 MOZ_ASSERT(era == EraCode::Standard); 222 return eras::names::Coptic; 223 224 case CalendarId::Ethiopian: 225 MOZ_ASSERT(era == EraCode::Standard); 226 return eras::names::Ethiopian; 227 228 case CalendarId::EthiopianAmeteAlem: 229 MOZ_ASSERT(era == EraCode::Standard); 230 return eras::names::EthiopianAmeteAlem; 231 232 case CalendarId::Hebrew: 233 MOZ_ASSERT(era == EraCode::Standard); 234 return eras::names::Hebrew; 235 236 case CalendarId::Indian: 237 MOZ_ASSERT(era == EraCode::Standard); 238 return eras::names::Indian; 239 240 case CalendarId::Persian: 241 MOZ_ASSERT(era == EraCode::Standard); 242 return eras::names::Persian; 243 244 case CalendarId::Gregorian: { 245 MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse); 246 return era == EraCode::Standard ? eras::names::Gregorian 247 : eras::names::GregorianInverse; 248 } 249 250 case CalendarId::IslamicCivil: 251 case CalendarId::IslamicTabular: 252 case CalendarId::IslamicUmmAlQura: { 253 MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse); 254 return era == EraCode::Standard ? eras::names::Islamic 255 : eras::names::IslamicInverse; 256 } 257 258 case CalendarId::Japanese: { 259 switch (era) { 260 case EraCode::Standard: 261 return eras::names::Gregorian; 262 case EraCode::Inverse: 263 return eras::names::GregorianInverse; 264 case EraCode::Meiji: 265 return eras::names::JapaneseMeiji; 266 case EraCode::Taisho: 267 return eras::names::JapaneseTaisho; 268 case EraCode::Showa: 269 return eras::names::JapaneseShowa; 270 case EraCode::Heisei: 271 return eras::names::JapaneseHeisei; 272 case EraCode::Reiwa: 273 return eras::names::JapaneseReiwa; 274 } 275 break; 276 } 277 278 case CalendarId::ROC: { 279 MOZ_ASSERT(era == EraCode::Standard || era == EraCode::Inverse); 280 return era == EraCode::Standard ? eras::names::ROC 281 : eras::names::ROCInverse; 282 } 283 } 284 MOZ_CRASH("invalid era"); 285 } 286 287 constexpr auto CalendarEraName(CalendarId calendar, EraCode era) { 288 auto& names = CalendarEraNames(calendar, era); 289 MOZ_ASSERT(names.size() > 0); 290 return *names.begin(); 291 } 292 293 /** 294 * CalendarHasMidYearEras ( calendar ) 295 */ 296 constexpr bool CalendarHasMidYearEras(CalendarId calendar) { 297 // Steps 1-2. 298 // 299 // Japanese eras can start in the middle of the year. All other calendars 300 // start their eras at year boundaries. (Or don't have eras at all.) 301 return calendar == CalendarId::Japanese; 302 } 303 304 constexpr bool IsJapaneseEraName(EraCode era) { 305 switch (era) { 306 case EraCode::Standard: 307 case EraCode::Inverse: 308 return false; 309 case EraCode::Meiji: 310 case EraCode::Taisho: 311 case EraCode::Showa: 312 case EraCode::Heisei: 313 case EraCode::Reiwa: 314 return true; 315 } 316 MOZ_CRASH("invalid era"); 317 } 318 319 struct EraYear { 320 EraCode era = EraCode::Standard; 321 int32_t year = 0; 322 }; 323 324 constexpr EraYear CalendarEraYear(CalendarId calendar, int32_t year) { 325 if (year > 0 || !CalendarEraHasInverse(calendar)) { 326 return EraYear{EraCode::Standard, year}; 327 } 328 return EraYear{EraCode::Inverse, int32_t(mozilla::Abs(year) + 1)}; 329 } 330 331 } // namespace js::temporal 332 333 #endif /* builtin_temporal_Era_h */