diff options
Diffstat (limited to 'externals')
| -rw-r--r-- | externals/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | externals/tz/tz/tz.cpp | 1636 | ||||
| -rw-r--r-- | externals/tz/tz/tz.h | 81 |
3 files changed, 1720 insertions, 0 deletions
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 407c5c640..15b444338 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -178,6 +178,9 @@ if (NOT TARGET stb::headers) | |||
| 178 | add_library(stb::headers ALIAS stb) | 178 | add_library(stb::headers ALIAS stb) |
| 179 | endif() | 179 | endif() |
| 180 | 180 | ||
| 181 | add_library(tz tz/tz/tz.cpp) | ||
| 182 | target_include_directories(tz PUBLIC ./tz) | ||
| 183 | |||
| 181 | add_library(bc_decoder bc_decoder/bc_decoder.cpp) | 184 | add_library(bc_decoder bc_decoder/bc_decoder.cpp) |
| 182 | target_include_directories(bc_decoder PUBLIC ./bc_decoder) | 185 | target_include_directories(bc_decoder PUBLIC ./bc_decoder) |
| 183 | 186 | ||
diff --git a/externals/tz/tz/tz.cpp b/externals/tz/tz/tz.cpp new file mode 100644 index 000000000..0c8b68217 --- /dev/null +++ b/externals/tz/tz/tz.cpp | |||
| @@ -0,0 +1,1636 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 1996 Arthur David Olson | ||
| 3 | // SPDX-License-Identifier: BSD-2-Clause | ||
| 4 | |||
| 5 | #include <climits> | ||
| 6 | #include <cstring> | ||
| 7 | #include <ctime> | ||
| 8 | |||
| 9 | #include "tz.h" | ||
| 10 | |||
| 11 | namespace Tz { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | #define EINVAL 22 | ||
| 15 | |||
| 16 | static Rule gmtmem{}; | ||
| 17 | static Rule* const gmtptr = &gmtmem; | ||
| 18 | |||
| 19 | struct TzifHeader { | ||
| 20 | std::array<char, 4> tzh_magic; // "TZif" | ||
| 21 | std::array<char, 1> tzh_version; | ||
| 22 | std::array<char, 15> tzh_reserved; | ||
| 23 | std::array<char, 4> tzh_ttisutcnt; | ||
| 24 | std::array<char, 4> tzh_ttisstdcnt; | ||
| 25 | std::array<char, 4> tzh_leapcnt; | ||
| 26 | std::array<char, 4> tzh_timecnt; | ||
| 27 | std::array<char, 4> tzh_typecnt; | ||
| 28 | std::array<char, 4> tzh_charcnt; | ||
| 29 | }; | ||
| 30 | static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader has the wrong size!"); | ||
| 31 | |||
| 32 | struct local_storage { | ||
| 33 | // Binary layout: | ||
| 34 | // char buf[2 * sizeof(TzifHeader) + 2 * sizeof(Rule) + 4 * TZ_MAX_TIMES]; | ||
| 35 | std::span<const u8> binary; | ||
| 36 | Rule state; | ||
| 37 | }; | ||
| 38 | static local_storage tzloadbody_local_storage; | ||
| 39 | |||
| 40 | enum rtype : s32 { | ||
| 41 | JULIAN_DAY = 0, | ||
| 42 | DAY_OF_YEAR = 1, | ||
| 43 | MONTH_NTH_DAY_OF_WEEK = 2, | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct tzrule { | ||
| 47 | rtype r_type; | ||
| 48 | int r_day; | ||
| 49 | int r_week; | ||
| 50 | int r_mon; | ||
| 51 | s64 r_time; | ||
| 52 | }; | ||
| 53 | static_assert(sizeof(tzrule) == 0x18, "tzrule has the wrong size!"); | ||
| 54 | |||
| 55 | constexpr static char UNSPEC[] = "-00"; | ||
| 56 | constexpr static char TZDEFRULESTRING[] = ",M3.2.0,M11.1.0"; | ||
| 57 | |||
| 58 | enum { | ||
| 59 | SECSPERMIN = 60, | ||
| 60 | MINSPERHOUR = 60, | ||
| 61 | SECSPERHOUR = SECSPERMIN * MINSPERHOUR, | ||
| 62 | HOURSPERDAY = 24, | ||
| 63 | DAYSPERWEEK = 7, | ||
| 64 | DAYSPERNYEAR = 365, | ||
| 65 | DAYSPERLYEAR = DAYSPERNYEAR + 1, | ||
| 66 | MONSPERYEAR = 12, | ||
| 67 | YEARSPERREPEAT = 400 /* years before a Gregorian repeat */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | #define SECSPERDAY ((s64)SECSPERHOUR * HOURSPERDAY) | ||
| 71 | |||
| 72 | #define DAYSPERREPEAT ((s64)400 * 365 + 100 - 4 + 1) | ||
| 73 | #define SECSPERREPEAT ((int_fast64_t)DAYSPERREPEAT * SECSPERDAY) | ||
| 74 | #define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT) | ||
| 75 | |||
| 76 | enum { | ||
| 77 | TM_SUNDAY, | ||
| 78 | TM_MONDAY, | ||
| 79 | TM_TUESDAY, | ||
| 80 | TM_WEDNESDAY, | ||
| 81 | TM_THURSDAY, | ||
| 82 | TM_FRIDAY, | ||
| 83 | TM_SATURDAY, | ||
| 84 | }; | ||
| 85 | |||
| 86 | enum { | ||
| 87 | TM_JANUARY, | ||
| 88 | TM_FEBRUARY, | ||
| 89 | TM_MARCH, | ||
| 90 | TM_APRIL, | ||
| 91 | TM_MAY, | ||
| 92 | TM_JUNE, | ||
| 93 | TM_JULY, | ||
| 94 | TM_AUGUST, | ||
| 95 | TM_SEPTEMBER, | ||
| 96 | TM_OCTOBER, | ||
| 97 | TM_NOVEMBER, | ||
| 98 | TM_DECEMBER, | ||
| 99 | }; | ||
| 100 | |||
| 101 | constexpr s32 TM_YEAR_BASE = 1900; | ||
| 102 | constexpr s32 TM_WDAY_BASE = TM_MONDAY; | ||
| 103 | constexpr s32 EPOCH_YEAR = 1970; | ||
| 104 | constexpr s32 EPOCH_WDAY = TM_THURSDAY; | ||
| 105 | |||
| 106 | #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) | ||
| 107 | |||
| 108 | static constexpr std::array<std::array<int, MONSPERYEAR>, 2> mon_lengths = { { | ||
| 109 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, | ||
| 110 | {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, | ||
| 111 | } }; | ||
| 112 | |||
| 113 | static constexpr std::array<int, 2> year_lengths = { | ||
| 114 | DAYSPERNYEAR, | ||
| 115 | DAYSPERLYEAR, | ||
| 116 | }; | ||
| 117 | |||
| 118 | constexpr static time_t leaps_thru_end_of_nonneg(time_t y) { | ||
| 119 | return y / 4 - y / 100 + y / 400; | ||
| 120 | } | ||
| 121 | |||
| 122 | constexpr static time_t leaps_thru_end_of(time_t y) { | ||
| 123 | return (y < 0 ? -1 - leaps_thru_end_of_nonneg(-1 - y) : leaps_thru_end_of_nonneg(y)); | ||
| 124 | } | ||
| 125 | |||
| 126 | #define TWOS_COMPLEMENT(t) ((t) ~(t)0 < 0) | ||
| 127 | |||
| 128 | s32 detzcode(const char* const codep) { | ||
| 129 | s32 result; | ||
| 130 | int i; | ||
| 131 | s32 one = 1; | ||
| 132 | s32 halfmaxval = one << (32 - 2); | ||
| 133 | s32 maxval = halfmaxval - 1 + halfmaxval; | ||
| 134 | s32 minval = -1 - maxval; | ||
| 135 | |||
| 136 | result = codep[0] & 0x7f; | ||
| 137 | for (i = 1; i < 4; ++i) { | ||
| 138 | result = (result << 8) | (codep[i] & 0xff); | ||
| 139 | } | ||
| 140 | |||
| 141 | if (codep[0] & 0x80) { | ||
| 142 | /* Do two's-complement negation even on non-two's-complement machines. | ||
| 143 | If the result would be minval - 1, return minval. */ | ||
| 144 | result -= !TWOS_COMPLEMENT(s32) && result != 0; | ||
| 145 | result += minval; | ||
| 146 | } | ||
| 147 | return result; | ||
| 148 | } | ||
| 149 | |||
| 150 | int_fast64_t detzcode64(const char* const codep) { | ||
| 151 | int_fast64_t result; | ||
| 152 | int i; | ||
| 153 | int_fast64_t one = 1; | ||
| 154 | int_fast64_t halfmaxval = one << (64 - 2); | ||
| 155 | int_fast64_t maxval = halfmaxval - 1 + halfmaxval; | ||
| 156 | int_fast64_t minval = -static_cast<int_fast64_t>(TWOS_COMPLEMENT(int_fast64_t)) - maxval; | ||
| 157 | |||
| 158 | result = codep[0] & 0x7f; | ||
| 159 | for (i = 1; i < 8; ++i) { | ||
| 160 | result = (result << 8) | (codep[i] & 0xff); | ||
| 161 | } | ||
| 162 | |||
| 163 | if (codep[0] & 0x80) { | ||
| 164 | /* Do two's-complement negation even on non-two's-complement machines. | ||
| 165 | If the result would be minval - 1, return minval. */ | ||
| 166 | result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0; | ||
| 167 | result += minval; | ||
| 168 | } | ||
| 169 | return result; | ||
| 170 | } | ||
| 171 | |||
| 172 | /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ | ||
| 173 | constexpr void init_ttinfo(ttinfo* s, s64 utoff, bool isdst, int desigidx) { | ||
| 174 | s->tt_utoff = static_cast<s32>(utoff); | ||
| 175 | s->tt_isdst = isdst; | ||
| 176 | s->tt_desigidx = desigidx; | ||
| 177 | s->tt_ttisstd = false; | ||
| 178 | s->tt_ttisut = false; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Return true if SP's time type I does not specify local time. */ | ||
| 182 | bool ttunspecified(struct Rule const* sp, int i) { | ||
| 183 | char const* abbr = &sp->chars[sp->ttis[i].tt_desigidx]; | ||
| 184 | /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */ | ||
| 185 | return memcmp(abbr, UNSPEC, sizeof(UNSPEC)) == 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | bool typesequiv(const Rule* sp, int a, int b) { | ||
| 189 | bool result; | ||
| 190 | |||
| 191 | if (sp == nullptr || a < 0 || a >= sp->typecnt || b < 0 || b >= sp->typecnt) { | ||
| 192 | result = false; | ||
| 193 | } | ||
| 194 | else { | ||
| 195 | /* Compare the relevant members of *AP and *BP. | ||
| 196 | Ignore tt_ttisstd and tt_ttisut, as they are | ||
| 197 | irrelevant now and counting them could cause | ||
| 198 | sp->goahead to mistakenly remain false. */ | ||
| 199 | const ttinfo* ap = &sp->ttis[a]; | ||
| 200 | const ttinfo* bp = &sp->ttis[b]; | ||
| 201 | result = (ap->tt_utoff == bp->tt_utoff && ap->tt_isdst == bp->tt_isdst && | ||
| 202 | (strcmp(&sp->chars[ap->tt_desigidx], &sp->chars[bp->tt_desigidx]) == 0)); | ||
| 203 | } | ||
| 204 | return result; | ||
| 205 | } | ||
| 206 | |||
| 207 | constexpr const char* getqzname(const char* strp, const int delim) { | ||
| 208 | int c; | ||
| 209 | |||
| 210 | while ((c = *strp) != '\0' && c != delim) { | ||
| 211 | ++strp; | ||
| 212 | } | ||
| 213 | return strp; | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Is C an ASCII digit? */ | ||
| 217 | constexpr bool is_digit(char c) { | ||
| 218 | return '0' <= c && c <= '9'; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* | ||
| 222 | ** Given a pointer into a timezone string, scan until a character that is not | ||
| 223 | ** a valid character in a time zone abbreviation is found. | ||
| 224 | ** Return a pointer to that character. | ||
| 225 | */ | ||
| 226 | |||
| 227 | constexpr const char* getzname(const char* strp) { | ||
| 228 | char c; | ||
| 229 | |||
| 230 | while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') { | ||
| 231 | ++strp; | ||
| 232 | } | ||
| 233 | return strp; | ||
| 234 | } | ||
| 235 | |||
| 236 | static const char* getnum(const char* strp, int* const nump, const int min, const int max) { | ||
| 237 | char c; | ||
| 238 | int num; | ||
| 239 | |||
| 240 | if (strp == nullptr || !is_digit(c = *strp)) { | ||
| 241 | return nullptr; | ||
| 242 | } | ||
| 243 | num = 0; | ||
| 244 | do { | ||
| 245 | num = num * 10 + (c - '0'); | ||
| 246 | if (num > max) { | ||
| 247 | return nullptr; /* illegal value */ | ||
| 248 | } | ||
| 249 | c = *++strp; | ||
| 250 | } while (is_digit(c)); | ||
| 251 | if (num < min) { | ||
| 252 | return nullptr; /* illegal value */ | ||
| 253 | } | ||
| 254 | *nump = num; | ||
| 255 | return strp; | ||
| 256 | } | ||
| 257 | |||
| 258 | /* | ||
| 259 | ** Given a pointer into a timezone string, extract a number of seconds, | ||
| 260 | ** in hh[:mm[:ss]] form, from the string. | ||
| 261 | ** If any error occurs, return NULL. | ||
| 262 | ** Otherwise, return a pointer to the first character not part of the number | ||
| 263 | ** of seconds. | ||
| 264 | */ | ||
| 265 | |||
| 266 | const char* getsecs(const char* strp, s64* const secsp) { | ||
| 267 | int num; | ||
| 268 | s64 secsperhour = SECSPERHOUR; | ||
| 269 | |||
| 270 | /* | ||
| 271 | ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like | ||
| 272 | ** "M10.4.6/26", which does not conform to Posix, | ||
| 273 | ** but which specifies the equivalent of | ||
| 274 | ** "02:00 on the first Sunday on or after 23 Oct". | ||
| 275 | */ | ||
| 276 | strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); | ||
| 277 | if (strp == nullptr) { | ||
| 278 | return nullptr; | ||
| 279 | } | ||
| 280 | *secsp = num * secsperhour; | ||
| 281 | if (*strp == ':') { | ||
| 282 | ++strp; | ||
| 283 | strp = getnum(strp, &num, 0, MINSPERHOUR - 1); | ||
| 284 | if (strp == nullptr) { | ||
| 285 | return nullptr; | ||
| 286 | } | ||
| 287 | *secsp += num * SECSPERMIN; | ||
| 288 | if (*strp == ':') { | ||
| 289 | ++strp; | ||
| 290 | /* 'SECSPERMIN' allows for leap seconds. */ | ||
| 291 | strp = getnum(strp, &num, 0, SECSPERMIN); | ||
| 292 | if (strp == nullptr) { | ||
| 293 | return nullptr; | ||
| 294 | } | ||
| 295 | *secsp += num; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | return strp; | ||
| 299 | } | ||
| 300 | |||
| 301 | /* | ||
| 302 | ** Given a pointer into a timezone string, extract an offset, in | ||
| 303 | ** [+-]hh[:mm[:ss]] form, from the string. | ||
| 304 | ** If any error occurs, return NULL. | ||
| 305 | ** Otherwise, return a pointer to the first character not part of the time. | ||
| 306 | */ | ||
| 307 | |||
| 308 | const char* getoffset(const char* strp, s64* const offsetp) { | ||
| 309 | bool neg = false; | ||
| 310 | |||
| 311 | if (*strp == '-') { | ||
| 312 | neg = true; | ||
| 313 | ++strp; | ||
| 314 | } | ||
| 315 | else if (*strp == '+') { | ||
| 316 | ++strp; | ||
| 317 | } | ||
| 318 | strp = getsecs(strp, offsetp); | ||
| 319 | if (strp == nullptr) { | ||
| 320 | return nullptr; /* illegal time */ | ||
| 321 | } | ||
| 322 | if (neg) { | ||
| 323 | *offsetp = -*offsetp; | ||
| 324 | } | ||
| 325 | return strp; | ||
| 326 | } | ||
| 327 | |||
| 328 | constexpr const char* getrule(const char* strp, tzrule* const rulep) { | ||
| 329 | if (*strp == 'J') { | ||
| 330 | /* | ||
| 331 | ** Julian day. | ||
| 332 | */ | ||
| 333 | rulep->r_type = JULIAN_DAY; | ||
| 334 | ++strp; | ||
| 335 | strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); | ||
| 336 | } | ||
| 337 | else if (*strp == 'M') { | ||
| 338 | /* | ||
| 339 | ** Month, week, day. | ||
| 340 | */ | ||
| 341 | rulep->r_type = MONTH_NTH_DAY_OF_WEEK; | ||
| 342 | ++strp; | ||
| 343 | strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); | ||
| 344 | if (strp == nullptr) { | ||
| 345 | return nullptr; | ||
| 346 | } | ||
| 347 | if (*strp++ != '.') { | ||
| 348 | return nullptr; | ||
| 349 | } | ||
| 350 | strp = getnum(strp, &rulep->r_week, 1, 5); | ||
| 351 | if (strp == nullptr) { | ||
| 352 | return nullptr; | ||
| 353 | } | ||
| 354 | if (*strp++ != '.') { | ||
| 355 | return nullptr; | ||
| 356 | } | ||
| 357 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); | ||
| 358 | } | ||
| 359 | else if (is_digit(*strp)) { | ||
| 360 | /* | ||
| 361 | ** Day of year. | ||
| 362 | */ | ||
| 363 | rulep->r_type = DAY_OF_YEAR; | ||
| 364 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); | ||
| 365 | } | ||
| 366 | else { | ||
| 367 | return nullptr; | ||
| 368 | } /* invalid format */ | ||
| 369 | if (strp == nullptr) { | ||
| 370 | return nullptr; | ||
| 371 | } | ||
| 372 | if (*strp == '/') { | ||
| 373 | /* | ||
| 374 | ** Time specified. | ||
| 375 | */ | ||
| 376 | ++strp; | ||
| 377 | strp = getoffset(strp, &rulep->r_time); | ||
| 378 | } | ||
| 379 | else { | ||
| 380 | rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ | ||
| 381 | } | ||
| 382 | return strp; | ||
| 383 | } | ||
| 384 | |||
| 385 | constexpr bool increment_overflow(int* ip, int j) { | ||
| 386 | int const i = *ip; | ||
| 387 | |||
| 388 | /* | ||
| 389 | ** If i >= 0 there can only be overflow if i + j > INT_MAX | ||
| 390 | ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. | ||
| 391 | ** If i < 0 there can only be overflow if i + j < INT_MIN | ||
| 392 | ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. | ||
| 393 | */ | ||
| 394 | if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) { | ||
| 395 | return true; | ||
| 396 | } | ||
| 397 | *ip += j; | ||
| 398 | return false; | ||
| 399 | } | ||
| 400 | |||
| 401 | constexpr bool increment_overflow32(s64* const lp, int const m) { | ||
| 402 | s64 const l = *lp; | ||
| 403 | |||
| 404 | if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) | ||
| 405 | return true; | ||
| 406 | *lp += m; | ||
| 407 | return false; | ||
| 408 | } | ||
| 409 | |||
| 410 | constexpr bool increment_overflow_time(time_t* tp, s64 j) { | ||
| 411 | /* | ||
| 412 | ** This is like | ||
| 413 | ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', | ||
| 414 | ** except that it does the right thing even if *tp + j would overflow. | ||
| 415 | */ | ||
| 416 | if (!(j < 0 ? (std::is_signed_v<time_t> ? TIME_T_MIN - j <= *tp : -1 - j < *tp) | ||
| 417 | : *tp <= TIME_T_MAX - j)) { | ||
| 418 | return true; | ||
| 419 | } | ||
| 420 | *tp += j; | ||
| 421 | return false; | ||
| 422 | } | ||
| 423 | |||
| 424 | CalendarTimeInternal* timesub(const time_t* timep, s64 offset, const Rule* sp, | ||
| 425 | CalendarTimeInternal* tmp) { | ||
| 426 | time_t tdays; | ||
| 427 | const int* ip; | ||
| 428 | s64 idays, rem, dayoff, dayrem; | ||
| 429 | time_t y; | ||
| 430 | |||
| 431 | /* Calculate the year, avoiding integer overflow even if | ||
| 432 | time_t is unsigned. */ | ||
| 433 | tdays = *timep / SECSPERDAY; | ||
| 434 | rem = *timep % SECSPERDAY; | ||
| 435 | rem += offset % SECSPERDAY + 3 * SECSPERDAY; | ||
| 436 | dayoff = offset / SECSPERDAY + rem / SECSPERDAY - 3; | ||
| 437 | rem %= SECSPERDAY; | ||
| 438 | /* y = (EPOCH_YEAR | ||
| 439 | + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), | ||
| 440 | sans overflow. But calculate against 1570 (EPOCH_YEAR - | ||
| 441 | YEARSPERREPEAT) instead of against 1970 so that things work | ||
| 442 | for localtime values before 1970 when time_t is unsigned. */ | ||
| 443 | dayrem = tdays % DAYSPERREPEAT; | ||
| 444 | dayrem += dayoff % DAYSPERREPEAT; | ||
| 445 | y = (EPOCH_YEAR - YEARSPERREPEAT + | ||
| 446 | ((1ull + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT - ((dayrem % DAYSPERREPEAT) < 0) + | ||
| 447 | tdays / DAYSPERREPEAT) * | ||
| 448 | YEARSPERREPEAT)); | ||
| 449 | /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */ | ||
| 450 | idays = tdays % DAYSPERREPEAT; | ||
| 451 | idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT; | ||
| 452 | idays %= DAYSPERREPEAT; | ||
| 453 | /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */ | ||
| 454 | while (year_lengths[isleap(y)] <= idays) { | ||
| 455 | s64 tdelta = idays / DAYSPERLYEAR; | ||
| 456 | s64 ydelta = tdelta + !tdelta; | ||
| 457 | time_t newy = y + ydelta; | ||
| 458 | int leapdays; | ||
| 459 | leapdays = static_cast<s32>(leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1)); | ||
| 460 | idays -= ydelta * DAYSPERNYEAR; | ||
| 461 | idays -= leapdays; | ||
| 462 | y = newy; | ||
| 463 | } | ||
| 464 | |||
| 465 | if constexpr (!std::is_signed_v<time_t> && y < TM_YEAR_BASE) { | ||
| 466 | int signed_y = static_cast<s32>(y); | ||
| 467 | tmp->tm_year = signed_y - TM_YEAR_BASE; | ||
| 468 | } | ||
| 469 | else if ((!std::is_signed_v<time_t> || std::numeric_limits<s32>::min() + TM_YEAR_BASE <= y) && | ||
| 470 | y - TM_YEAR_BASE <= std::numeric_limits<s32>::max()) { | ||
| 471 | tmp->tm_year = static_cast<s32>(y - TM_YEAR_BASE); | ||
| 472 | } | ||
| 473 | else { | ||
| 474 | // errno = EOVERFLOW; | ||
| 475 | return nullptr; | ||
| 476 | } | ||
| 477 | |||
| 478 | tmp->tm_yday = static_cast<s32>(idays); | ||
| 479 | /* | ||
| 480 | ** The "extra" mods below avoid overflow problems. | ||
| 481 | */ | ||
| 482 | tmp->tm_wday = static_cast<s32>( | ||
| 483 | TM_WDAY_BASE + ((tmp->tm_year % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK)) + | ||
| 484 | leaps_thru_end_of(y - 1) - leaps_thru_end_of(TM_YEAR_BASE - 1) + idays); | ||
| 485 | tmp->tm_wday %= DAYSPERWEEK; | ||
| 486 | if (tmp->tm_wday < 0) { | ||
| 487 | tmp->tm_wday += DAYSPERWEEK; | ||
| 488 | } | ||
| 489 | tmp->tm_hour = static_cast<s32>(rem / SECSPERHOUR); | ||
| 490 | rem %= SECSPERHOUR; | ||
| 491 | tmp->tm_min = static_cast<s32>(rem / SECSPERMIN); | ||
| 492 | tmp->tm_sec = static_cast<s32>(rem % SECSPERMIN); | ||
| 493 | |||
| 494 | ip = mon_lengths[isleap(y)].data(); | ||
| 495 | for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) { | ||
| 496 | idays -= ip[tmp->tm_mon]; | ||
| 497 | } | ||
| 498 | tmp->tm_mday = static_cast<s32>(idays + 1); | ||
| 499 | tmp->tm_isdst = 0; | ||
| 500 | return tmp; | ||
| 501 | } | ||
| 502 | |||
| 503 | CalendarTimeInternal* gmtsub([[maybe_unused]] Rule const* sp, time_t const* timep, | ||
| 504 | s64 offset, CalendarTimeInternal* tmp) { | ||
| 505 | CalendarTimeInternal* result; | ||
| 506 | |||
| 507 | result = timesub(timep, offset, gmtptr, tmp); | ||
| 508 | return result; | ||
| 509 | } | ||
| 510 | |||
| 511 | CalendarTimeInternal* localsub(Rule const* sp, time_t const* timep, s64 setname, | ||
| 512 | CalendarTimeInternal* const tmp) { | ||
| 513 | const ttinfo* ttisp; | ||
| 514 | int i; | ||
| 515 | CalendarTimeInternal* result; | ||
| 516 | const time_t t = *timep; | ||
| 517 | |||
| 518 | if (sp == nullptr) { | ||
| 519 | /* Don't bother to set tzname etc.; tzset has already done it. */ | ||
| 520 | return gmtsub(gmtptr, timep, 0, tmp); | ||
| 521 | } | ||
| 522 | if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) { | ||
| 523 | time_t newt; | ||
| 524 | time_t seconds; | ||
| 525 | time_t years; | ||
| 526 | |||
| 527 | if (t < sp->ats[0]) { | ||
| 528 | seconds = sp->ats[0] - t; | ||
| 529 | } | ||
| 530 | else { | ||
| 531 | seconds = t - sp->ats[sp->timecnt - 1]; | ||
| 532 | } | ||
| 533 | --seconds; | ||
| 534 | |||
| 535 | /* Beware integer overflow, as SECONDS might | ||
| 536 | be close to the maximum time_t. */ | ||
| 537 | years = seconds / SECSPERREPEAT * YEARSPERREPEAT; | ||
| 538 | seconds = years * AVGSECSPERYEAR; | ||
| 539 | years += YEARSPERREPEAT; | ||
| 540 | if (t < sp->ats[0]) { | ||
| 541 | newt = t + seconds + SECSPERREPEAT; | ||
| 542 | } | ||
| 543 | else { | ||
| 544 | newt = t - seconds - SECSPERREPEAT; | ||
| 545 | } | ||
| 546 | |||
| 547 | if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) { | ||
| 548 | return nullptr; /* "cannot happen" */ | ||
| 549 | } | ||
| 550 | result = localsub(sp, &newt, setname, tmp); | ||
| 551 | if (result) { | ||
| 552 | int_fast64_t newy; | ||
| 553 | |||
| 554 | newy = result->tm_year; | ||
| 555 | if (t < sp->ats[0]) { | ||
| 556 | newy -= years; | ||
| 557 | } | ||
| 558 | else { | ||
| 559 | newy += years; | ||
| 560 | } | ||
| 561 | if (!(std::numeric_limits<s32>::min() <= newy && | ||
| 562 | newy <= std::numeric_limits<s32>::max())) { | ||
| 563 | return nullptr; | ||
| 564 | } | ||
| 565 | result->tm_year = static_cast<s32>(newy); | ||
| 566 | } | ||
| 567 | return result; | ||
| 568 | } | ||
| 569 | if (sp->timecnt == 0 || t < sp->ats[0]) { | ||
| 570 | i = sp->defaulttype; | ||
| 571 | } | ||
| 572 | else { | ||
| 573 | int lo = 1; | ||
| 574 | int hi = sp->timecnt; | ||
| 575 | |||
| 576 | while (lo < hi) { | ||
| 577 | int mid = (lo + hi) >> 1; | ||
| 578 | |||
| 579 | if (t < sp->ats[mid]) | ||
| 580 | hi = mid; | ||
| 581 | else | ||
| 582 | lo = mid + 1; | ||
| 583 | } | ||
| 584 | i = sp->types[lo - 1]; | ||
| 585 | } | ||
| 586 | ttisp = &sp->ttis[i]; | ||
| 587 | /* | ||
| 588 | ** To get (wrong) behavior that's compatible with System V Release 2.0 | ||
| 589 | ** you'd replace the statement below with | ||
| 590 | ** t += ttisp->tt_utoff; | ||
| 591 | ** timesub(&t, 0L, sp, tmp); | ||
| 592 | */ | ||
| 593 | result = timesub(&t, ttisp->tt_utoff, sp, tmp); | ||
| 594 | if (result) { | ||
| 595 | result->tm_isdst = ttisp->tt_isdst; | ||
| 596 | |||
| 597 | if (ttisp->tt_desigidx > static_cast<s32>(sp->chars.size() - CHARS_EXTRA)) { | ||
| 598 | return nullptr; | ||
| 599 | } | ||
| 600 | |||
| 601 | auto num_chars_to_copy{ | ||
| 602 | std::min(sp->chars.size() - ttisp->tt_desigidx, result->tm_zone.size()) - 1 }; | ||
| 603 | std::strncpy(result->tm_zone.data(), &sp->chars[ttisp->tt_desigidx], num_chars_to_copy); | ||
| 604 | result->tm_zone[num_chars_to_copy] = '\0'; | ||
| 605 | |||
| 606 | auto original_size{ std::strlen(&sp->chars[ttisp->tt_desigidx]) }; | ||
| 607 | if (original_size > num_chars_to_copy) { | ||
| 608 | return nullptr; | ||
| 609 | } | ||
| 610 | |||
| 611 | result->tm_utoff = ttisp->tt_utoff; | ||
| 612 | result->time_index = i; | ||
| 613 | } | ||
| 614 | return result; | ||
| 615 | } | ||
| 616 | |||
| 617 | /* | ||
| 618 | ** Given a year, a rule, and the offset from UT at the time that rule takes | ||
| 619 | ** effect, calculate the year-relative time that rule takes effect. | ||
| 620 | */ | ||
| 621 | |||
| 622 | constexpr s64 transtime(const int year, const tzrule* const rulep, | ||
| 623 | const s64 offset) { | ||
| 624 | bool leapyear; | ||
| 625 | s64 value; | ||
| 626 | int i; | ||
| 627 | int d, m1, yy0, yy1, yy2, dow; | ||
| 628 | |||
| 629 | leapyear = isleap(year); | ||
| 630 | switch (rulep->r_type) { | ||
| 631 | case JULIAN_DAY: | ||
| 632 | /* | ||
| 633 | ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap | ||
| 634 | ** years. | ||
| 635 | ** In non-leap years, or if the day number is 59 or less, just | ||
| 636 | ** add SECSPERDAY times the day number-1 to the time of | ||
| 637 | ** January 1, midnight, to get the day. | ||
| 638 | */ | ||
| 639 | value = (rulep->r_day - 1) * SECSPERDAY; | ||
| 640 | if (leapyear && rulep->r_day >= 60) { | ||
| 641 | value += SECSPERDAY; | ||
| 642 | } | ||
| 643 | break; | ||
| 644 | |||
| 645 | case DAY_OF_YEAR: | ||
| 646 | /* | ||
| 647 | ** n - day of year. | ||
| 648 | ** Just add SECSPERDAY times the day number to the time of | ||
| 649 | ** January 1, midnight, to get the day. | ||
| 650 | */ | ||
| 651 | value = rulep->r_day * SECSPERDAY; | ||
| 652 | break; | ||
| 653 | |||
| 654 | case MONTH_NTH_DAY_OF_WEEK: | ||
| 655 | /* | ||
| 656 | ** Mm.n.d - nth "dth day" of month m. | ||
| 657 | */ | ||
| 658 | |||
| 659 | /* | ||
| 660 | ** Use Zeller's Congruence to get day-of-week of first day of | ||
| 661 | ** month. | ||
| 662 | */ | ||
| 663 | m1 = (rulep->r_mon + 9) % 12 + 1; | ||
| 664 | yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; | ||
| 665 | yy1 = yy0 / 100; | ||
| 666 | yy2 = yy0 % 100; | ||
| 667 | dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; | ||
| 668 | if (dow < 0) { | ||
| 669 | dow += DAYSPERWEEK; | ||
| 670 | } | ||
| 671 | |||
| 672 | /* | ||
| 673 | ** "dow" is the day-of-week of the first day of the month. Get | ||
| 674 | ** the day-of-month (zero-origin) of the first "dow" day of the | ||
| 675 | ** month. | ||
| 676 | */ | ||
| 677 | d = rulep->r_day - dow; | ||
| 678 | if (d < 0) { | ||
| 679 | d += DAYSPERWEEK; | ||
| 680 | } | ||
| 681 | for (i = 1; i < rulep->r_week; ++i) { | ||
| 682 | if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) { | ||
| 683 | break; | ||
| 684 | } | ||
| 685 | d += DAYSPERWEEK; | ||
| 686 | } | ||
| 687 | |||
| 688 | /* | ||
| 689 | ** "d" is the day-of-month (zero-origin) of the day we want. | ||
| 690 | */ | ||
| 691 | value = d * SECSPERDAY; | ||
| 692 | for (i = 0; i < rulep->r_mon - 1; ++i) { | ||
| 693 | value += mon_lengths[leapyear][i] * SECSPERDAY; | ||
| 694 | } | ||
| 695 | break; | ||
| 696 | |||
| 697 | default: | ||
| 698 | //UNREACHABLE(); | ||
| 699 | break; | ||
| 700 | } | ||
| 701 | |||
| 702 | /* | ||
| 703 | ** "value" is the year-relative time of 00:00:00 UT on the day in | ||
| 704 | ** question. To get the year-relative time of the specified local | ||
| 705 | ** time on that day, add the transition time and the current offset | ||
| 706 | ** from UT. | ||
| 707 | */ | ||
| 708 | return value + rulep->r_time + offset; | ||
| 709 | } | ||
| 710 | |||
| 711 | bool tzparse(const char* name, Rule* sp) { | ||
| 712 | const char* stdname{}; | ||
| 713 | const char* dstname{}; | ||
| 714 | s64 stdoffset; | ||
| 715 | s64 dstoffset; | ||
| 716 | char* cp; | ||
| 717 | ptrdiff_t stdlen; | ||
| 718 | ptrdiff_t dstlen{}; | ||
| 719 | ptrdiff_t charcnt; | ||
| 720 | time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN; | ||
| 721 | |||
| 722 | stdname = name; | ||
| 723 | if (*name == '<') { | ||
| 724 | name++; | ||
| 725 | stdname = name; | ||
| 726 | name = getqzname(name, '>'); | ||
| 727 | if (*name != '>') { | ||
| 728 | return false; | ||
| 729 | } | ||
| 730 | stdlen = name - stdname; | ||
| 731 | name++; | ||
| 732 | } | ||
| 733 | else { | ||
| 734 | name = getzname(name); | ||
| 735 | stdlen = name - stdname; | ||
| 736 | } | ||
| 737 | if (!(0 < stdlen && stdlen <= TZNAME_MAXIMUM)) { | ||
| 738 | return false; | ||
| 739 | } | ||
| 740 | name = getoffset(name, &stdoffset); | ||
| 741 | if (name == nullptr) { | ||
| 742 | return false; | ||
| 743 | } | ||
| 744 | charcnt = stdlen + 1; | ||
| 745 | if (charcnt > TZ_MAX_CHARS) { | ||
| 746 | return false; | ||
| 747 | } | ||
| 748 | if (*name != '\0') { | ||
| 749 | if (*name == '<') { | ||
| 750 | dstname = ++name; | ||
| 751 | name = getqzname(name, '>'); | ||
| 752 | if (*name != '>') | ||
| 753 | return false; | ||
| 754 | dstlen = name - dstname; | ||
| 755 | name++; | ||
| 756 | } | ||
| 757 | else { | ||
| 758 | dstname = name; | ||
| 759 | name = getzname(name); | ||
| 760 | dstlen = name - dstname; /* length of DST abbr. */ | ||
| 761 | } | ||
| 762 | if (!(0 < dstlen && dstlen <= TZNAME_MAXIMUM)) { | ||
| 763 | return false; | ||
| 764 | } | ||
| 765 | charcnt += dstlen + 1; | ||
| 766 | if (charcnt > TZ_MAX_CHARS) { | ||
| 767 | return false; | ||
| 768 | } | ||
| 769 | if (*name != '\0' && *name != ',' && *name != ';') { | ||
| 770 | name = getoffset(name, &dstoffset); | ||
| 771 | if (name == nullptr) { | ||
| 772 | return false; | ||
| 773 | } | ||
| 774 | } | ||
| 775 | else { | ||
| 776 | dstoffset = stdoffset - SECSPERHOUR; | ||
| 777 | } | ||
| 778 | if (*name == '\0') { | ||
| 779 | name = TZDEFRULESTRING; | ||
| 780 | } | ||
| 781 | if (*name == ',' || *name == ';') { | ||
| 782 | struct tzrule start; | ||
| 783 | struct tzrule end; | ||
| 784 | int year; | ||
| 785 | int timecnt; | ||
| 786 | time_t janfirst; | ||
| 787 | s64 janoffset = 0; | ||
| 788 | int yearbeg, yearlim; | ||
| 789 | |||
| 790 | ++name; | ||
| 791 | if ((name = getrule(name, &start)) == nullptr) { | ||
| 792 | return false; | ||
| 793 | } | ||
| 794 | if (*name++ != ',') { | ||
| 795 | return false; | ||
| 796 | } | ||
| 797 | if ((name = getrule(name, &end)) == nullptr) { | ||
| 798 | return false; | ||
| 799 | } | ||
| 800 | if (*name != '\0') { | ||
| 801 | return false; | ||
| 802 | } | ||
| 803 | sp->typecnt = 2; /* standard time and DST */ | ||
| 804 | /* | ||
| 805 | ** Two transitions per year, from EPOCH_YEAR forward. | ||
| 806 | */ | ||
| 807 | init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); | ||
| 808 | init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast<s32>(stdlen + 1)); | ||
| 809 | sp->defaulttype = 0; | ||
| 810 | timecnt = 0; | ||
| 811 | janfirst = 0; | ||
| 812 | yearbeg = EPOCH_YEAR; | ||
| 813 | |||
| 814 | do { | ||
| 815 | s64 yearsecs = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; | ||
| 816 | yearbeg--; | ||
| 817 | if (increment_overflow_time(&janfirst, -yearsecs)) { | ||
| 818 | janoffset = -yearsecs; | ||
| 819 | break; | ||
| 820 | } | ||
| 821 | } while (atlo < janfirst && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); | ||
| 822 | |||
| 823 | while (true) { | ||
| 824 | s64 yearsecs = year_lengths[isleap(yearbeg)] * SECSPERDAY; | ||
| 825 | int yearbeg1 = yearbeg; | ||
| 826 | time_t janfirst1 = janfirst; | ||
| 827 | if (increment_overflow_time(&janfirst1, yearsecs) || | ||
| 828 | increment_overflow(&yearbeg1, 1) || atlo <= janfirst1) { | ||
| 829 | break; | ||
| 830 | } | ||
| 831 | yearbeg = yearbeg1; | ||
| 832 | janfirst = janfirst1; | ||
| 833 | } | ||
| 834 | |||
| 835 | yearlim = yearbeg; | ||
| 836 | if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) { | ||
| 837 | yearlim = INT_MAX; | ||
| 838 | } | ||
| 839 | for (year = yearbeg; year < yearlim; year++) { | ||
| 840 | s64 starttime = transtime(year, &start, stdoffset), | ||
| 841 | endtime = transtime(year, &end, dstoffset); | ||
| 842 | s64 yearsecs = (year_lengths[isleap(year)] * SECSPERDAY); | ||
| 843 | bool reversed = endtime < starttime; | ||
| 844 | if (reversed) { | ||
| 845 | s64 swap = starttime; | ||
| 846 | starttime = endtime; | ||
| 847 | endtime = swap; | ||
| 848 | } | ||
| 849 | if (reversed || (starttime < endtime && endtime - starttime < yearsecs)) { | ||
| 850 | if (TZ_MAX_TIMES - 2 < timecnt) { | ||
| 851 | break; | ||
| 852 | } | ||
| 853 | sp->ats[timecnt] = janfirst; | ||
| 854 | if (!increment_overflow_time(reinterpret_cast<time_t*>(&sp->ats[timecnt]), janoffset + starttime) && | ||
| 855 | atlo <= sp->ats[timecnt]) { | ||
| 856 | sp->types[timecnt++] = !reversed; | ||
| 857 | } | ||
| 858 | sp->ats[timecnt] = janfirst; | ||
| 859 | if (!increment_overflow_time(reinterpret_cast<time_t*>(&sp->ats[timecnt]), janoffset + endtime) && | ||
| 860 | atlo <= sp->ats[timecnt]) { | ||
| 861 | sp->types[timecnt++] = reversed; | ||
| 862 | } | ||
| 863 | } | ||
| 864 | if (endtime < leaplo) { | ||
| 865 | yearlim = year; | ||
| 866 | if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) { | ||
| 867 | yearlim = INT_MAX; | ||
| 868 | } | ||
| 869 | } | ||
| 870 | if (increment_overflow_time(&janfirst, janoffset + yearsecs)) { | ||
| 871 | break; | ||
| 872 | } | ||
| 873 | janoffset = 0; | ||
| 874 | } | ||
| 875 | sp->timecnt = timecnt; | ||
| 876 | if (!timecnt) { | ||
| 877 | sp->ttis[0] = sp->ttis[1]; | ||
| 878 | sp->typecnt = 1; /* Perpetual DST. */ | ||
| 879 | } | ||
| 880 | else if (YEARSPERREPEAT < year - yearbeg) { | ||
| 881 | sp->goback = sp->goahead = true; | ||
| 882 | } | ||
| 883 | } | ||
| 884 | else { | ||
| 885 | s64 theirstdoffset; | ||
| 886 | s64 theirdstoffset; | ||
| 887 | s64 theiroffset; | ||
| 888 | bool isdst; | ||
| 889 | int i; | ||
| 890 | int j; | ||
| 891 | |||
| 892 | if (*name != '\0') { | ||
| 893 | return false; | ||
| 894 | } | ||
| 895 | /* | ||
| 896 | ** Initial values of theirstdoffset and theirdstoffset. | ||
| 897 | */ | ||
| 898 | theirstdoffset = 0; | ||
| 899 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 900 | j = sp->types[i]; | ||
| 901 | if (!sp->ttis[j].tt_isdst) { | ||
| 902 | theirstdoffset = -sp->ttis[j].tt_utoff; | ||
| 903 | break; | ||
| 904 | } | ||
| 905 | } | ||
| 906 | theirdstoffset = 0; | ||
| 907 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 908 | j = sp->types[i]; | ||
| 909 | if (sp->ttis[j].tt_isdst) { | ||
| 910 | theirdstoffset = -sp->ttis[j].tt_utoff; | ||
| 911 | break; | ||
| 912 | } | ||
| 913 | } | ||
| 914 | /* | ||
| 915 | ** Initially we're assumed to be in standard time. | ||
| 916 | */ | ||
| 917 | isdst = false; | ||
| 918 | /* | ||
| 919 | ** Now juggle transition times and types | ||
| 920 | ** tracking offsets as you do. | ||
| 921 | */ | ||
| 922 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 923 | j = sp->types[i]; | ||
| 924 | sp->types[i] = sp->ttis[j].tt_isdst; | ||
| 925 | if (sp->ttis[j].tt_ttisut) { | ||
| 926 | /* No adjustment to transition time */ | ||
| 927 | } | ||
| 928 | else { | ||
| 929 | /* | ||
| 930 | ** If daylight saving time is in | ||
| 931 | ** effect, and the transition time was | ||
| 932 | ** not specified as standard time, add | ||
| 933 | ** the daylight saving time offset to | ||
| 934 | ** the transition time; otherwise, add | ||
| 935 | ** the standard time offset to the | ||
| 936 | ** transition time. | ||
| 937 | */ | ||
| 938 | /* | ||
| 939 | ** Transitions from DST to DDST | ||
| 940 | ** will effectively disappear since | ||
| 941 | ** POSIX provides for only one DST | ||
| 942 | ** offset. | ||
| 943 | */ | ||
| 944 | if (isdst && !sp->ttis[j].tt_ttisstd) { | ||
| 945 | sp->ats[i] += dstoffset - theirdstoffset; | ||
| 946 | } | ||
| 947 | else { | ||
| 948 | sp->ats[i] += stdoffset - theirstdoffset; | ||
| 949 | } | ||
| 950 | } | ||
| 951 | theiroffset = -sp->ttis[j].tt_utoff; | ||
| 952 | if (sp->ttis[j].tt_isdst) { | ||
| 953 | theirdstoffset = theiroffset; | ||
| 954 | } | ||
| 955 | else { | ||
| 956 | theirstdoffset = theiroffset; | ||
| 957 | } | ||
| 958 | } | ||
| 959 | /* | ||
| 960 | ** Finally, fill in ttis. | ||
| 961 | */ | ||
| 962 | init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); | ||
| 963 | init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast<s32>(stdlen + 1)); | ||
| 964 | sp->typecnt = 2; | ||
| 965 | sp->defaulttype = 0; | ||
| 966 | } | ||
| 967 | } | ||
| 968 | else { | ||
| 969 | dstlen = 0; | ||
| 970 | sp->typecnt = 1; /* only standard time */ | ||
| 971 | sp->timecnt = 0; | ||
| 972 | init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); | ||
| 973 | sp->defaulttype = 0; | ||
| 974 | } | ||
| 975 | sp->charcnt = static_cast<s32>(charcnt); | ||
| 976 | cp = &sp->chars[0]; | ||
| 977 | memcpy(cp, stdname, stdlen); | ||
| 978 | cp += stdlen; | ||
| 979 | *cp++ = '\0'; | ||
| 980 | if (dstlen != 0) { | ||
| 981 | memcpy(cp, dstname, dstlen); | ||
| 982 | *(cp + dstlen) = '\0'; | ||
| 983 | } | ||
| 984 | return true; | ||
| 985 | } | ||
| 986 | |||
| 987 | int tzloadbody(Rule* sp, local_storage& local_storage) { | ||
| 988 | int i; | ||
| 989 | int stored; | ||
| 990 | size_t nread{ local_storage.binary.size_bytes() }; | ||
| 991 | int tzheadsize = sizeof(struct TzifHeader); | ||
| 992 | TzifHeader header{}; | ||
| 993 | |||
| 994 | //ASSERT(local_storage.binary.size_bytes() >= sizeof(TzifHeader)); | ||
| 995 | std::memcpy(&header, local_storage.binary.data(), sizeof(TzifHeader)); | ||
| 996 | |||
| 997 | sp->goback = sp->goahead = false; | ||
| 998 | |||
| 999 | for (stored = 8; stored <= 8; stored *= 2) { | ||
| 1000 | s64 datablock_size; | ||
| 1001 | s32 ttisstdcnt = detzcode(header.tzh_ttisstdcnt.data()); | ||
| 1002 | s32 ttisutcnt = detzcode(header.tzh_ttisutcnt.data()); | ||
| 1003 | s32 leapcnt = detzcode(header.tzh_leapcnt.data()); | ||
| 1004 | s32 timecnt = detzcode(header.tzh_timecnt.data()); | ||
| 1005 | s32 typecnt = detzcode(header.tzh_typecnt.data()); | ||
| 1006 | s32 charcnt = detzcode(header.tzh_charcnt.data()); | ||
| 1007 | /* Although tzfile(5) currently requires typecnt to be nonzero, | ||
| 1008 | support future formats that may allow zero typecnt | ||
| 1009 | in files that have a TZ string and no transitions. */ | ||
| 1010 | if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS && 0 <= typecnt && typecnt < TZ_MAX_TYPES && | ||
| 1011 | 0 <= timecnt && timecnt < TZ_MAX_TIMES && 0 <= charcnt && charcnt < TZ_MAX_CHARS && | ||
| 1012 | 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES && 0 <= ttisutcnt && | ||
| 1013 | ttisutcnt < TZ_MAX_TYPES)) { | ||
| 1014 | return EINVAL; | ||
| 1015 | } | ||
| 1016 | datablock_size = (timecnt * stored /* ats */ | ||
| 1017 | + timecnt /* types */ | ||
| 1018 | + typecnt * 6 /* ttinfos */ | ||
| 1019 | + charcnt /* chars */ | ||
| 1020 | + leapcnt * (stored + 4) /* lsinfos */ | ||
| 1021 | + ttisstdcnt /* ttisstds */ | ||
| 1022 | + ttisutcnt); /* ttisuts */ | ||
| 1023 | if (static_cast<s32>(local_storage.binary.size_bytes()) < tzheadsize + datablock_size) { | ||
| 1024 | return EINVAL; | ||
| 1025 | } | ||
| 1026 | if (!((ttisstdcnt == typecnt || ttisstdcnt == 0) && | ||
| 1027 | (ttisutcnt == typecnt || ttisutcnt == 0))) { | ||
| 1028 | return EINVAL; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | char const* p = (const char*)local_storage.binary.data() + tzheadsize; | ||
| 1032 | |||
| 1033 | sp->timecnt = timecnt; | ||
| 1034 | sp->typecnt = typecnt; | ||
| 1035 | sp->charcnt = charcnt; | ||
| 1036 | |||
| 1037 | /* Read transitions, discarding those out of time_t range. | ||
| 1038 | But pretend the last transition before TIME_T_MIN | ||
| 1039 | occurred at TIME_T_MIN. */ | ||
| 1040 | timecnt = 0; | ||
| 1041 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 1042 | int_fast64_t at = stored == 4 ? detzcode(p) : detzcode64(p); | ||
| 1043 | sp->types[i] = at <= TIME_T_MAX; | ||
| 1044 | if (sp->types[i]) { | ||
| 1045 | time_t attime = | ||
| 1046 | ((std::is_signed_v<time_t> ? at < TIME_T_MIN : at < 0) ? TIME_T_MIN : at); | ||
| 1047 | if (timecnt && attime <= sp->ats[timecnt - 1]) { | ||
| 1048 | if (attime < sp->ats[timecnt - 1]) | ||
| 1049 | return EINVAL; | ||
| 1050 | sp->types[i - 1] = 0; | ||
| 1051 | timecnt--; | ||
| 1052 | } | ||
| 1053 | sp->ats[timecnt++] = attime; | ||
| 1054 | } | ||
| 1055 | p += stored; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | timecnt = 0; | ||
| 1059 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 1060 | unsigned char typ = *p++; | ||
| 1061 | if (sp->typecnt <= typ) { | ||
| 1062 | return EINVAL; | ||
| 1063 | } | ||
| 1064 | if (sp->types[i]) { | ||
| 1065 | sp->types[timecnt++] = typ; | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | sp->timecnt = timecnt; | ||
| 1069 | for (i = 0; i < sp->typecnt; ++i) { | ||
| 1070 | struct ttinfo* ttisp; | ||
| 1071 | unsigned char isdst, desigidx; | ||
| 1072 | |||
| 1073 | ttisp = &sp->ttis[i]; | ||
| 1074 | ttisp->tt_utoff = detzcode(p); | ||
| 1075 | p += 4; | ||
| 1076 | isdst = *p++; | ||
| 1077 | if (!(isdst < 2)) { | ||
| 1078 | return EINVAL; | ||
| 1079 | } | ||
| 1080 | ttisp->tt_isdst = isdst != 0; | ||
| 1081 | desigidx = *p++; | ||
| 1082 | if (!(desigidx < sp->charcnt)) { | ||
| 1083 | return EINVAL; | ||
| 1084 | } | ||
| 1085 | ttisp->tt_desigidx = desigidx; | ||
| 1086 | } | ||
| 1087 | for (i = 0; i < sp->charcnt; ++i) { | ||
| 1088 | sp->chars[i] = *p++; | ||
| 1089 | } | ||
| 1090 | /* Ensure '\0'-terminated, and make it safe to call | ||
| 1091 | ttunspecified later. */ | ||
| 1092 | memset(&sp->chars[i], 0, CHARS_EXTRA); | ||
| 1093 | |||
| 1094 | for (i = 0; i < sp->typecnt; ++i) { | ||
| 1095 | struct ttinfo* ttisp; | ||
| 1096 | |||
| 1097 | ttisp = &sp->ttis[i]; | ||
| 1098 | if (ttisstdcnt == 0) { | ||
| 1099 | ttisp->tt_ttisstd = false; | ||
| 1100 | } | ||
| 1101 | else { | ||
| 1102 | if (*(bool*)p != true && *(bool*)p != false) { | ||
| 1103 | return EINVAL; | ||
| 1104 | } | ||
| 1105 | ttisp->tt_ttisstd = *(bool*)p++; | ||
| 1106 | } | ||
| 1107 | } | ||
| 1108 | for (i = 0; i < sp->typecnt; ++i) { | ||
| 1109 | struct ttinfo* ttisp; | ||
| 1110 | |||
| 1111 | ttisp = &sp->ttis[i]; | ||
| 1112 | if (ttisutcnt == 0) { | ||
| 1113 | ttisp->tt_ttisut = false; | ||
| 1114 | } | ||
| 1115 | else { | ||
| 1116 | if (*(bool*)p != true && *(bool*)p != false) { | ||
| 1117 | return EINVAL; | ||
| 1118 | } | ||
| 1119 | ttisp->tt_ttisut = *(bool*)p++; | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | nread += (ptrdiff_t)local_storage.binary.data() - (ptrdiff_t)p; | ||
| 1124 | if (nread < 0) { | ||
| 1125 | return EINVAL; | ||
| 1126 | } | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | std::array<char, 256> buf{}; | ||
| 1130 | if (nread > buf.size()) { | ||
| 1131 | //ASSERT(false); | ||
| 1132 | return EINVAL; | ||
| 1133 | } | ||
| 1134 | memmove(buf.data(), &local_storage.binary[local_storage.binary.size_bytes() - nread], nread); | ||
| 1135 | |||
| 1136 | if (nread > 2 && buf[0] == '\n' && buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) { | ||
| 1137 | Rule* ts = &local_storage.state; | ||
| 1138 | |||
| 1139 | buf[nread - 1] = '\0'; | ||
| 1140 | if (tzparse(&buf[1], ts) && local_storage.state.typecnt == 2) { | ||
| 1141 | |||
| 1142 | /* Attempt to reuse existing abbreviations. | ||
| 1143 | Without this, America/Anchorage would be right on | ||
| 1144 | the edge after 2037 when TZ_MAX_CHARS is 50, as | ||
| 1145 | sp->charcnt equals 40 (for LMT AST AWT APT AHST | ||
| 1146 | AHDT YST AKDT AKST) and ts->charcnt equals 10 | ||
| 1147 | (for AKST AKDT). Reusing means sp->charcnt can | ||
| 1148 | stay 40 in this example. */ | ||
| 1149 | int gotabbr = 0; | ||
| 1150 | int charcnt = sp->charcnt; | ||
| 1151 | for (i = 0; i < ts->typecnt; i++) { | ||
| 1152 | char* tsabbr = &ts->chars[ts->ttis[i].tt_desigidx]; | ||
| 1153 | int j; | ||
| 1154 | for (j = 0; j < charcnt; j++) | ||
| 1155 | if (strcmp(&sp->chars[j], tsabbr) == 0) { | ||
| 1156 | ts->ttis[i].tt_desigidx = j; | ||
| 1157 | gotabbr++; | ||
| 1158 | break; | ||
| 1159 | } | ||
| 1160 | if (!(j < charcnt)) { | ||
| 1161 | int tsabbrlen = static_cast<s32>(strlen(tsabbr)); | ||
| 1162 | if (j + tsabbrlen < TZ_MAX_CHARS) { | ||
| 1163 | strcpy(&sp->chars[j], tsabbr); | ||
| 1164 | charcnt = j + tsabbrlen + 1; | ||
| 1165 | ts->ttis[i].tt_desigidx = j; | ||
| 1166 | gotabbr++; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | } | ||
| 1170 | if (gotabbr == ts->typecnt) { | ||
| 1171 | sp->charcnt = charcnt; | ||
| 1172 | |||
| 1173 | /* Ignore any trailing, no-op transitions generated | ||
| 1174 | by zic as they don't help here and can run afoul | ||
| 1175 | of bugs in zic 2016j or earlier. */ | ||
| 1176 | while (1 < sp->timecnt && | ||
| 1177 | (sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 2])) { | ||
| 1178 | sp->timecnt--; | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | for (i = 0; i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; i++) { | ||
| 1182 | time_t t = ts->ats[i]; | ||
| 1183 | if (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1]) { | ||
| 1184 | continue; | ||
| 1185 | } | ||
| 1186 | sp->ats[sp->timecnt] = t; | ||
| 1187 | sp->types[sp->timecnt] = static_cast<u8>(sp->typecnt + ts->types[i]); | ||
| 1188 | sp->timecnt++; | ||
| 1189 | } | ||
| 1190 | for (i = 0; i < ts->typecnt; i++) { | ||
| 1191 | sp->ttis[sp->typecnt++] = ts->ttis[i]; | ||
| 1192 | } | ||
| 1193 | } | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | if (sp->typecnt == 0) { | ||
| 1197 | return EINVAL; | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | if (sp->timecnt > 1) { | ||
| 1201 | if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) { | ||
| 1202 | time_t repeatat = sp->ats[0] + SECSPERREPEAT; | ||
| 1203 | int repeattype = sp->types[0]; | ||
| 1204 | for (i = 1; i < sp->timecnt; ++i) { | ||
| 1205 | if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) { | ||
| 1206 | sp->goback = true; | ||
| 1207 | break; | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { | ||
| 1212 | time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT; | ||
| 1213 | int repeattype = sp->types[sp->timecnt - 1]; | ||
| 1214 | for (i = sp->timecnt - 2; i >= 0; --i) { | ||
| 1215 | if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) { | ||
| 1216 | sp->goahead = true; | ||
| 1217 | break; | ||
| 1218 | } | ||
| 1219 | } | ||
| 1220 | } | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | /* Infer sp->defaulttype from the data. Although this default | ||
| 1224 | type is always zero for data from recent tzdb releases, | ||
| 1225 | things are trickier for data from tzdb 2018e or earlier. | ||
| 1226 | |||
| 1227 | The first set of heuristics work around bugs in 32-bit data | ||
| 1228 | generated by tzdb 2013c or earlier. The workaround is for | ||
| 1229 | zones like Australia/Macquarie where timestamps before the | ||
| 1230 | first transition have a time type that is not the earliest | ||
| 1231 | standard-time type. See: | ||
| 1232 | https://mm.icann.org/pipermail/tz/2013-May/019368.html */ | ||
| 1233 | /* | ||
| 1234 | ** If type 0 does not specify local time, or is unused in transitions, | ||
| 1235 | ** it's the type to use for early times. | ||
| 1236 | */ | ||
| 1237 | for (i = 0; i < sp->timecnt; ++i) { | ||
| 1238 | if (sp->types[i] == 0) { | ||
| 1239 | break; | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | i = i < sp->timecnt && !ttunspecified(sp, 0) ? -1 : 0; | ||
| 1243 | /* | ||
| 1244 | ** Absent the above, | ||
| 1245 | ** if there are transition times | ||
| 1246 | ** and the first transition is to a daylight time | ||
| 1247 | ** find the standard type less than and closest to | ||
| 1248 | ** the type of the first transition. | ||
| 1249 | */ | ||
| 1250 | if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { | ||
| 1251 | i = sp->types[0]; | ||
| 1252 | while (--i >= 0) { | ||
| 1253 | if (!sp->ttis[i].tt_isdst) { | ||
| 1254 | break; | ||
| 1255 | } | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | /* The next heuristics are for data generated by tzdb 2018e or | ||
| 1259 | earlier, for zones like EST5EDT where the first transition | ||
| 1260 | is to DST. */ | ||
| 1261 | /* | ||
| 1262 | ** If no result yet, find the first standard type. | ||
| 1263 | ** If there is none, punt to type zero. | ||
| 1264 | */ | ||
| 1265 | if (i < 0) { | ||
| 1266 | i = 0; | ||
| 1267 | while (sp->ttis[i].tt_isdst) { | ||
| 1268 | if (++i >= sp->typecnt) { | ||
| 1269 | i = 0; | ||
| 1270 | break; | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | } | ||
| 1274 | /* A simple 'sp->defaulttype = 0;' would suffice here if we | ||
| 1275 | didn't have to worry about 2018e-or-earlier data. Even | ||
| 1276 | simpler would be to remove the defaulttype member and just | ||
| 1277 | use 0 in its place. */ | ||
| 1278 | sp->defaulttype = i; | ||
| 1279 | |||
| 1280 | return 0; | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | constexpr int tmcomp(const CalendarTimeInternal* const atmp, | ||
| 1284 | const CalendarTimeInternal* const btmp) { | ||
| 1285 | int result; | ||
| 1286 | |||
| 1287 | if (atmp->tm_year != btmp->tm_year) { | ||
| 1288 | return atmp->tm_year < btmp->tm_year ? -1 : 1; | ||
| 1289 | } | ||
| 1290 | if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && | ||
| 1291 | (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && | ||
| 1292 | (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && | ||
| 1293 | (result = (atmp->tm_min - btmp->tm_min)) == 0) { | ||
| 1294 | result = atmp->tm_sec - btmp->tm_sec; | ||
| 1295 | } | ||
| 1296 | return result; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | /* Copy to *DEST from *SRC. Copy only the members needed for mktime, | ||
| 1300 | as other members might not be initialized. */ | ||
| 1301 | constexpr void mktmcpy(struct CalendarTimeInternal* dest, struct CalendarTimeInternal const* src) { | ||
| 1302 | dest->tm_sec = src->tm_sec; | ||
| 1303 | dest->tm_min = src->tm_min; | ||
| 1304 | dest->tm_hour = src->tm_hour; | ||
| 1305 | dest->tm_mday = src->tm_mday; | ||
| 1306 | dest->tm_mon = src->tm_mon; | ||
| 1307 | dest->tm_year = src->tm_year; | ||
| 1308 | dest->tm_isdst = src->tm_isdst; | ||
| 1309 | dest->tm_zone = src->tm_zone; | ||
| 1310 | dest->tm_utoff = src->tm_utoff; | ||
| 1311 | dest->time_index = src->time_index; | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | constexpr bool normalize_overflow(int* const tensptr, int* const unitsptr, const int base) { | ||
| 1315 | int tensdelta; | ||
| 1316 | |||
| 1317 | tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); | ||
| 1318 | *unitsptr -= tensdelta * base; | ||
| 1319 | return increment_overflow(tensptr, tensdelta); | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | constexpr bool normalize_overflow32(s64* tensptr, int* unitsptr, int base) { | ||
| 1323 | int tensdelta; | ||
| 1324 | |||
| 1325 | tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); | ||
| 1326 | *unitsptr -= tensdelta * base; | ||
| 1327 | return increment_overflow32(tensptr, tensdelta); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | int time2sub(time_t* out_time, CalendarTimeInternal* const tmp, | ||
| 1331 | CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64, | ||
| 1332 | CalendarTimeInternal*), | ||
| 1333 | Rule const* sp, const s64 offset, bool* okayp, bool do_norm_secs) { | ||
| 1334 | int dir; | ||
| 1335 | int i, j; | ||
| 1336 | int saved_seconds; | ||
| 1337 | s64 li; | ||
| 1338 | time_t lo; | ||
| 1339 | time_t hi; | ||
| 1340 | s64 y; | ||
| 1341 | time_t newt; | ||
| 1342 | time_t t; | ||
| 1343 | CalendarTimeInternal yourtm, mytm; | ||
| 1344 | |||
| 1345 | *okayp = false; | ||
| 1346 | mktmcpy(&yourtm, tmp); | ||
| 1347 | |||
| 1348 | if (do_norm_secs) { | ||
| 1349 | if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) { | ||
| 1350 | return 1; | ||
| 1351 | } | ||
| 1352 | } | ||
| 1353 | if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) { | ||
| 1354 | return 1; | ||
| 1355 | } | ||
| 1356 | if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) { | ||
| 1357 | return 1; | ||
| 1358 | } | ||
| 1359 | y = yourtm.tm_year; | ||
| 1360 | if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) { | ||
| 1361 | return 1; | ||
| 1362 | } | ||
| 1363 | /* | ||
| 1364 | ** Turn y into an actual year number for now. | ||
| 1365 | ** It is converted back to an offset from TM_YEAR_BASE later. | ||
| 1366 | */ | ||
| 1367 | if (increment_overflow32(&y, TM_YEAR_BASE)) { | ||
| 1368 | return 1; | ||
| 1369 | } | ||
| 1370 | while (yourtm.tm_mday <= 0) { | ||
| 1371 | if (increment_overflow32(&y, -1)) { | ||
| 1372 | return 1; | ||
| 1373 | } | ||
| 1374 | li = y + (1 < yourtm.tm_mon); | ||
| 1375 | yourtm.tm_mday += year_lengths[isleap(li)]; | ||
| 1376 | } | ||
| 1377 | while (yourtm.tm_mday > DAYSPERLYEAR) { | ||
| 1378 | li = y + (1 < yourtm.tm_mon); | ||
| 1379 | yourtm.tm_mday -= year_lengths[isleap(li)]; | ||
| 1380 | if (increment_overflow32(&y, 1)) { | ||
| 1381 | return 1; | ||
| 1382 | } | ||
| 1383 | } | ||
| 1384 | for (;;) { | ||
| 1385 | i = mon_lengths[isleap(y)][yourtm.tm_mon]; | ||
| 1386 | if (yourtm.tm_mday <= i) { | ||
| 1387 | break; | ||
| 1388 | } | ||
| 1389 | yourtm.tm_mday -= i; | ||
| 1390 | if (++yourtm.tm_mon >= MONSPERYEAR) { | ||
| 1391 | yourtm.tm_mon = 0; | ||
| 1392 | if (increment_overflow32(&y, 1)) { | ||
| 1393 | return 1; | ||
| 1394 | } | ||
| 1395 | } | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | if (increment_overflow32(&y, -TM_YEAR_BASE)) { | ||
| 1399 | return 1; | ||
| 1400 | } | ||
| 1401 | if (!(INT_MIN <= y && y <= INT_MAX)) { | ||
| 1402 | return 1; | ||
| 1403 | } | ||
| 1404 | yourtm.tm_year = static_cast<s32>(y); | ||
| 1405 | |||
| 1406 | if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) { | ||
| 1407 | saved_seconds = 0; | ||
| 1408 | } | ||
| 1409 | else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) { | ||
| 1410 | /* | ||
| 1411 | ** We can't set tm_sec to 0, because that might push the | ||
| 1412 | ** time below the minimum representable time. | ||
| 1413 | ** Set tm_sec to 59 instead. | ||
| 1414 | ** This assumes that the minimum representable time is | ||
| 1415 | ** not in the same minute that a leap second was deleted from, | ||
| 1416 | ** which is a safer assumption than using 58 would be. | ||
| 1417 | */ | ||
| 1418 | if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) { | ||
| 1419 | return 1; | ||
| 1420 | } | ||
| 1421 | saved_seconds = yourtm.tm_sec; | ||
| 1422 | yourtm.tm_sec = SECSPERMIN - 1; | ||
| 1423 | } | ||
| 1424 | else { | ||
| 1425 | saved_seconds = yourtm.tm_sec; | ||
| 1426 | yourtm.tm_sec = 0; | ||
| 1427 | } | ||
| 1428 | /* | ||
| 1429 | ** Do a binary search (this works whatever time_t's type is). | ||
| 1430 | */ | ||
| 1431 | lo = TIME_T_MIN; | ||
| 1432 | hi = TIME_T_MAX; | ||
| 1433 | for (;;) { | ||
| 1434 | t = lo / 2 + hi / 2; | ||
| 1435 | if (t < lo) { | ||
| 1436 | t = lo; | ||
| 1437 | } | ||
| 1438 | else if (t > hi) { | ||
| 1439 | t = hi; | ||
| 1440 | } | ||
| 1441 | if (!funcp(sp, &t, offset, &mytm)) { | ||
| 1442 | /* | ||
| 1443 | ** Assume that t is too extreme to be represented in | ||
| 1444 | ** a struct tm; arrange things so that it is less | ||
| 1445 | ** extreme on the next pass. | ||
| 1446 | */ | ||
| 1447 | dir = (t > 0) ? 1 : -1; | ||
| 1448 | } | ||
| 1449 | else { | ||
| 1450 | dir = tmcomp(&mytm, &yourtm); | ||
| 1451 | } | ||
| 1452 | if (dir != 0) { | ||
| 1453 | if (t == lo) { | ||
| 1454 | if (t == TIME_T_MAX) { | ||
| 1455 | return 2; | ||
| 1456 | } | ||
| 1457 | ++t; | ||
| 1458 | ++lo; | ||
| 1459 | } | ||
| 1460 | else if (t == hi) { | ||
| 1461 | if (t == TIME_T_MIN) { | ||
| 1462 | return 2; | ||
| 1463 | } | ||
| 1464 | --t; | ||
| 1465 | --hi; | ||
| 1466 | } | ||
| 1467 | if (lo > hi) { | ||
| 1468 | return 2; | ||
| 1469 | } | ||
| 1470 | if (dir > 0) { | ||
| 1471 | hi = t; | ||
| 1472 | } | ||
| 1473 | else { | ||
| 1474 | lo = t; | ||
| 1475 | } | ||
| 1476 | continue; | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) { | ||
| 1480 | break; | ||
| 1481 | } | ||
| 1482 | /* | ||
| 1483 | ** Right time, wrong type. | ||
| 1484 | ** Hunt for right time, right type. | ||
| 1485 | ** It's okay to guess wrong since the guess | ||
| 1486 | ** gets checked. | ||
| 1487 | */ | ||
| 1488 | if (sp == nullptr) { | ||
| 1489 | return 2; | ||
| 1490 | } | ||
| 1491 | for (i = sp->typecnt - 1; i >= 0; --i) { | ||
| 1492 | if (sp->ttis[i].tt_isdst != static_cast<bool>(yourtm.tm_isdst)) { | ||
| 1493 | continue; | ||
| 1494 | } | ||
| 1495 | for (j = sp->typecnt - 1; j >= 0; --j) { | ||
| 1496 | if (sp->ttis[j].tt_isdst == static_cast<bool>(yourtm.tm_isdst)) { | ||
| 1497 | continue; | ||
| 1498 | } | ||
| 1499 | if (ttunspecified(sp, j)) { | ||
| 1500 | continue; | ||
| 1501 | } | ||
| 1502 | newt = (t + sp->ttis[j].tt_utoff - sp->ttis[i].tt_utoff); | ||
| 1503 | if (!funcp(sp, &newt, offset, &mytm)) { | ||
| 1504 | continue; | ||
| 1505 | } | ||
| 1506 | if (tmcomp(&mytm, &yourtm) != 0) { | ||
| 1507 | continue; | ||
| 1508 | } | ||
| 1509 | if (mytm.tm_isdst != yourtm.tm_isdst) { | ||
| 1510 | continue; | ||
| 1511 | } | ||
| 1512 | /* | ||
| 1513 | ** We have a match. | ||
| 1514 | */ | ||
| 1515 | t = newt; | ||
| 1516 | goto label; | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | return 2; | ||
| 1520 | } | ||
| 1521 | label: | ||
| 1522 | newt = t + saved_seconds; | ||
| 1523 | t = newt; | ||
| 1524 | if (funcp(sp, &t, offset, tmp) || *okayp) { | ||
| 1525 | *okayp = true; | ||
| 1526 | *out_time = t; | ||
| 1527 | return 0; | ||
| 1528 | } | ||
| 1529 | return 2; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | int time2(time_t* out_time, struct CalendarTimeInternal* const tmp, | ||
| 1533 | struct CalendarTimeInternal* (*funcp)(struct Rule const*, time_t const*, s64, | ||
| 1534 | struct CalendarTimeInternal*), | ||
| 1535 | struct Rule const* sp, const s64 offset, bool* okayp) { | ||
| 1536 | int res; | ||
| 1537 | |||
| 1538 | /* | ||
| 1539 | ** First try without normalization of seconds | ||
| 1540 | ** (in case tm_sec contains a value associated with a leap second). | ||
| 1541 | ** If that fails, try with normalization of seconds. | ||
| 1542 | */ | ||
| 1543 | res = time2sub(out_time, tmp, funcp, sp, offset, okayp, false); | ||
| 1544 | return *okayp ? res : time2sub(out_time, tmp, funcp, sp, offset, okayp, true); | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | int time1(time_t* out_time, CalendarTimeInternal* const tmp, | ||
| 1548 | CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64, | ||
| 1549 | CalendarTimeInternal*), | ||
| 1550 | Rule const* sp, const s64 offset) { | ||
| 1551 | int samei, otheri; | ||
| 1552 | int sameind, otherind; | ||
| 1553 | int i; | ||
| 1554 | int nseen; | ||
| 1555 | char seen[TZ_MAX_TYPES]; | ||
| 1556 | unsigned char types[TZ_MAX_TYPES]; | ||
| 1557 | bool okay; | ||
| 1558 | |||
| 1559 | if (tmp->tm_isdst > 1) { | ||
| 1560 | tmp->tm_isdst = 1; | ||
| 1561 | } | ||
| 1562 | auto res = time2(out_time, tmp, funcp, sp, offset, &okay); | ||
| 1563 | if (res == 0) { | ||
| 1564 | return res; | ||
| 1565 | } | ||
| 1566 | if (tmp->tm_isdst < 0) { | ||
| 1567 | return res; | ||
| 1568 | } | ||
| 1569 | /* | ||
| 1570 | ** We're supposed to assume that somebody took a time of one type | ||
| 1571 | ** and did some math on it that yielded a "struct tm" that's bad. | ||
| 1572 | ** We try to divine the type they started from and adjust to the | ||
| 1573 | ** type they need. | ||
| 1574 | */ | ||
| 1575 | for (i = 0; i < sp->typecnt; ++i) { | ||
| 1576 | seen[i] = false; | ||
| 1577 | } | ||
| 1578 | |||
| 1579 | if (sp->timecnt < 1) { | ||
| 1580 | return 2; | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | nseen = 0; | ||
| 1584 | for (i = sp->timecnt - 1; i >= 0; --i) { | ||
| 1585 | if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) { | ||
| 1586 | seen[sp->types[i]] = true; | ||
| 1587 | types[nseen++] = sp->types[i]; | ||
| 1588 | } | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | if (nseen < 1) { | ||
| 1592 | return 2; | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | for (sameind = 0; sameind < nseen; ++sameind) { | ||
| 1596 | samei = types[sameind]; | ||
| 1597 | if (sp->ttis[samei].tt_isdst != static_cast<bool>(tmp->tm_isdst)) { | ||
| 1598 | continue; | ||
| 1599 | } | ||
| 1600 | for (otherind = 0; otherind < nseen; ++otherind) { | ||
| 1601 | otheri = types[otherind]; | ||
| 1602 | if (sp->ttis[otheri].tt_isdst == static_cast<bool>(tmp->tm_isdst)) { | ||
| 1603 | continue; | ||
| 1604 | } | ||
| 1605 | tmp->tm_sec += (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff); | ||
| 1606 | tmp->tm_isdst = !tmp->tm_isdst; | ||
| 1607 | res = time2(out_time, tmp, funcp, sp, offset, &okay); | ||
| 1608 | if (res == 0) { | ||
| 1609 | return res; | ||
| 1610 | } | ||
| 1611 | tmp->tm_sec -= (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff); | ||
| 1612 | tmp->tm_isdst = !tmp->tm_isdst; | ||
| 1613 | } | ||
| 1614 | } | ||
| 1615 | return 2; | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | } // namespace | ||
| 1619 | |||
| 1620 | s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary) { | ||
| 1621 | tzloadbody_local_storage.binary = binary; | ||
| 1622 | if (tzloadbody(&out_rule, tzloadbody_local_storage)) { | ||
| 1623 | return 3; | ||
| 1624 | } | ||
| 1625 | return 0; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep) { | ||
| 1629 | return localsub(sp, timep, 0, tmp) == nullptr; | ||
| 1630 | } | ||
| 1631 | |||
| 1632 | u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp) { | ||
| 1633 | return time1(out_time, tmp, localsub, sp, 0); | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | } // namespace Tz | ||
diff --git a/externals/tz/tz/tz.h b/externals/tz/tz/tz.h new file mode 100644 index 000000000..38605cfb1 --- /dev/null +++ b/externals/tz/tz/tz.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 1996 Arthur David Olson | ||
| 3 | // SPDX-License-Identifier: BSD-2-Clause | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstdint> | ||
| 8 | #include <limits> | ||
| 9 | #include <span> | ||
| 10 | #include <array> | ||
| 11 | #include <time.h> | ||
| 12 | |||
| 13 | namespace Tz { | ||
| 14 | using u8 = uint8_t; | ||
| 15 | using s8 = int8_t; | ||
| 16 | using u16 = uint16_t; | ||
| 17 | using s16 = int16_t; | ||
| 18 | using u32 = uint32_t; | ||
| 19 | using s32 = int32_t; | ||
| 20 | using u64 = uint64_t; | ||
| 21 | using s64 = int64_t; | ||
| 22 | |||
| 23 | constexpr size_t TZ_MAX_TIMES = 1000; | ||
| 24 | constexpr size_t TZ_MAX_TYPES = 128; | ||
| 25 | constexpr size_t TZ_MAX_CHARS = 50; | ||
| 26 | constexpr size_t MY_TZNAME_MAX = 255; | ||
| 27 | constexpr size_t TZNAME_MAXIMUM = 255; | ||
| 28 | constexpr size_t TZ_MAX_LEAPS = 50; | ||
| 29 | constexpr s64 TIME_T_MAX = std::numeric_limits<s64>::max(); | ||
| 30 | constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::min(); | ||
| 31 | constexpr size_t CHARS_EXTRA = 3; | ||
| 32 | constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC")); | ||
| 33 | constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1); | ||
| 34 | |||
| 35 | struct ttinfo { | ||
| 36 | s32 tt_utoff; | ||
| 37 | bool tt_isdst; | ||
| 38 | s32 tt_desigidx; | ||
| 39 | bool tt_ttisstd; | ||
| 40 | bool tt_ttisut; | ||
| 41 | }; | ||
| 42 | static_assert(sizeof(ttinfo) == 0x10, "ttinfo has the wrong size!"); | ||
| 43 | |||
| 44 | struct Rule { | ||
| 45 | s32 timecnt; | ||
| 46 | s32 typecnt; | ||
| 47 | s32 charcnt; | ||
| 48 | bool goback; | ||
| 49 | bool goahead; | ||
| 50 | std::array <u8, 0x2> padding0; | ||
| 51 | std::array<s64, TZ_MAX_TIMES> ats; | ||
| 52 | std::array<u8, TZ_MAX_TIMES> types; | ||
| 53 | std::array<ttinfo, TZ_MAX_TYPES> ttis; | ||
| 54 | std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars; | ||
| 55 | s32 defaulttype; | ||
| 56 | std::array <u8, 0x12C4> padding1; | ||
| 57 | }; | ||
| 58 | static_assert(sizeof(Rule) == 0x4000, "Rule has the wrong size!"); | ||
| 59 | |||
| 60 | struct CalendarTimeInternal { | ||
| 61 | s32 tm_sec; | ||
| 62 | s32 tm_min; | ||
| 63 | s32 tm_hour; | ||
| 64 | s32 tm_mday; | ||
| 65 | s32 tm_mon; | ||
| 66 | s32 tm_year; | ||
| 67 | s32 tm_wday; | ||
| 68 | s32 tm_yday; | ||
| 69 | s32 tm_isdst; | ||
| 70 | std::array<char, 16> tm_zone; | ||
| 71 | s32 tm_utoff; | ||
| 72 | s32 time_index; | ||
| 73 | }; | ||
| 74 | static_assert(sizeof(CalendarTimeInternal) == 0x3C, "CalendarTimeInternal has the wrong size!"); | ||
| 75 | |||
| 76 | s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary); | ||
| 77 | |||
| 78 | bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep); | ||
| 79 | u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp); | ||
| 80 | |||
| 81 | } // namespace Tz | ||