17db96d56Sopenharmony_ci#ifndef Py_BUILD_CORE_BUILTIN
27db96d56Sopenharmony_ci#  define Py_BUILD_CORE_MODULE 1
37db96d56Sopenharmony_ci#endif
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ci#include "Python.h"
67db96d56Sopenharmony_ci#include "pycore_long.h"          // _PyLong_GetOne()
77db96d56Sopenharmony_ci#include "structmember.h"
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci#include <ctype.h>
107db96d56Sopenharmony_ci#include <stddef.h>
117db96d56Sopenharmony_ci#include <stdint.h>
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ci#include "datetime.h"
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci// Imports
167db96d56Sopenharmony_cistatic PyObject *io_open = NULL;
177db96d56Sopenharmony_cistatic PyObject *_tzpath_find_tzfile = NULL;
187db96d56Sopenharmony_cistatic PyObject *_common_mod = NULL;
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_citypedef struct TransitionRuleType TransitionRuleType;
217db96d56Sopenharmony_citypedef struct StrongCacheNode StrongCacheNode;
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_citypedef struct {
247db96d56Sopenharmony_ci    PyObject *utcoff;
257db96d56Sopenharmony_ci    PyObject *dstoff;
267db96d56Sopenharmony_ci    PyObject *tzname;
277db96d56Sopenharmony_ci    long utcoff_seconds;
287db96d56Sopenharmony_ci} _ttinfo;
297db96d56Sopenharmony_ci
307db96d56Sopenharmony_citypedef struct {
317db96d56Sopenharmony_ci    _ttinfo std;
327db96d56Sopenharmony_ci    _ttinfo dst;
337db96d56Sopenharmony_ci    int dst_diff;
347db96d56Sopenharmony_ci    TransitionRuleType *start;
357db96d56Sopenharmony_ci    TransitionRuleType *end;
367db96d56Sopenharmony_ci    unsigned char std_only;
377db96d56Sopenharmony_ci} _tzrule;
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_citypedef struct {
407db96d56Sopenharmony_ci    PyDateTime_TZInfo base;
417db96d56Sopenharmony_ci    PyObject *key;
427db96d56Sopenharmony_ci    PyObject *file_repr;
437db96d56Sopenharmony_ci    PyObject *weakreflist;
447db96d56Sopenharmony_ci    size_t num_transitions;
457db96d56Sopenharmony_ci    size_t num_ttinfos;
467db96d56Sopenharmony_ci    int64_t *trans_list_utc;
477db96d56Sopenharmony_ci    int64_t *trans_list_wall[2];
487db96d56Sopenharmony_ci    _ttinfo **trans_ttinfos;  // References to the ttinfo for each transition
497db96d56Sopenharmony_ci    _ttinfo *ttinfo_before;
507db96d56Sopenharmony_ci    _tzrule tzrule_after;
517db96d56Sopenharmony_ci    _ttinfo *_ttinfos;  // Unique array of ttinfos for ease of deallocation
527db96d56Sopenharmony_ci    unsigned char fixed_offset;
537db96d56Sopenharmony_ci    unsigned char source;
547db96d56Sopenharmony_ci} PyZoneInfo_ZoneInfo;
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_cistruct TransitionRuleType {
577db96d56Sopenharmony_ci    int64_t (*year_to_timestamp)(TransitionRuleType *, int);
587db96d56Sopenharmony_ci};
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_citypedef struct {
617db96d56Sopenharmony_ci    TransitionRuleType base;
627db96d56Sopenharmony_ci    uint8_t month;
637db96d56Sopenharmony_ci    uint8_t week;
647db96d56Sopenharmony_ci    uint8_t day;
657db96d56Sopenharmony_ci    int8_t hour;
667db96d56Sopenharmony_ci    int8_t minute;
677db96d56Sopenharmony_ci    int8_t second;
687db96d56Sopenharmony_ci} CalendarRule;
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_citypedef struct {
717db96d56Sopenharmony_ci    TransitionRuleType base;
727db96d56Sopenharmony_ci    uint8_t julian;
737db96d56Sopenharmony_ci    unsigned int day;
747db96d56Sopenharmony_ci    int8_t hour;
757db96d56Sopenharmony_ci    int8_t minute;
767db96d56Sopenharmony_ci    int8_t second;
777db96d56Sopenharmony_ci} DayRule;
787db96d56Sopenharmony_ci
797db96d56Sopenharmony_cistruct StrongCacheNode {
807db96d56Sopenharmony_ci    StrongCacheNode *next;
817db96d56Sopenharmony_ci    StrongCacheNode *prev;
827db96d56Sopenharmony_ci    PyObject *key;
837db96d56Sopenharmony_ci    PyObject *zone;
847db96d56Sopenharmony_ci};
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_cistatic PyTypeObject PyZoneInfo_ZoneInfoType;
877db96d56Sopenharmony_ci
887db96d56Sopenharmony_ci// Globals
897db96d56Sopenharmony_cistatic PyObject *TIMEDELTA_CACHE = NULL;
907db96d56Sopenharmony_cistatic PyObject *ZONEINFO_WEAK_CACHE = NULL;
917db96d56Sopenharmony_cistatic StrongCacheNode *ZONEINFO_STRONG_CACHE = NULL;
927db96d56Sopenharmony_cistatic size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8;
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_cistatic _ttinfo NO_TTINFO = {NULL, NULL, NULL, 0};
957db96d56Sopenharmony_ci
967db96d56Sopenharmony_ci// Constants
977db96d56Sopenharmony_cistatic const int EPOCHORDINAL = 719163;
987db96d56Sopenharmony_cistatic int DAYS_IN_MONTH[] = {
997db96d56Sopenharmony_ci    -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
1007db96d56Sopenharmony_ci};
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_cistatic int DAYS_BEFORE_MONTH[] = {
1037db96d56Sopenharmony_ci    -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
1047db96d56Sopenharmony_ci};
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_cistatic const int SOURCE_NOCACHE = 0;
1077db96d56Sopenharmony_cistatic const int SOURCE_CACHE = 1;
1087db96d56Sopenharmony_cistatic const int SOURCE_FILE = 2;
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci// Forward declarations
1117db96d56Sopenharmony_cistatic int
1127db96d56Sopenharmony_ciload_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj);
1137db96d56Sopenharmony_cistatic void
1147db96d56Sopenharmony_ciutcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs,
1157db96d56Sopenharmony_ci                 unsigned char *isdsts, size_t num_transitions,
1167db96d56Sopenharmony_ci                 size_t num_ttinfos);
1177db96d56Sopenharmony_cistatic int
1187db96d56Sopenharmony_cits_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff,
1197db96d56Sopenharmony_ci            int64_t *trans_local[2], size_t num_ttinfos,
1207db96d56Sopenharmony_ci            size_t num_transitions);
1217db96d56Sopenharmony_ci
1227db96d56Sopenharmony_cistatic int
1237db96d56Sopenharmony_ciparse_tz_str(PyObject *tz_str_obj, _tzrule *out);
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_cistatic Py_ssize_t
1267db96d56Sopenharmony_ciparse_abbr(const char *const p, PyObject **abbr);
1277db96d56Sopenharmony_cistatic Py_ssize_t
1287db96d56Sopenharmony_ciparse_tz_delta(const char *const p, long *total_seconds);
1297db96d56Sopenharmony_cistatic Py_ssize_t
1307db96d56Sopenharmony_ciparse_transition_time(const char *const p, int8_t *hour, int8_t *minute,
1317db96d56Sopenharmony_ci                      int8_t *second);
1327db96d56Sopenharmony_cistatic Py_ssize_t
1337db96d56Sopenharmony_ciparse_transition_rule(const char *const p, TransitionRuleType **out);
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_cistatic _ttinfo *
1367db96d56Sopenharmony_cifind_tzrule_ttinfo(_tzrule *rule, int64_t ts, unsigned char fold, int year);
1377db96d56Sopenharmony_cistatic _ttinfo *
1387db96d56Sopenharmony_cifind_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year,
1397db96d56Sopenharmony_ci                           unsigned char *fold);
1407db96d56Sopenharmony_ci
1417db96d56Sopenharmony_cistatic int
1427db96d56Sopenharmony_cibuild_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out);
1437db96d56Sopenharmony_cistatic void
1447db96d56Sopenharmony_cixdecref_ttinfo(_ttinfo *ttinfo);
1457db96d56Sopenharmony_cistatic int
1467db96d56Sopenharmony_cittinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1);
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_cistatic int
1497db96d56Sopenharmony_cibuild_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset,
1507db96d56Sopenharmony_ci             long dst_offset, TransitionRuleType *start,
1517db96d56Sopenharmony_ci             TransitionRuleType *end, _tzrule *out);
1527db96d56Sopenharmony_cistatic void
1537db96d56Sopenharmony_cifree_tzrule(_tzrule *tzrule);
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_cistatic PyObject *
1567db96d56Sopenharmony_ciload_timedelta(long seconds);
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_cistatic int
1597db96d56Sopenharmony_ciget_local_timestamp(PyObject *dt, int64_t *local_ts);
1607db96d56Sopenharmony_cistatic _ttinfo *
1617db96d56Sopenharmony_cifind_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt);
1627db96d56Sopenharmony_ci
1637db96d56Sopenharmony_cistatic int
1647db96d56Sopenharmony_ciymd_to_ord(int y, int m, int d);
1657db96d56Sopenharmony_cistatic int
1667db96d56Sopenharmony_ciis_leap_year(int year);
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_cistatic size_t
1697db96d56Sopenharmony_ci_bisect(const int64_t value, const int64_t *arr, size_t size);
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_cistatic int
1727db96d56Sopenharmony_cieject_from_strong_cache(const PyTypeObject *const type, PyObject *key);
1737db96d56Sopenharmony_cistatic void
1747db96d56Sopenharmony_ciclear_strong_cache(const PyTypeObject *const type);
1757db96d56Sopenharmony_cistatic void
1767db96d56Sopenharmony_ciupdate_strong_cache(const PyTypeObject *const type, PyObject *key,
1777db96d56Sopenharmony_ci                    PyObject *zone);
1787db96d56Sopenharmony_cistatic PyObject *
1797db96d56Sopenharmony_cizone_from_strong_cache(const PyTypeObject *const type, PyObject *const key);
1807db96d56Sopenharmony_ci
1817db96d56Sopenharmony_cistatic PyObject *
1827db96d56Sopenharmony_cizoneinfo_new_instance(PyTypeObject *type, PyObject *key)
1837db96d56Sopenharmony_ci{
1847db96d56Sopenharmony_ci    PyObject *file_obj = NULL;
1857db96d56Sopenharmony_ci    PyObject *file_path = NULL;
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci    file_path = PyObject_CallFunctionObjArgs(_tzpath_find_tzfile, key, NULL);
1887db96d56Sopenharmony_ci    if (file_path == NULL) {
1897db96d56Sopenharmony_ci        return NULL;
1907db96d56Sopenharmony_ci    }
1917db96d56Sopenharmony_ci    else if (file_path == Py_None) {
1927db96d56Sopenharmony_ci        file_obj = PyObject_CallMethod(_common_mod, "load_tzdata", "O", key);
1937db96d56Sopenharmony_ci        if (file_obj == NULL) {
1947db96d56Sopenharmony_ci            Py_DECREF(file_path);
1957db96d56Sopenharmony_ci            return NULL;
1967db96d56Sopenharmony_ci        }
1977db96d56Sopenharmony_ci    }
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci    PyObject *self = (PyObject *)(type->tp_alloc(type, 0));
2007db96d56Sopenharmony_ci    if (self == NULL) {
2017db96d56Sopenharmony_ci        goto error;
2027db96d56Sopenharmony_ci    }
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    if (file_obj == NULL) {
2057db96d56Sopenharmony_ci        file_obj = PyObject_CallFunction(io_open, "Os", file_path, "rb");
2067db96d56Sopenharmony_ci        if (file_obj == NULL) {
2077db96d56Sopenharmony_ci            goto error;
2087db96d56Sopenharmony_ci        }
2097db96d56Sopenharmony_ci    }
2107db96d56Sopenharmony_ci
2117db96d56Sopenharmony_ci    if (load_data((PyZoneInfo_ZoneInfo *)self, file_obj)) {
2127db96d56Sopenharmony_ci        goto error;
2137db96d56Sopenharmony_ci    }
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci    PyObject *rv = PyObject_CallMethod(file_obj, "close", NULL);
2167db96d56Sopenharmony_ci    Py_DECREF(file_obj);
2177db96d56Sopenharmony_ci    file_obj = NULL;
2187db96d56Sopenharmony_ci    if (rv == NULL) {
2197db96d56Sopenharmony_ci        goto error;
2207db96d56Sopenharmony_ci    }
2217db96d56Sopenharmony_ci    Py_DECREF(rv);
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci    ((PyZoneInfo_ZoneInfo *)self)->key = key;
2247db96d56Sopenharmony_ci    Py_INCREF(key);
2257db96d56Sopenharmony_ci
2267db96d56Sopenharmony_ci    goto cleanup;
2277db96d56Sopenharmony_cierror:
2287db96d56Sopenharmony_ci    Py_XDECREF(self);
2297db96d56Sopenharmony_ci    self = NULL;
2307db96d56Sopenharmony_cicleanup:
2317db96d56Sopenharmony_ci    if (file_obj != NULL) {
2327db96d56Sopenharmony_ci        PyObject *exc, *val, *tb;
2337db96d56Sopenharmony_ci        PyErr_Fetch(&exc, &val, &tb);
2347db96d56Sopenharmony_ci        PyObject *tmp = PyObject_CallMethod(file_obj, "close", NULL);
2357db96d56Sopenharmony_ci        _PyErr_ChainExceptions(exc, val, tb);
2367db96d56Sopenharmony_ci        if (tmp == NULL) {
2377db96d56Sopenharmony_ci            Py_CLEAR(self);
2387db96d56Sopenharmony_ci        }
2397db96d56Sopenharmony_ci        Py_XDECREF(tmp);
2407db96d56Sopenharmony_ci        Py_DECREF(file_obj);
2417db96d56Sopenharmony_ci    }
2427db96d56Sopenharmony_ci    Py_DECREF(file_path);
2437db96d56Sopenharmony_ci    return self;
2447db96d56Sopenharmony_ci}
2457db96d56Sopenharmony_ci
2467db96d56Sopenharmony_cistatic PyObject *
2477db96d56Sopenharmony_ciget_weak_cache(PyTypeObject *type)
2487db96d56Sopenharmony_ci{
2497db96d56Sopenharmony_ci    if (type == &PyZoneInfo_ZoneInfoType) {
2507db96d56Sopenharmony_ci        return ZONEINFO_WEAK_CACHE;
2517db96d56Sopenharmony_ci    }
2527db96d56Sopenharmony_ci    else {
2537db96d56Sopenharmony_ci        PyObject *cache =
2547db96d56Sopenharmony_ci            PyObject_GetAttrString((PyObject *)type, "_weak_cache");
2557db96d56Sopenharmony_ci        // We are assuming that the type lives at least as long as the function
2567db96d56Sopenharmony_ci        // that calls get_weak_cache, and that it holds a reference to the
2577db96d56Sopenharmony_ci        // cache, so we'll return a "borrowed reference".
2587db96d56Sopenharmony_ci        Py_XDECREF(cache);
2597db96d56Sopenharmony_ci        return cache;
2607db96d56Sopenharmony_ci    }
2617db96d56Sopenharmony_ci}
2627db96d56Sopenharmony_ci
2637db96d56Sopenharmony_cistatic PyObject *
2647db96d56Sopenharmony_cizoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw)
2657db96d56Sopenharmony_ci{
2667db96d56Sopenharmony_ci    PyObject *key = NULL;
2677db96d56Sopenharmony_ci    static char *kwlist[] = {"key", NULL};
2687db96d56Sopenharmony_ci    if (PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &key) == 0) {
2697db96d56Sopenharmony_ci        return NULL;
2707db96d56Sopenharmony_ci    }
2717db96d56Sopenharmony_ci
2727db96d56Sopenharmony_ci    PyObject *instance = zone_from_strong_cache(type, key);
2737db96d56Sopenharmony_ci    if (instance != NULL || PyErr_Occurred()) {
2747db96d56Sopenharmony_ci        return instance;
2757db96d56Sopenharmony_ci    }
2767db96d56Sopenharmony_ci
2777db96d56Sopenharmony_ci    PyObject *weak_cache = get_weak_cache(type);
2787db96d56Sopenharmony_ci    instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None);
2797db96d56Sopenharmony_ci    if (instance == NULL) {
2807db96d56Sopenharmony_ci        return NULL;
2817db96d56Sopenharmony_ci    }
2827db96d56Sopenharmony_ci
2837db96d56Sopenharmony_ci    if (instance == Py_None) {
2847db96d56Sopenharmony_ci        Py_DECREF(instance);
2857db96d56Sopenharmony_ci        PyObject *tmp = zoneinfo_new_instance(type, key);
2867db96d56Sopenharmony_ci        if (tmp == NULL) {
2877db96d56Sopenharmony_ci            return NULL;
2887db96d56Sopenharmony_ci        }
2897db96d56Sopenharmony_ci
2907db96d56Sopenharmony_ci        instance =
2917db96d56Sopenharmony_ci            PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp);
2927db96d56Sopenharmony_ci        Py_DECREF(tmp);
2937db96d56Sopenharmony_ci        if (instance == NULL) {
2947db96d56Sopenharmony_ci            return NULL;
2957db96d56Sopenharmony_ci        }
2967db96d56Sopenharmony_ci        ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
2977db96d56Sopenharmony_ci    }
2987db96d56Sopenharmony_ci
2997db96d56Sopenharmony_ci    update_strong_cache(type, key, instance);
3007db96d56Sopenharmony_ci    return instance;
3017db96d56Sopenharmony_ci}
3027db96d56Sopenharmony_ci
3037db96d56Sopenharmony_cistatic void
3047db96d56Sopenharmony_cizoneinfo_dealloc(PyObject *obj_self)
3057db96d56Sopenharmony_ci{
3067db96d56Sopenharmony_ci    PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
3077db96d56Sopenharmony_ci
3087db96d56Sopenharmony_ci    if (self->weakreflist != NULL) {
3097db96d56Sopenharmony_ci        PyObject_ClearWeakRefs(obj_self);
3107db96d56Sopenharmony_ci    }
3117db96d56Sopenharmony_ci
3127db96d56Sopenharmony_ci    if (self->trans_list_utc != NULL) {
3137db96d56Sopenharmony_ci        PyMem_Free(self->trans_list_utc);
3147db96d56Sopenharmony_ci    }
3157db96d56Sopenharmony_ci
3167db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; i++) {
3177db96d56Sopenharmony_ci        if (self->trans_list_wall[i] != NULL) {
3187db96d56Sopenharmony_ci            PyMem_Free(self->trans_list_wall[i]);
3197db96d56Sopenharmony_ci        }
3207db96d56Sopenharmony_ci    }
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci    if (self->_ttinfos != NULL) {
3237db96d56Sopenharmony_ci        for (size_t i = 0; i < self->num_ttinfos; ++i) {
3247db96d56Sopenharmony_ci            xdecref_ttinfo(&(self->_ttinfos[i]));
3257db96d56Sopenharmony_ci        }
3267db96d56Sopenharmony_ci        PyMem_Free(self->_ttinfos);
3277db96d56Sopenharmony_ci    }
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci    if (self->trans_ttinfos != NULL) {
3307db96d56Sopenharmony_ci        PyMem_Free(self->trans_ttinfos);
3317db96d56Sopenharmony_ci    }
3327db96d56Sopenharmony_ci
3337db96d56Sopenharmony_ci    free_tzrule(&(self->tzrule_after));
3347db96d56Sopenharmony_ci
3357db96d56Sopenharmony_ci    Py_XDECREF(self->key);
3367db96d56Sopenharmony_ci    Py_XDECREF(self->file_repr);
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_ci    Py_TYPE(self)->tp_free((PyObject *)self);
3397db96d56Sopenharmony_ci}
3407db96d56Sopenharmony_ci
3417db96d56Sopenharmony_cistatic PyObject *
3427db96d56Sopenharmony_cizoneinfo_from_file(PyTypeObject *type, PyObject *args, PyObject *kwargs)
3437db96d56Sopenharmony_ci{
3447db96d56Sopenharmony_ci    PyObject *file_obj = NULL;
3457db96d56Sopenharmony_ci    PyObject *file_repr = NULL;
3467db96d56Sopenharmony_ci    PyObject *key = Py_None;
3477db96d56Sopenharmony_ci    PyZoneInfo_ZoneInfo *self = NULL;
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci    static char *kwlist[] = {"", "key", NULL};
3507db96d56Sopenharmony_ci    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &file_obj,
3517db96d56Sopenharmony_ci                                     &key)) {
3527db96d56Sopenharmony_ci        return NULL;
3537db96d56Sopenharmony_ci    }
3547db96d56Sopenharmony_ci
3557db96d56Sopenharmony_ci    PyObject *obj_self = (PyObject *)(type->tp_alloc(type, 0));
3567db96d56Sopenharmony_ci    self = (PyZoneInfo_ZoneInfo *)obj_self;
3577db96d56Sopenharmony_ci    if (self == NULL) {
3587db96d56Sopenharmony_ci        return NULL;
3597db96d56Sopenharmony_ci    }
3607db96d56Sopenharmony_ci
3617db96d56Sopenharmony_ci    file_repr = PyUnicode_FromFormat("%R", file_obj);
3627db96d56Sopenharmony_ci    if (file_repr == NULL) {
3637db96d56Sopenharmony_ci        goto error;
3647db96d56Sopenharmony_ci    }
3657db96d56Sopenharmony_ci
3667db96d56Sopenharmony_ci    if (load_data(self, file_obj)) {
3677db96d56Sopenharmony_ci        goto error;
3687db96d56Sopenharmony_ci    }
3697db96d56Sopenharmony_ci
3707db96d56Sopenharmony_ci    self->source = SOURCE_FILE;
3717db96d56Sopenharmony_ci    self->file_repr = file_repr;
3727db96d56Sopenharmony_ci    self->key = key;
3737db96d56Sopenharmony_ci    Py_INCREF(key);
3747db96d56Sopenharmony_ci
3757db96d56Sopenharmony_ci    return obj_self;
3767db96d56Sopenharmony_cierror:
3777db96d56Sopenharmony_ci    Py_XDECREF(file_repr);
3787db96d56Sopenharmony_ci    Py_XDECREF(self);
3797db96d56Sopenharmony_ci    return NULL;
3807db96d56Sopenharmony_ci}
3817db96d56Sopenharmony_ci
3827db96d56Sopenharmony_cistatic PyObject *
3837db96d56Sopenharmony_cizoneinfo_no_cache(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
3847db96d56Sopenharmony_ci{
3857db96d56Sopenharmony_ci    static char *kwlist[] = {"key", NULL};
3867db96d56Sopenharmony_ci    PyObject *key = NULL;
3877db96d56Sopenharmony_ci    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &key)) {
3887db96d56Sopenharmony_ci        return NULL;
3897db96d56Sopenharmony_ci    }
3907db96d56Sopenharmony_ci
3917db96d56Sopenharmony_ci    PyObject *out = zoneinfo_new_instance(cls, key);
3927db96d56Sopenharmony_ci    if (out != NULL) {
3937db96d56Sopenharmony_ci        ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE;
3947db96d56Sopenharmony_ci    }
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ci    return out;
3977db96d56Sopenharmony_ci}
3987db96d56Sopenharmony_ci
3997db96d56Sopenharmony_cistatic PyObject *
4007db96d56Sopenharmony_cizoneinfo_clear_cache(PyObject *cls, PyObject *args, PyObject *kwargs)
4017db96d56Sopenharmony_ci{
4027db96d56Sopenharmony_ci    PyObject *only_keys = NULL;
4037db96d56Sopenharmony_ci    static char *kwlist[] = {"only_keys", NULL};
4047db96d56Sopenharmony_ci
4057db96d56Sopenharmony_ci    if (!(PyArg_ParseTupleAndKeywords(args, kwargs, "|$O", kwlist,
4067db96d56Sopenharmony_ci                                      &only_keys))) {
4077db96d56Sopenharmony_ci        return NULL;
4087db96d56Sopenharmony_ci    }
4097db96d56Sopenharmony_ci
4107db96d56Sopenharmony_ci    PyTypeObject *type = (PyTypeObject *)cls;
4117db96d56Sopenharmony_ci    PyObject *weak_cache = get_weak_cache(type);
4127db96d56Sopenharmony_ci
4137db96d56Sopenharmony_ci    if (only_keys == NULL || only_keys == Py_None) {
4147db96d56Sopenharmony_ci        PyObject *rv = PyObject_CallMethod(weak_cache, "clear", NULL);
4157db96d56Sopenharmony_ci        if (rv != NULL) {
4167db96d56Sopenharmony_ci            Py_DECREF(rv);
4177db96d56Sopenharmony_ci        }
4187db96d56Sopenharmony_ci
4197db96d56Sopenharmony_ci        clear_strong_cache(type);
4207db96d56Sopenharmony_ci    }
4217db96d56Sopenharmony_ci    else {
4227db96d56Sopenharmony_ci        PyObject *item = NULL;
4237db96d56Sopenharmony_ci        PyObject *pop = PyUnicode_FromString("pop");
4247db96d56Sopenharmony_ci        if (pop == NULL) {
4257db96d56Sopenharmony_ci            return NULL;
4267db96d56Sopenharmony_ci        }
4277db96d56Sopenharmony_ci
4287db96d56Sopenharmony_ci        PyObject *iter = PyObject_GetIter(only_keys);
4297db96d56Sopenharmony_ci        if (iter == NULL) {
4307db96d56Sopenharmony_ci            Py_DECREF(pop);
4317db96d56Sopenharmony_ci            return NULL;
4327db96d56Sopenharmony_ci        }
4337db96d56Sopenharmony_ci
4347db96d56Sopenharmony_ci        while ((item = PyIter_Next(iter))) {
4357db96d56Sopenharmony_ci            // Remove from strong cache
4367db96d56Sopenharmony_ci            if (eject_from_strong_cache(type, item) < 0) {
4377db96d56Sopenharmony_ci                Py_DECREF(item);
4387db96d56Sopenharmony_ci                break;
4397db96d56Sopenharmony_ci            }
4407db96d56Sopenharmony_ci
4417db96d56Sopenharmony_ci            // Remove from weak cache
4427db96d56Sopenharmony_ci            PyObject *tmp = PyObject_CallMethodObjArgs(weak_cache, pop, item,
4437db96d56Sopenharmony_ci                                                       Py_None, NULL);
4447db96d56Sopenharmony_ci
4457db96d56Sopenharmony_ci            Py_DECREF(item);
4467db96d56Sopenharmony_ci            if (tmp == NULL) {
4477db96d56Sopenharmony_ci                break;
4487db96d56Sopenharmony_ci            }
4497db96d56Sopenharmony_ci            Py_DECREF(tmp);
4507db96d56Sopenharmony_ci        }
4517db96d56Sopenharmony_ci        Py_DECREF(iter);
4527db96d56Sopenharmony_ci        Py_DECREF(pop);
4537db96d56Sopenharmony_ci    }
4547db96d56Sopenharmony_ci
4557db96d56Sopenharmony_ci    if (PyErr_Occurred()) {
4567db96d56Sopenharmony_ci        return NULL;
4577db96d56Sopenharmony_ci    }
4587db96d56Sopenharmony_ci
4597db96d56Sopenharmony_ci    Py_RETURN_NONE;
4607db96d56Sopenharmony_ci}
4617db96d56Sopenharmony_ci
4627db96d56Sopenharmony_cistatic PyObject *
4637db96d56Sopenharmony_cizoneinfo_utcoffset(PyObject *self, PyObject *dt)
4647db96d56Sopenharmony_ci{
4657db96d56Sopenharmony_ci    _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt);
4667db96d56Sopenharmony_ci    if (tti == NULL) {
4677db96d56Sopenharmony_ci        return NULL;
4687db96d56Sopenharmony_ci    }
4697db96d56Sopenharmony_ci    Py_INCREF(tti->utcoff);
4707db96d56Sopenharmony_ci    return tti->utcoff;
4717db96d56Sopenharmony_ci}
4727db96d56Sopenharmony_ci
4737db96d56Sopenharmony_cistatic PyObject *
4747db96d56Sopenharmony_cizoneinfo_dst(PyObject *self, PyObject *dt)
4757db96d56Sopenharmony_ci{
4767db96d56Sopenharmony_ci    _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt);
4777db96d56Sopenharmony_ci    if (tti == NULL) {
4787db96d56Sopenharmony_ci        return NULL;
4797db96d56Sopenharmony_ci    }
4807db96d56Sopenharmony_ci    Py_INCREF(tti->dstoff);
4817db96d56Sopenharmony_ci    return tti->dstoff;
4827db96d56Sopenharmony_ci}
4837db96d56Sopenharmony_ci
4847db96d56Sopenharmony_cistatic PyObject *
4857db96d56Sopenharmony_cizoneinfo_tzname(PyObject *self, PyObject *dt)
4867db96d56Sopenharmony_ci{
4877db96d56Sopenharmony_ci    _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt);
4887db96d56Sopenharmony_ci    if (tti == NULL) {
4897db96d56Sopenharmony_ci        return NULL;
4907db96d56Sopenharmony_ci    }
4917db96d56Sopenharmony_ci    Py_INCREF(tti->tzname);
4927db96d56Sopenharmony_ci    return tti->tzname;
4937db96d56Sopenharmony_ci}
4947db96d56Sopenharmony_ci
4957db96d56Sopenharmony_ci#define GET_DT_TZINFO PyDateTime_DATE_GET_TZINFO
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_cistatic PyObject *
4987db96d56Sopenharmony_cizoneinfo_fromutc(PyObject *obj_self, PyObject *dt)
4997db96d56Sopenharmony_ci{
5007db96d56Sopenharmony_ci    if (!PyDateTime_Check(dt)) {
5017db96d56Sopenharmony_ci        PyErr_SetString(PyExc_TypeError,
5027db96d56Sopenharmony_ci                        "fromutc: argument must be a datetime");
5037db96d56Sopenharmony_ci        return NULL;
5047db96d56Sopenharmony_ci    }
5057db96d56Sopenharmony_ci    if (GET_DT_TZINFO(dt) != obj_self) {
5067db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError,
5077db96d56Sopenharmony_ci                        "fromutc: dt.tzinfo "
5087db96d56Sopenharmony_ci                        "is not self");
5097db96d56Sopenharmony_ci        return NULL;
5107db96d56Sopenharmony_ci    }
5117db96d56Sopenharmony_ci
5127db96d56Sopenharmony_ci    PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
5137db96d56Sopenharmony_ci
5147db96d56Sopenharmony_ci    int64_t timestamp;
5157db96d56Sopenharmony_ci    if (get_local_timestamp(dt, &timestamp)) {
5167db96d56Sopenharmony_ci        return NULL;
5177db96d56Sopenharmony_ci    }
5187db96d56Sopenharmony_ci    size_t num_trans = self->num_transitions;
5197db96d56Sopenharmony_ci
5207db96d56Sopenharmony_ci    _ttinfo *tti = NULL;
5217db96d56Sopenharmony_ci    unsigned char fold = 0;
5227db96d56Sopenharmony_ci
5237db96d56Sopenharmony_ci    if (num_trans >= 1 && timestamp < self->trans_list_utc[0]) {
5247db96d56Sopenharmony_ci        tti = self->ttinfo_before;
5257db96d56Sopenharmony_ci    }
5267db96d56Sopenharmony_ci    else if (num_trans == 0 ||
5277db96d56Sopenharmony_ci             timestamp > self->trans_list_utc[num_trans - 1]) {
5287db96d56Sopenharmony_ci        tti = find_tzrule_ttinfo_fromutc(&(self->tzrule_after), timestamp,
5297db96d56Sopenharmony_ci                                         PyDateTime_GET_YEAR(dt), &fold);
5307db96d56Sopenharmony_ci
5317db96d56Sopenharmony_ci        // Immediately after the last manual transition, the fold/gap is
5327db96d56Sopenharmony_ci        // between self->trans_ttinfos[num_transitions - 1] and whatever
5337db96d56Sopenharmony_ci        // ttinfo applies immediately after the last transition, not between
5347db96d56Sopenharmony_ci        // the STD and DST rules in the tzrule_after, so we may need to
5357db96d56Sopenharmony_ci        // adjust the fold value.
5367db96d56Sopenharmony_ci        if (num_trans) {
5377db96d56Sopenharmony_ci            _ttinfo *tti_prev = NULL;
5387db96d56Sopenharmony_ci            if (num_trans == 1) {
5397db96d56Sopenharmony_ci                tti_prev = self->ttinfo_before;
5407db96d56Sopenharmony_ci            }
5417db96d56Sopenharmony_ci            else {
5427db96d56Sopenharmony_ci                tti_prev = self->trans_ttinfos[num_trans - 2];
5437db96d56Sopenharmony_ci            }
5447db96d56Sopenharmony_ci            int64_t diff = tti_prev->utcoff_seconds - tti->utcoff_seconds;
5457db96d56Sopenharmony_ci            if (diff > 0 &&
5467db96d56Sopenharmony_ci                timestamp < (self->trans_list_utc[num_trans - 1] + diff)) {
5477db96d56Sopenharmony_ci                fold = 1;
5487db96d56Sopenharmony_ci            }
5497db96d56Sopenharmony_ci        }
5507db96d56Sopenharmony_ci    }
5517db96d56Sopenharmony_ci    else {
5527db96d56Sopenharmony_ci        size_t idx = _bisect(timestamp, self->trans_list_utc, num_trans);
5537db96d56Sopenharmony_ci        _ttinfo *tti_prev = NULL;
5547db96d56Sopenharmony_ci
5557db96d56Sopenharmony_ci        if (idx >= 2) {
5567db96d56Sopenharmony_ci            tti_prev = self->trans_ttinfos[idx - 2];
5577db96d56Sopenharmony_ci            tti = self->trans_ttinfos[idx - 1];
5587db96d56Sopenharmony_ci        }
5597db96d56Sopenharmony_ci        else {
5607db96d56Sopenharmony_ci            tti_prev = self->ttinfo_before;
5617db96d56Sopenharmony_ci            tti = self->trans_ttinfos[0];
5627db96d56Sopenharmony_ci        }
5637db96d56Sopenharmony_ci
5647db96d56Sopenharmony_ci        // Detect fold
5657db96d56Sopenharmony_ci        int64_t shift =
5667db96d56Sopenharmony_ci            (int64_t)(tti_prev->utcoff_seconds - tti->utcoff_seconds);
5677db96d56Sopenharmony_ci        if (shift > (timestamp - self->trans_list_utc[idx - 1])) {
5687db96d56Sopenharmony_ci            fold = 1;
5697db96d56Sopenharmony_ci        }
5707db96d56Sopenharmony_ci    }
5717db96d56Sopenharmony_ci
5727db96d56Sopenharmony_ci    PyObject *tmp = PyNumber_Add(dt, tti->utcoff);
5737db96d56Sopenharmony_ci    if (tmp == NULL) {
5747db96d56Sopenharmony_ci        return NULL;
5757db96d56Sopenharmony_ci    }
5767db96d56Sopenharmony_ci
5777db96d56Sopenharmony_ci    if (fold) {
5787db96d56Sopenharmony_ci        if (PyDateTime_CheckExact(tmp)) {
5797db96d56Sopenharmony_ci            ((PyDateTime_DateTime *)tmp)->fold = 1;
5807db96d56Sopenharmony_ci            dt = tmp;
5817db96d56Sopenharmony_ci        }
5827db96d56Sopenharmony_ci        else {
5837db96d56Sopenharmony_ci            PyObject *replace = PyObject_GetAttrString(tmp, "replace");
5847db96d56Sopenharmony_ci            PyObject *args = PyTuple_New(0);
5857db96d56Sopenharmony_ci            PyObject *kwargs = PyDict_New();
5867db96d56Sopenharmony_ci
5877db96d56Sopenharmony_ci            Py_DECREF(tmp);
5887db96d56Sopenharmony_ci            if (args == NULL || kwargs == NULL || replace == NULL) {
5897db96d56Sopenharmony_ci                Py_XDECREF(args);
5907db96d56Sopenharmony_ci                Py_XDECREF(kwargs);
5917db96d56Sopenharmony_ci                Py_XDECREF(replace);
5927db96d56Sopenharmony_ci                return NULL;
5937db96d56Sopenharmony_ci            }
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci            dt = NULL;
5967db96d56Sopenharmony_ci            if (!PyDict_SetItemString(kwargs, "fold", _PyLong_GetOne())) {
5977db96d56Sopenharmony_ci                dt = PyObject_Call(replace, args, kwargs);
5987db96d56Sopenharmony_ci            }
5997db96d56Sopenharmony_ci
6007db96d56Sopenharmony_ci            Py_DECREF(args);
6017db96d56Sopenharmony_ci            Py_DECREF(kwargs);
6027db96d56Sopenharmony_ci            Py_DECREF(replace);
6037db96d56Sopenharmony_ci
6047db96d56Sopenharmony_ci            if (dt == NULL) {
6057db96d56Sopenharmony_ci                return NULL;
6067db96d56Sopenharmony_ci            }
6077db96d56Sopenharmony_ci        }
6087db96d56Sopenharmony_ci    }
6097db96d56Sopenharmony_ci    else {
6107db96d56Sopenharmony_ci        dt = tmp;
6117db96d56Sopenharmony_ci    }
6127db96d56Sopenharmony_ci    return dt;
6137db96d56Sopenharmony_ci}
6147db96d56Sopenharmony_ci
6157db96d56Sopenharmony_cistatic PyObject *
6167db96d56Sopenharmony_cizoneinfo_repr(PyZoneInfo_ZoneInfo *self)
6177db96d56Sopenharmony_ci{
6187db96d56Sopenharmony_ci    PyObject *rv = NULL;
6197db96d56Sopenharmony_ci    const char *type_name = Py_TYPE((PyObject *)self)->tp_name;
6207db96d56Sopenharmony_ci    if (!(self->key == Py_None)) {
6217db96d56Sopenharmony_ci        rv = PyUnicode_FromFormat("%s(key=%R)", type_name, self->key);
6227db96d56Sopenharmony_ci    }
6237db96d56Sopenharmony_ci    else {
6247db96d56Sopenharmony_ci        assert(PyUnicode_Check(self->file_repr));
6257db96d56Sopenharmony_ci        rv = PyUnicode_FromFormat("%s.from_file(%U)", type_name,
6267db96d56Sopenharmony_ci                                  self->file_repr);
6277db96d56Sopenharmony_ci    }
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci    return rv;
6307db96d56Sopenharmony_ci}
6317db96d56Sopenharmony_ci
6327db96d56Sopenharmony_cistatic PyObject *
6337db96d56Sopenharmony_cizoneinfo_str(PyZoneInfo_ZoneInfo *self)
6347db96d56Sopenharmony_ci{
6357db96d56Sopenharmony_ci    if (!(self->key == Py_None)) {
6367db96d56Sopenharmony_ci        Py_INCREF(self->key);
6377db96d56Sopenharmony_ci        return self->key;
6387db96d56Sopenharmony_ci    }
6397db96d56Sopenharmony_ci    else {
6407db96d56Sopenharmony_ci        return zoneinfo_repr(self);
6417db96d56Sopenharmony_ci    }
6427db96d56Sopenharmony_ci}
6437db96d56Sopenharmony_ci
6447db96d56Sopenharmony_ci/* Pickles the ZoneInfo object by key and source.
6457db96d56Sopenharmony_ci *
6467db96d56Sopenharmony_ci * ZoneInfo objects are pickled by reference to the TZif file that they came
6477db96d56Sopenharmony_ci * from, which means that the exact transitions may be different or the file
6487db96d56Sopenharmony_ci * may not un-pickle if the data has changed on disk in the interim.
6497db96d56Sopenharmony_ci *
6507db96d56Sopenharmony_ci * It is necessary to include a bit indicating whether or not the object
6517db96d56Sopenharmony_ci * was constructed from the cache, because from-cache objects will hit the
6527db96d56Sopenharmony_ci * unpickling process's cache, whereas no-cache objects will bypass it.
6537db96d56Sopenharmony_ci *
6547db96d56Sopenharmony_ci * Objects constructed from ZoneInfo.from_file cannot be pickled.
6557db96d56Sopenharmony_ci */
6567db96d56Sopenharmony_cistatic PyObject *
6577db96d56Sopenharmony_cizoneinfo_reduce(PyObject *obj_self, PyObject *unused)
6587db96d56Sopenharmony_ci{
6597db96d56Sopenharmony_ci    PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
6607db96d56Sopenharmony_ci    if (self->source == SOURCE_FILE) {
6617db96d56Sopenharmony_ci        // Objects constructed from files cannot be pickled.
6627db96d56Sopenharmony_ci        PyObject *pickle = PyImport_ImportModule("pickle");
6637db96d56Sopenharmony_ci        if (pickle == NULL) {
6647db96d56Sopenharmony_ci            return NULL;
6657db96d56Sopenharmony_ci        }
6667db96d56Sopenharmony_ci
6677db96d56Sopenharmony_ci        PyObject *pickle_error =
6687db96d56Sopenharmony_ci            PyObject_GetAttrString(pickle, "PicklingError");
6697db96d56Sopenharmony_ci        Py_DECREF(pickle);
6707db96d56Sopenharmony_ci        if (pickle_error == NULL) {
6717db96d56Sopenharmony_ci            return NULL;
6727db96d56Sopenharmony_ci        }
6737db96d56Sopenharmony_ci
6747db96d56Sopenharmony_ci        PyErr_Format(pickle_error,
6757db96d56Sopenharmony_ci                     "Cannot pickle a ZoneInfo file from a file stream.");
6767db96d56Sopenharmony_ci        Py_DECREF(pickle_error);
6777db96d56Sopenharmony_ci        return NULL;
6787db96d56Sopenharmony_ci    }
6797db96d56Sopenharmony_ci
6807db96d56Sopenharmony_ci    unsigned char from_cache = self->source == SOURCE_CACHE ? 1 : 0;
6817db96d56Sopenharmony_ci    PyObject *constructor = PyObject_GetAttrString(obj_self, "_unpickle");
6827db96d56Sopenharmony_ci
6837db96d56Sopenharmony_ci    if (constructor == NULL) {
6847db96d56Sopenharmony_ci        return NULL;
6857db96d56Sopenharmony_ci    }
6867db96d56Sopenharmony_ci
6877db96d56Sopenharmony_ci    PyObject *rv = Py_BuildValue("O(OB)", constructor, self->key, from_cache);
6887db96d56Sopenharmony_ci    Py_DECREF(constructor);
6897db96d56Sopenharmony_ci    return rv;
6907db96d56Sopenharmony_ci}
6917db96d56Sopenharmony_ci
6927db96d56Sopenharmony_cistatic PyObject *
6937db96d56Sopenharmony_cizoneinfo__unpickle(PyTypeObject *cls, PyObject *args)
6947db96d56Sopenharmony_ci{
6957db96d56Sopenharmony_ci    PyObject *key;
6967db96d56Sopenharmony_ci    unsigned char from_cache;
6977db96d56Sopenharmony_ci    if (!PyArg_ParseTuple(args, "OB", &key, &from_cache)) {
6987db96d56Sopenharmony_ci        return NULL;
6997db96d56Sopenharmony_ci    }
7007db96d56Sopenharmony_ci
7017db96d56Sopenharmony_ci    if (from_cache) {
7027db96d56Sopenharmony_ci        PyObject *val_args = Py_BuildValue("(O)", key);
7037db96d56Sopenharmony_ci        if (val_args == NULL) {
7047db96d56Sopenharmony_ci            return NULL;
7057db96d56Sopenharmony_ci        }
7067db96d56Sopenharmony_ci
7077db96d56Sopenharmony_ci        PyObject *rv = zoneinfo_new(cls, val_args, NULL);
7087db96d56Sopenharmony_ci
7097db96d56Sopenharmony_ci        Py_DECREF(val_args);
7107db96d56Sopenharmony_ci        return rv;
7117db96d56Sopenharmony_ci    }
7127db96d56Sopenharmony_ci    else {
7137db96d56Sopenharmony_ci        return zoneinfo_new_instance(cls, key);
7147db96d56Sopenharmony_ci    }
7157db96d56Sopenharmony_ci}
7167db96d56Sopenharmony_ci
7177db96d56Sopenharmony_ci/* It is relatively expensive to construct new timedelta objects, and in most
7187db96d56Sopenharmony_ci * cases we're looking at a relatively small number of timedeltas, such as
7197db96d56Sopenharmony_ci * integer number of hours, etc. We will keep a cache so that we construct
7207db96d56Sopenharmony_ci * a minimal number of these.
7217db96d56Sopenharmony_ci *
7227db96d56Sopenharmony_ci * Possibly this should be replaced with an LRU cache so that it's not possible
7237db96d56Sopenharmony_ci * for the memory usage to explode from this, but in order for this to be a
7247db96d56Sopenharmony_ci * serious problem, one would need to deliberately craft a malicious time zone
7257db96d56Sopenharmony_ci * file with many distinct offsets. As of tzdb 2019c, loading every single zone
7267db96d56Sopenharmony_ci * fills the cache with ~450 timedeltas for a total size of ~12kB.
7277db96d56Sopenharmony_ci *
7287db96d56Sopenharmony_ci * This returns a new reference to the timedelta.
7297db96d56Sopenharmony_ci */
7307db96d56Sopenharmony_cistatic PyObject *
7317db96d56Sopenharmony_ciload_timedelta(long seconds)
7327db96d56Sopenharmony_ci{
7337db96d56Sopenharmony_ci    PyObject *rv;
7347db96d56Sopenharmony_ci    PyObject *pyoffset = PyLong_FromLong(seconds);
7357db96d56Sopenharmony_ci    if (pyoffset == NULL) {
7367db96d56Sopenharmony_ci        return NULL;
7377db96d56Sopenharmony_ci    }
7387db96d56Sopenharmony_ci    rv = PyDict_GetItemWithError(TIMEDELTA_CACHE, pyoffset);
7397db96d56Sopenharmony_ci    if (rv == NULL) {
7407db96d56Sopenharmony_ci        if (PyErr_Occurred()) {
7417db96d56Sopenharmony_ci            goto error;
7427db96d56Sopenharmony_ci        }
7437db96d56Sopenharmony_ci        PyObject *tmp = PyDateTimeAPI->Delta_FromDelta(
7447db96d56Sopenharmony_ci            0, seconds, 0, 1, PyDateTimeAPI->DeltaType);
7457db96d56Sopenharmony_ci
7467db96d56Sopenharmony_ci        if (tmp == NULL) {
7477db96d56Sopenharmony_ci            goto error;
7487db96d56Sopenharmony_ci        }
7497db96d56Sopenharmony_ci
7507db96d56Sopenharmony_ci        rv = PyDict_SetDefault(TIMEDELTA_CACHE, pyoffset, tmp);
7517db96d56Sopenharmony_ci        Py_DECREF(tmp);
7527db96d56Sopenharmony_ci    }
7537db96d56Sopenharmony_ci
7547db96d56Sopenharmony_ci    Py_XINCREF(rv);
7557db96d56Sopenharmony_ci    Py_DECREF(pyoffset);
7567db96d56Sopenharmony_ci    return rv;
7577db96d56Sopenharmony_cierror:
7587db96d56Sopenharmony_ci    Py_DECREF(pyoffset);
7597db96d56Sopenharmony_ci    return NULL;
7607db96d56Sopenharmony_ci}
7617db96d56Sopenharmony_ci
7627db96d56Sopenharmony_ci/* Constructor for _ttinfo object - this starts by initializing the _ttinfo
7637db96d56Sopenharmony_ci * to { NULL, NULL, NULL }, so that Py_XDECREF will work on partially
7647db96d56Sopenharmony_ci * initialized _ttinfo objects.
7657db96d56Sopenharmony_ci */
7667db96d56Sopenharmony_cistatic int
7677db96d56Sopenharmony_cibuild_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out)
7687db96d56Sopenharmony_ci{
7697db96d56Sopenharmony_ci    out->utcoff = NULL;
7707db96d56Sopenharmony_ci    out->dstoff = NULL;
7717db96d56Sopenharmony_ci    out->tzname = NULL;
7727db96d56Sopenharmony_ci
7737db96d56Sopenharmony_ci    out->utcoff_seconds = utcoffset;
7747db96d56Sopenharmony_ci    out->utcoff = load_timedelta(utcoffset);
7757db96d56Sopenharmony_ci    if (out->utcoff == NULL) {
7767db96d56Sopenharmony_ci        return -1;
7777db96d56Sopenharmony_ci    }
7787db96d56Sopenharmony_ci
7797db96d56Sopenharmony_ci    out->dstoff = load_timedelta(dstoffset);
7807db96d56Sopenharmony_ci    if (out->dstoff == NULL) {
7817db96d56Sopenharmony_ci        return -1;
7827db96d56Sopenharmony_ci    }
7837db96d56Sopenharmony_ci
7847db96d56Sopenharmony_ci    out->tzname = tzname;
7857db96d56Sopenharmony_ci    Py_INCREF(tzname);
7867db96d56Sopenharmony_ci
7877db96d56Sopenharmony_ci    return 0;
7887db96d56Sopenharmony_ci}
7897db96d56Sopenharmony_ci
7907db96d56Sopenharmony_ci/* Decrease reference count on any non-NULL members of a _ttinfo  */
7917db96d56Sopenharmony_cistatic void
7927db96d56Sopenharmony_cixdecref_ttinfo(_ttinfo *ttinfo)
7937db96d56Sopenharmony_ci{
7947db96d56Sopenharmony_ci    if (ttinfo != NULL) {
7957db96d56Sopenharmony_ci        Py_XDECREF(ttinfo->utcoff);
7967db96d56Sopenharmony_ci        Py_XDECREF(ttinfo->dstoff);
7977db96d56Sopenharmony_ci        Py_XDECREF(ttinfo->tzname);
7987db96d56Sopenharmony_ci    }
7997db96d56Sopenharmony_ci}
8007db96d56Sopenharmony_ci
8017db96d56Sopenharmony_ci/* Equality function for _ttinfo. */
8027db96d56Sopenharmony_cistatic int
8037db96d56Sopenharmony_cittinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1)
8047db96d56Sopenharmony_ci{
8057db96d56Sopenharmony_ci    int rv;
8067db96d56Sopenharmony_ci    if ((rv = PyObject_RichCompareBool(tti0->utcoff, tti1->utcoff, Py_EQ)) <
8077db96d56Sopenharmony_ci        1) {
8087db96d56Sopenharmony_ci        goto end;
8097db96d56Sopenharmony_ci    }
8107db96d56Sopenharmony_ci
8117db96d56Sopenharmony_ci    if ((rv = PyObject_RichCompareBool(tti0->dstoff, tti1->dstoff, Py_EQ)) <
8127db96d56Sopenharmony_ci        1) {
8137db96d56Sopenharmony_ci        goto end;
8147db96d56Sopenharmony_ci    }
8157db96d56Sopenharmony_ci
8167db96d56Sopenharmony_ci    if ((rv = PyObject_RichCompareBool(tti0->tzname, tti1->tzname, Py_EQ)) <
8177db96d56Sopenharmony_ci        1) {
8187db96d56Sopenharmony_ci        goto end;
8197db96d56Sopenharmony_ci    }
8207db96d56Sopenharmony_ciend:
8217db96d56Sopenharmony_ci    return rv;
8227db96d56Sopenharmony_ci}
8237db96d56Sopenharmony_ci
8247db96d56Sopenharmony_ci/* Given a file-like object, this populates a ZoneInfo object
8257db96d56Sopenharmony_ci *
8267db96d56Sopenharmony_ci * The current version calls into a Python function to read the data from
8277db96d56Sopenharmony_ci * file into Python objects, and this translates those Python objects into
8287db96d56Sopenharmony_ci * C values and calculates derived values (e.g. dstoff) in C.
8297db96d56Sopenharmony_ci *
8307db96d56Sopenharmony_ci * This returns 0 on success and -1 on failure.
8317db96d56Sopenharmony_ci *
8327db96d56Sopenharmony_ci * The function will never return while `self` is partially initialized —
8337db96d56Sopenharmony_ci * the object only needs to be freed / deallocated if this succeeds.
8347db96d56Sopenharmony_ci */
8357db96d56Sopenharmony_cistatic int
8367db96d56Sopenharmony_ciload_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj)
8377db96d56Sopenharmony_ci{
8387db96d56Sopenharmony_ci    PyObject *data_tuple = NULL;
8397db96d56Sopenharmony_ci
8407db96d56Sopenharmony_ci    long *utcoff = NULL;
8417db96d56Sopenharmony_ci    long *dstoff = NULL;
8427db96d56Sopenharmony_ci    size_t *trans_idx = NULL;
8437db96d56Sopenharmony_ci    unsigned char *isdst = NULL;
8447db96d56Sopenharmony_ci
8457db96d56Sopenharmony_ci    self->trans_list_utc = NULL;
8467db96d56Sopenharmony_ci    self->trans_list_wall[0] = NULL;
8477db96d56Sopenharmony_ci    self->trans_list_wall[1] = NULL;
8487db96d56Sopenharmony_ci    self->trans_ttinfos = NULL;
8497db96d56Sopenharmony_ci    self->_ttinfos = NULL;
8507db96d56Sopenharmony_ci    self->file_repr = NULL;
8517db96d56Sopenharmony_ci
8527db96d56Sopenharmony_ci    size_t ttinfos_allocated = 0;
8537db96d56Sopenharmony_ci
8547db96d56Sopenharmony_ci    data_tuple = PyObject_CallMethod(_common_mod, "load_data", "O", file_obj);
8557db96d56Sopenharmony_ci
8567db96d56Sopenharmony_ci    if (data_tuple == NULL) {
8577db96d56Sopenharmony_ci        goto error;
8587db96d56Sopenharmony_ci    }
8597db96d56Sopenharmony_ci
8607db96d56Sopenharmony_ci    if (!PyTuple_CheckExact(data_tuple)) {
8617db96d56Sopenharmony_ci        PyErr_Format(PyExc_TypeError, "Invalid data result type: %r",
8627db96d56Sopenharmony_ci                     data_tuple);
8637db96d56Sopenharmony_ci        goto error;
8647db96d56Sopenharmony_ci    }
8657db96d56Sopenharmony_ci
8667db96d56Sopenharmony_ci    // Unpack the data tuple
8677db96d56Sopenharmony_ci    PyObject *trans_idx_list = PyTuple_GetItem(data_tuple, 0);
8687db96d56Sopenharmony_ci    if (trans_idx_list == NULL) {
8697db96d56Sopenharmony_ci        goto error;
8707db96d56Sopenharmony_ci    }
8717db96d56Sopenharmony_ci
8727db96d56Sopenharmony_ci    PyObject *trans_utc = PyTuple_GetItem(data_tuple, 1);
8737db96d56Sopenharmony_ci    if (trans_utc == NULL) {
8747db96d56Sopenharmony_ci        goto error;
8757db96d56Sopenharmony_ci    }
8767db96d56Sopenharmony_ci
8777db96d56Sopenharmony_ci    PyObject *utcoff_list = PyTuple_GetItem(data_tuple, 2);
8787db96d56Sopenharmony_ci    if (utcoff_list == NULL) {
8797db96d56Sopenharmony_ci        goto error;
8807db96d56Sopenharmony_ci    }
8817db96d56Sopenharmony_ci
8827db96d56Sopenharmony_ci    PyObject *isdst_list = PyTuple_GetItem(data_tuple, 3);
8837db96d56Sopenharmony_ci    if (isdst_list == NULL) {
8847db96d56Sopenharmony_ci        goto error;
8857db96d56Sopenharmony_ci    }
8867db96d56Sopenharmony_ci
8877db96d56Sopenharmony_ci    PyObject *abbr = PyTuple_GetItem(data_tuple, 4);
8887db96d56Sopenharmony_ci    if (abbr == NULL) {
8897db96d56Sopenharmony_ci        goto error;
8907db96d56Sopenharmony_ci    }
8917db96d56Sopenharmony_ci
8927db96d56Sopenharmony_ci    PyObject *tz_str = PyTuple_GetItem(data_tuple, 5);
8937db96d56Sopenharmony_ci    if (tz_str == NULL) {
8947db96d56Sopenharmony_ci        goto error;
8957db96d56Sopenharmony_ci    }
8967db96d56Sopenharmony_ci
8977db96d56Sopenharmony_ci    // Load the relevant sizes
8987db96d56Sopenharmony_ci    Py_ssize_t num_transitions = PyTuple_Size(trans_utc);
8997db96d56Sopenharmony_ci    if (num_transitions < 0) {
9007db96d56Sopenharmony_ci        goto error;
9017db96d56Sopenharmony_ci    }
9027db96d56Sopenharmony_ci
9037db96d56Sopenharmony_ci    Py_ssize_t num_ttinfos = PyTuple_Size(utcoff_list);
9047db96d56Sopenharmony_ci    if (num_ttinfos < 0) {
9057db96d56Sopenharmony_ci        goto error;
9067db96d56Sopenharmony_ci    }
9077db96d56Sopenharmony_ci
9087db96d56Sopenharmony_ci    self->num_transitions = (size_t)num_transitions;
9097db96d56Sopenharmony_ci    self->num_ttinfos = (size_t)num_ttinfos;
9107db96d56Sopenharmony_ci
9117db96d56Sopenharmony_ci    // Load the transition indices and list
9127db96d56Sopenharmony_ci    self->trans_list_utc =
9137db96d56Sopenharmony_ci        PyMem_Malloc(self->num_transitions * sizeof(int64_t));
9147db96d56Sopenharmony_ci    if (self->trans_list_utc == NULL) {
9157db96d56Sopenharmony_ci        goto error;
9167db96d56Sopenharmony_ci    }
9177db96d56Sopenharmony_ci    trans_idx = PyMem_Malloc(self->num_transitions * sizeof(Py_ssize_t));
9187db96d56Sopenharmony_ci    if (trans_idx == NULL) {
9197db96d56Sopenharmony_ci        goto error;
9207db96d56Sopenharmony_ci    }
9217db96d56Sopenharmony_ci
9227db96d56Sopenharmony_ci    for (size_t i = 0; i < self->num_transitions; ++i) {
9237db96d56Sopenharmony_ci        PyObject *num = PyTuple_GetItem(trans_utc, i);
9247db96d56Sopenharmony_ci        if (num == NULL) {
9257db96d56Sopenharmony_ci            goto error;
9267db96d56Sopenharmony_ci        }
9277db96d56Sopenharmony_ci        self->trans_list_utc[i] = PyLong_AsLongLong(num);
9287db96d56Sopenharmony_ci        if (self->trans_list_utc[i] == -1 && PyErr_Occurred()) {
9297db96d56Sopenharmony_ci            goto error;
9307db96d56Sopenharmony_ci        }
9317db96d56Sopenharmony_ci
9327db96d56Sopenharmony_ci        num = PyTuple_GetItem(trans_idx_list, i);
9337db96d56Sopenharmony_ci        if (num == NULL) {
9347db96d56Sopenharmony_ci            goto error;
9357db96d56Sopenharmony_ci        }
9367db96d56Sopenharmony_ci
9377db96d56Sopenharmony_ci        Py_ssize_t cur_trans_idx = PyLong_AsSsize_t(num);
9387db96d56Sopenharmony_ci        if (cur_trans_idx == -1) {
9397db96d56Sopenharmony_ci            goto error;
9407db96d56Sopenharmony_ci        }
9417db96d56Sopenharmony_ci
9427db96d56Sopenharmony_ci        trans_idx[i] = (size_t)cur_trans_idx;
9437db96d56Sopenharmony_ci        if (trans_idx[i] > self->num_ttinfos) {
9447db96d56Sopenharmony_ci            PyErr_Format(
9457db96d56Sopenharmony_ci                PyExc_ValueError,
9467db96d56Sopenharmony_ci                "Invalid transition index found while reading TZif: %zd",
9477db96d56Sopenharmony_ci                cur_trans_idx);
9487db96d56Sopenharmony_ci
9497db96d56Sopenharmony_ci            goto error;
9507db96d56Sopenharmony_ci        }
9517db96d56Sopenharmony_ci    }
9527db96d56Sopenharmony_ci
9537db96d56Sopenharmony_ci    // Load UTC offsets and isdst (size num_ttinfos)
9547db96d56Sopenharmony_ci    utcoff = PyMem_Malloc(self->num_ttinfos * sizeof(long));
9557db96d56Sopenharmony_ci    isdst = PyMem_Malloc(self->num_ttinfos * sizeof(unsigned char));
9567db96d56Sopenharmony_ci
9577db96d56Sopenharmony_ci    if (utcoff == NULL || isdst == NULL) {
9587db96d56Sopenharmony_ci        goto error;
9597db96d56Sopenharmony_ci    }
9607db96d56Sopenharmony_ci    for (size_t i = 0; i < self->num_ttinfos; ++i) {
9617db96d56Sopenharmony_ci        PyObject *num = PyTuple_GetItem(utcoff_list, i);
9627db96d56Sopenharmony_ci        if (num == NULL) {
9637db96d56Sopenharmony_ci            goto error;
9647db96d56Sopenharmony_ci        }
9657db96d56Sopenharmony_ci
9667db96d56Sopenharmony_ci        utcoff[i] = PyLong_AsLong(num);
9677db96d56Sopenharmony_ci        if (utcoff[i] == -1 && PyErr_Occurred()) {
9687db96d56Sopenharmony_ci            goto error;
9697db96d56Sopenharmony_ci        }
9707db96d56Sopenharmony_ci
9717db96d56Sopenharmony_ci        num = PyTuple_GetItem(isdst_list, i);
9727db96d56Sopenharmony_ci        if (num == NULL) {
9737db96d56Sopenharmony_ci            goto error;
9747db96d56Sopenharmony_ci        }
9757db96d56Sopenharmony_ci
9767db96d56Sopenharmony_ci        int isdst_with_error = PyObject_IsTrue(num);
9777db96d56Sopenharmony_ci        if (isdst_with_error == -1) {
9787db96d56Sopenharmony_ci            goto error;
9797db96d56Sopenharmony_ci        }
9807db96d56Sopenharmony_ci        else {
9817db96d56Sopenharmony_ci            isdst[i] = (unsigned char)isdst_with_error;
9827db96d56Sopenharmony_ci        }
9837db96d56Sopenharmony_ci    }
9847db96d56Sopenharmony_ci
9857db96d56Sopenharmony_ci    dstoff = PyMem_Calloc(self->num_ttinfos, sizeof(long));
9867db96d56Sopenharmony_ci    if (dstoff == NULL) {
9877db96d56Sopenharmony_ci        goto error;
9887db96d56Sopenharmony_ci    }
9897db96d56Sopenharmony_ci
9907db96d56Sopenharmony_ci    // Derive dstoff and trans_list_wall from the information we've loaded
9917db96d56Sopenharmony_ci    utcoff_to_dstoff(trans_idx, utcoff, dstoff, isdst, self->num_transitions,
9927db96d56Sopenharmony_ci                     self->num_ttinfos);
9937db96d56Sopenharmony_ci
9947db96d56Sopenharmony_ci    if (ts_to_local(trans_idx, self->trans_list_utc, utcoff,
9957db96d56Sopenharmony_ci                    self->trans_list_wall, self->num_ttinfos,
9967db96d56Sopenharmony_ci                    self->num_transitions)) {
9977db96d56Sopenharmony_ci        goto error;
9987db96d56Sopenharmony_ci    }
9997db96d56Sopenharmony_ci
10007db96d56Sopenharmony_ci    // Build _ttinfo objects from utcoff, dstoff and abbr
10017db96d56Sopenharmony_ci    self->_ttinfos = PyMem_Malloc(self->num_ttinfos * sizeof(_ttinfo));
10027db96d56Sopenharmony_ci    if (self->_ttinfos == NULL) {
10037db96d56Sopenharmony_ci        goto error;
10047db96d56Sopenharmony_ci    }
10057db96d56Sopenharmony_ci    for (size_t i = 0; i < self->num_ttinfos; ++i) {
10067db96d56Sopenharmony_ci        PyObject *tzname = PyTuple_GetItem(abbr, i);
10077db96d56Sopenharmony_ci        if (tzname == NULL) {
10087db96d56Sopenharmony_ci            goto error;
10097db96d56Sopenharmony_ci        }
10107db96d56Sopenharmony_ci
10117db96d56Sopenharmony_ci        ttinfos_allocated++;
10127db96d56Sopenharmony_ci        if (build_ttinfo(utcoff[i], dstoff[i], tzname, &(self->_ttinfos[i]))) {
10137db96d56Sopenharmony_ci            goto error;
10147db96d56Sopenharmony_ci        }
10157db96d56Sopenharmony_ci    }
10167db96d56Sopenharmony_ci
10177db96d56Sopenharmony_ci    // Build our mapping from transition to the ttinfo that applies
10187db96d56Sopenharmony_ci    self->trans_ttinfos =
10197db96d56Sopenharmony_ci        PyMem_Calloc(self->num_transitions, sizeof(_ttinfo *));
10207db96d56Sopenharmony_ci    if (self->trans_ttinfos == NULL) {
10217db96d56Sopenharmony_ci        goto error;
10227db96d56Sopenharmony_ci    }
10237db96d56Sopenharmony_ci    for (size_t i = 0; i < self->num_transitions; ++i) {
10247db96d56Sopenharmony_ci        size_t ttinfo_idx = trans_idx[i];
10257db96d56Sopenharmony_ci        assert(ttinfo_idx < self->num_ttinfos);
10267db96d56Sopenharmony_ci        self->trans_ttinfos[i] = &(self->_ttinfos[ttinfo_idx]);
10277db96d56Sopenharmony_ci    }
10287db96d56Sopenharmony_ci
10297db96d56Sopenharmony_ci    // Set ttinfo_before to the first non-DST transition
10307db96d56Sopenharmony_ci    for (size_t i = 0; i < self->num_ttinfos; ++i) {
10317db96d56Sopenharmony_ci        if (!isdst[i]) {
10327db96d56Sopenharmony_ci            self->ttinfo_before = &(self->_ttinfos[i]);
10337db96d56Sopenharmony_ci            break;
10347db96d56Sopenharmony_ci        }
10357db96d56Sopenharmony_ci    }
10367db96d56Sopenharmony_ci
10377db96d56Sopenharmony_ci    // If there are only DST ttinfos, pick the first one, if there are no
10387db96d56Sopenharmony_ci    // ttinfos at all, set ttinfo_before to NULL
10397db96d56Sopenharmony_ci    if (self->ttinfo_before == NULL && self->num_ttinfos > 0) {
10407db96d56Sopenharmony_ci        self->ttinfo_before = &(self->_ttinfos[0]);
10417db96d56Sopenharmony_ci    }
10427db96d56Sopenharmony_ci
10437db96d56Sopenharmony_ci    if (tz_str != Py_None && PyObject_IsTrue(tz_str)) {
10447db96d56Sopenharmony_ci        if (parse_tz_str(tz_str, &(self->tzrule_after))) {
10457db96d56Sopenharmony_ci            goto error;
10467db96d56Sopenharmony_ci        }
10477db96d56Sopenharmony_ci    }
10487db96d56Sopenharmony_ci    else {
10497db96d56Sopenharmony_ci        if (!self->num_ttinfos) {
10507db96d56Sopenharmony_ci            PyErr_Format(PyExc_ValueError, "No time zone information found.");
10517db96d56Sopenharmony_ci            goto error;
10527db96d56Sopenharmony_ci        }
10537db96d56Sopenharmony_ci
10547db96d56Sopenharmony_ci        size_t idx;
10557db96d56Sopenharmony_ci        if (!self->num_transitions) {
10567db96d56Sopenharmony_ci            idx = self->num_ttinfos - 1;
10577db96d56Sopenharmony_ci        }
10587db96d56Sopenharmony_ci        else {
10597db96d56Sopenharmony_ci            idx = trans_idx[self->num_transitions - 1];
10607db96d56Sopenharmony_ci        }
10617db96d56Sopenharmony_ci
10627db96d56Sopenharmony_ci        _ttinfo *tti = &(self->_ttinfos[idx]);
10637db96d56Sopenharmony_ci        build_tzrule(tti->tzname, NULL, tti->utcoff_seconds, 0, NULL, NULL,
10647db96d56Sopenharmony_ci                     &(self->tzrule_after));
10657db96d56Sopenharmony_ci
10667db96d56Sopenharmony_ci        // We've abused the build_tzrule constructor to construct an STD-only
10677db96d56Sopenharmony_ci        // rule mimicking whatever ttinfo we've picked up, but it's possible
10687db96d56Sopenharmony_ci        // that the one we've picked up is a DST zone, so we need to make sure
10697db96d56Sopenharmony_ci        // that the dstoff is set correctly in that case.
10707db96d56Sopenharmony_ci        if (PyObject_IsTrue(tti->dstoff)) {
10717db96d56Sopenharmony_ci            _ttinfo *tti_after = &(self->tzrule_after.std);
10727db96d56Sopenharmony_ci            Py_DECREF(tti_after->dstoff);
10737db96d56Sopenharmony_ci            tti_after->dstoff = tti->dstoff;
10747db96d56Sopenharmony_ci            Py_INCREF(tti_after->dstoff);
10757db96d56Sopenharmony_ci        }
10767db96d56Sopenharmony_ci    }
10777db96d56Sopenharmony_ci
10787db96d56Sopenharmony_ci    // Determine if this is a "fixed offset" zone, meaning that the output of
10797db96d56Sopenharmony_ci    // the utcoffset, dst and tzname functions does not depend on the specific
10807db96d56Sopenharmony_ci    // datetime passed.
10817db96d56Sopenharmony_ci    //
10827db96d56Sopenharmony_ci    // We make three simplifying assumptions here:
10837db96d56Sopenharmony_ci    //
10847db96d56Sopenharmony_ci    // 1. If tzrule_after is not std_only, it has transitions that might occur
10857db96d56Sopenharmony_ci    //    (it is possible to construct TZ strings that specify STD and DST but
10867db96d56Sopenharmony_ci    //    no transitions ever occur, such as AAA0BBB,0/0,J365/25).
10877db96d56Sopenharmony_ci    // 2. If self->_ttinfos contains more than one _ttinfo object, the objects
10887db96d56Sopenharmony_ci    //    represent different offsets.
10897db96d56Sopenharmony_ci    // 3. self->ttinfos contains no unused _ttinfos (in which case an otherwise
10907db96d56Sopenharmony_ci    //    fixed-offset zone with extra _ttinfos defined may appear to *not* be
10917db96d56Sopenharmony_ci    //    a fixed offset zone).
10927db96d56Sopenharmony_ci    //
10937db96d56Sopenharmony_ci    // Violations to these assumptions would be fairly exotic, and exotic
10947db96d56Sopenharmony_ci    // zones should almost certainly not be used with datetime.time (the
10957db96d56Sopenharmony_ci    // only thing that would be affected by this).
10967db96d56Sopenharmony_ci    if (self->num_ttinfos > 1 || !self->tzrule_after.std_only) {
10977db96d56Sopenharmony_ci        self->fixed_offset = 0;
10987db96d56Sopenharmony_ci    }
10997db96d56Sopenharmony_ci    else if (self->num_ttinfos == 0) {
11007db96d56Sopenharmony_ci        self->fixed_offset = 1;
11017db96d56Sopenharmony_ci    }
11027db96d56Sopenharmony_ci    else {
11037db96d56Sopenharmony_ci        int constant_offset =
11047db96d56Sopenharmony_ci            ttinfo_eq(&(self->_ttinfos[0]), &self->tzrule_after.std);
11057db96d56Sopenharmony_ci        if (constant_offset < 0) {
11067db96d56Sopenharmony_ci            goto error;
11077db96d56Sopenharmony_ci        }
11087db96d56Sopenharmony_ci        else {
11097db96d56Sopenharmony_ci            self->fixed_offset = constant_offset;
11107db96d56Sopenharmony_ci        }
11117db96d56Sopenharmony_ci    }
11127db96d56Sopenharmony_ci
11137db96d56Sopenharmony_ci    int rv = 0;
11147db96d56Sopenharmony_ci    goto cleanup;
11157db96d56Sopenharmony_cierror:
11167db96d56Sopenharmony_ci    // These resources only need to be freed if we have failed, if we succeed
11177db96d56Sopenharmony_ci    // in initializing a PyZoneInfo_ZoneInfo object, we can rely on its dealloc
11187db96d56Sopenharmony_ci    // method to free the relevant resources.
11197db96d56Sopenharmony_ci    if (self->trans_list_utc != NULL) {
11207db96d56Sopenharmony_ci        PyMem_Free(self->trans_list_utc);
11217db96d56Sopenharmony_ci        self->trans_list_utc = NULL;
11227db96d56Sopenharmony_ci    }
11237db96d56Sopenharmony_ci
11247db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; ++i) {
11257db96d56Sopenharmony_ci        if (self->trans_list_wall[i] != NULL) {
11267db96d56Sopenharmony_ci            PyMem_Free(self->trans_list_wall[i]);
11277db96d56Sopenharmony_ci            self->trans_list_wall[i] = NULL;
11287db96d56Sopenharmony_ci        }
11297db96d56Sopenharmony_ci    }
11307db96d56Sopenharmony_ci
11317db96d56Sopenharmony_ci    if (self->_ttinfos != NULL) {
11327db96d56Sopenharmony_ci        for (size_t i = 0; i < ttinfos_allocated; ++i) {
11337db96d56Sopenharmony_ci            xdecref_ttinfo(&(self->_ttinfos[i]));
11347db96d56Sopenharmony_ci        }
11357db96d56Sopenharmony_ci        PyMem_Free(self->_ttinfos);
11367db96d56Sopenharmony_ci        self->_ttinfos = NULL;
11377db96d56Sopenharmony_ci    }
11387db96d56Sopenharmony_ci
11397db96d56Sopenharmony_ci    if (self->trans_ttinfos != NULL) {
11407db96d56Sopenharmony_ci        PyMem_Free(self->trans_ttinfos);
11417db96d56Sopenharmony_ci        self->trans_ttinfos = NULL;
11427db96d56Sopenharmony_ci    }
11437db96d56Sopenharmony_ci
11447db96d56Sopenharmony_ci    rv = -1;
11457db96d56Sopenharmony_cicleanup:
11467db96d56Sopenharmony_ci    Py_XDECREF(data_tuple);
11477db96d56Sopenharmony_ci
11487db96d56Sopenharmony_ci    if (utcoff != NULL) {
11497db96d56Sopenharmony_ci        PyMem_Free(utcoff);
11507db96d56Sopenharmony_ci    }
11517db96d56Sopenharmony_ci
11527db96d56Sopenharmony_ci    if (dstoff != NULL) {
11537db96d56Sopenharmony_ci        PyMem_Free(dstoff);
11547db96d56Sopenharmony_ci    }
11557db96d56Sopenharmony_ci
11567db96d56Sopenharmony_ci    if (isdst != NULL) {
11577db96d56Sopenharmony_ci        PyMem_Free(isdst);
11587db96d56Sopenharmony_ci    }
11597db96d56Sopenharmony_ci
11607db96d56Sopenharmony_ci    if (trans_idx != NULL) {
11617db96d56Sopenharmony_ci        PyMem_Free(trans_idx);
11627db96d56Sopenharmony_ci    }
11637db96d56Sopenharmony_ci
11647db96d56Sopenharmony_ci    return rv;
11657db96d56Sopenharmony_ci}
11667db96d56Sopenharmony_ci
11677db96d56Sopenharmony_ci/* Function to calculate the local timestamp of a transition from the year. */
11687db96d56Sopenharmony_ciint64_t
11697db96d56Sopenharmony_cicalendarrule_year_to_timestamp(TransitionRuleType *base_self, int year)
11707db96d56Sopenharmony_ci{
11717db96d56Sopenharmony_ci    CalendarRule *self = (CalendarRule *)base_self;
11727db96d56Sopenharmony_ci
11737db96d56Sopenharmony_ci    // We want (year, month, day of month); we have year and month, but we
11747db96d56Sopenharmony_ci    // need to turn (week, day-of-week) into day-of-month
11757db96d56Sopenharmony_ci    //
11767db96d56Sopenharmony_ci    // Week 1 is the first week in which day `day` (where 0 = Sunday) appears.
11777db96d56Sopenharmony_ci    // Week 5 represents the last occurrence of day `day`, so we need to know
11787db96d56Sopenharmony_ci    // the first weekday of the month and the number of days in the month.
11797db96d56Sopenharmony_ci    int8_t first_day = (ymd_to_ord(year, self->month, 1) + 6) % 7;
11807db96d56Sopenharmony_ci    uint8_t days_in_month = DAYS_IN_MONTH[self->month];
11817db96d56Sopenharmony_ci    if (self->month == 2 && is_leap_year(year)) {
11827db96d56Sopenharmony_ci        days_in_month += 1;
11837db96d56Sopenharmony_ci    }
11847db96d56Sopenharmony_ci
11857db96d56Sopenharmony_ci    // This equation seems magical, so I'll break it down:
11867db96d56Sopenharmony_ci    // 1. calendar says 0 = Monday, POSIX says 0 = Sunday so we need first_day
11877db96d56Sopenharmony_ci    //    + 1 to get 1 = Monday -> 7 = Sunday, which is still equivalent
11887db96d56Sopenharmony_ci    //    because this math is mod 7
11897db96d56Sopenharmony_ci    // 2. Get first day - desired day mod 7 (adjusting by 7 for negative
11907db96d56Sopenharmony_ci    //    numbers so that -1 % 7 = 6).
11917db96d56Sopenharmony_ci    // 3. Add 1 because month days are a 1-based index.
11927db96d56Sopenharmony_ci    int8_t month_day = ((int8_t)(self->day) - (first_day + 1)) % 7;
11937db96d56Sopenharmony_ci    if (month_day < 0) {
11947db96d56Sopenharmony_ci        month_day += 7;
11957db96d56Sopenharmony_ci    }
11967db96d56Sopenharmony_ci    month_day += 1;
11977db96d56Sopenharmony_ci
11987db96d56Sopenharmony_ci    // Now use a 0-based index version of `week` to calculate the w-th
11997db96d56Sopenharmony_ci    // occurrence of `day`
12007db96d56Sopenharmony_ci    month_day += ((int8_t)(self->week) - 1) * 7;
12017db96d56Sopenharmony_ci
12027db96d56Sopenharmony_ci    // month_day will only be > days_in_month if w was 5, and `w` means "last
12037db96d56Sopenharmony_ci    // occurrence of `d`", so now we just check if we over-shot the end of the
12047db96d56Sopenharmony_ci    // month and if so knock off 1 week.
12057db96d56Sopenharmony_ci    if (month_day > days_in_month) {
12067db96d56Sopenharmony_ci        month_day -= 7;
12077db96d56Sopenharmony_ci    }
12087db96d56Sopenharmony_ci
12097db96d56Sopenharmony_ci    int64_t ordinal = ymd_to_ord(year, self->month, month_day) - EPOCHORDINAL;
12107db96d56Sopenharmony_ci    return ((ordinal * 86400) + (int64_t)(self->hour * 3600) +
12117db96d56Sopenharmony_ci            (int64_t)(self->minute * 60) + (int64_t)(self->second));
12127db96d56Sopenharmony_ci}
12137db96d56Sopenharmony_ci
12147db96d56Sopenharmony_ci/* Constructor for CalendarRule. */
12157db96d56Sopenharmony_ciint
12167db96d56Sopenharmony_cicalendarrule_new(uint8_t month, uint8_t week, uint8_t day, int8_t hour,
12177db96d56Sopenharmony_ci                 int8_t minute, int8_t second, CalendarRule *out)
12187db96d56Sopenharmony_ci{
12197db96d56Sopenharmony_ci    // These bounds come from the POSIX standard, which describes an Mm.n.d
12207db96d56Sopenharmony_ci    // rule as:
12217db96d56Sopenharmony_ci    //
12227db96d56Sopenharmony_ci    //   The d'th day (0 <= d <= 6) of week n of month m of the year (1 <= n <=
12237db96d56Sopenharmony_ci    //   5, 1 <= m <= 12, where week 5 means "the last d day in month m" which
12247db96d56Sopenharmony_ci    //   may occur in either the fourth or the fifth week). Week 1 is the first
12257db96d56Sopenharmony_ci    //   week in which the d'th day occurs. Day zero is Sunday.
12267db96d56Sopenharmony_ci    if (month <= 0 || month > 12) {
12277db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Month must be in (0, 12]");
12287db96d56Sopenharmony_ci        return -1;
12297db96d56Sopenharmony_ci    }
12307db96d56Sopenharmony_ci
12317db96d56Sopenharmony_ci    if (week <= 0 || week > 5) {
12327db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Week must be in (0, 5]");
12337db96d56Sopenharmony_ci        return -1;
12347db96d56Sopenharmony_ci    }
12357db96d56Sopenharmony_ci
12367db96d56Sopenharmony_ci    // If the 'day' parameter type is changed to a signed type,
12377db96d56Sopenharmony_ci    // "day < 0" check must be added.
12387db96d56Sopenharmony_ci    if (/* day < 0 || */ day > 6) {
12397db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Day must be in [0, 6]");
12407db96d56Sopenharmony_ci        return -1;
12417db96d56Sopenharmony_ci    }
12427db96d56Sopenharmony_ci
12437db96d56Sopenharmony_ci    TransitionRuleType base = {&calendarrule_year_to_timestamp};
12447db96d56Sopenharmony_ci
12457db96d56Sopenharmony_ci    CalendarRule new_offset = {
12467db96d56Sopenharmony_ci        .base = base,
12477db96d56Sopenharmony_ci        .month = month,
12487db96d56Sopenharmony_ci        .week = week,
12497db96d56Sopenharmony_ci        .day = day,
12507db96d56Sopenharmony_ci        .hour = hour,
12517db96d56Sopenharmony_ci        .minute = minute,
12527db96d56Sopenharmony_ci        .second = second,
12537db96d56Sopenharmony_ci    };
12547db96d56Sopenharmony_ci
12557db96d56Sopenharmony_ci    *out = new_offset;
12567db96d56Sopenharmony_ci    return 0;
12577db96d56Sopenharmony_ci}
12587db96d56Sopenharmony_ci
12597db96d56Sopenharmony_ci/* Function to calculate the local timestamp of a transition from the year.
12607db96d56Sopenharmony_ci *
12617db96d56Sopenharmony_ci * This translates the day of the year into a local timestamp — either a
12627db96d56Sopenharmony_ci * 1-based Julian day, not including leap days, or the 0-based year-day,
12637db96d56Sopenharmony_ci * including leap days.
12647db96d56Sopenharmony_ci * */
12657db96d56Sopenharmony_ciint64_t
12667db96d56Sopenharmony_cidayrule_year_to_timestamp(TransitionRuleType *base_self, int year)
12677db96d56Sopenharmony_ci{
12687db96d56Sopenharmony_ci    // The function signature requires a TransitionRuleType pointer, but this
12697db96d56Sopenharmony_ci    // function is only applicable to DayRule* objects.
12707db96d56Sopenharmony_ci    DayRule *self = (DayRule *)base_self;
12717db96d56Sopenharmony_ci
12727db96d56Sopenharmony_ci    // ymd_to_ord calculates the number of days since 0001-01-01, but we want
12737db96d56Sopenharmony_ci    // to know the number of days since 1970-01-01, so we must subtract off
12747db96d56Sopenharmony_ci    // the equivalent of ymd_to_ord(1970, 1, 1).
12757db96d56Sopenharmony_ci    //
12767db96d56Sopenharmony_ci    // We subtract off an additional 1 day to account for January 1st (we want
12777db96d56Sopenharmony_ci    // the number of full days *before* the date of the transition - partial
12787db96d56Sopenharmony_ci    // days are accounted for in the hour, minute and second portions.
12797db96d56Sopenharmony_ci    int64_t days_before_year = ymd_to_ord(year, 1, 1) - EPOCHORDINAL - 1;
12807db96d56Sopenharmony_ci
12817db96d56Sopenharmony_ci    // The Julian day specification skips over February 29th in leap years,
12827db96d56Sopenharmony_ci    // from the POSIX standard:
12837db96d56Sopenharmony_ci    //
12847db96d56Sopenharmony_ci    //   Leap days shall not be counted. That is, in all years-including leap
12857db96d56Sopenharmony_ci    //   years-February 28 is day 59 and March 1 is day 60. It is impossible to
12867db96d56Sopenharmony_ci    //   refer explicitly to the occasional February 29.
12877db96d56Sopenharmony_ci    //
12887db96d56Sopenharmony_ci    // This is actually more useful than you'd think — if you want a rule that
12897db96d56Sopenharmony_ci    // always transitions on a given calendar day (other than February 29th),
12907db96d56Sopenharmony_ci    // you would use a Julian day, e.g. J91 always refers to April 1st and J365
12917db96d56Sopenharmony_ci    // always refers to December 31st.
12927db96d56Sopenharmony_ci    unsigned int day = self->day;
12937db96d56Sopenharmony_ci    if (self->julian && day >= 59 && is_leap_year(year)) {
12947db96d56Sopenharmony_ci        day += 1;
12957db96d56Sopenharmony_ci    }
12967db96d56Sopenharmony_ci
12977db96d56Sopenharmony_ci    return ((days_before_year + day) * 86400) + (self->hour * 3600) +
12987db96d56Sopenharmony_ci           (self->minute * 60) + self->second;
12997db96d56Sopenharmony_ci}
13007db96d56Sopenharmony_ci
13017db96d56Sopenharmony_ci/* Constructor for DayRule. */
13027db96d56Sopenharmony_cistatic int
13037db96d56Sopenharmony_cidayrule_new(uint8_t julian, unsigned int day, int8_t hour, int8_t minute,
13047db96d56Sopenharmony_ci            int8_t second, DayRule *out)
13057db96d56Sopenharmony_ci{
13067db96d56Sopenharmony_ci    // The POSIX standard specifies that Julian days must be in the range (1 <=
13077db96d56Sopenharmony_ci    // n <= 365) and that non-Julian (they call it "0-based Julian") days must
13087db96d56Sopenharmony_ci    // be in the range (0 <= n <= 365).
13097db96d56Sopenharmony_ci    if (day < julian || day > 365) {
13107db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "day must be in [%u, 365], not: %u",
13117db96d56Sopenharmony_ci                     julian, day);
13127db96d56Sopenharmony_ci        return -1;
13137db96d56Sopenharmony_ci    }
13147db96d56Sopenharmony_ci
13157db96d56Sopenharmony_ci    TransitionRuleType base = {
13167db96d56Sopenharmony_ci        &dayrule_year_to_timestamp,
13177db96d56Sopenharmony_ci    };
13187db96d56Sopenharmony_ci
13197db96d56Sopenharmony_ci    DayRule tmp = {
13207db96d56Sopenharmony_ci        .base = base,
13217db96d56Sopenharmony_ci        .julian = julian,
13227db96d56Sopenharmony_ci        .day = day,
13237db96d56Sopenharmony_ci        .hour = hour,
13247db96d56Sopenharmony_ci        .minute = minute,
13257db96d56Sopenharmony_ci        .second = second,
13267db96d56Sopenharmony_ci    };
13277db96d56Sopenharmony_ci
13287db96d56Sopenharmony_ci    *out = tmp;
13297db96d56Sopenharmony_ci
13307db96d56Sopenharmony_ci    return 0;
13317db96d56Sopenharmony_ci}
13327db96d56Sopenharmony_ci
13337db96d56Sopenharmony_ci/* Calculate the start and end rules for a _tzrule in the given year. */
13347db96d56Sopenharmony_cistatic void
13357db96d56Sopenharmony_citzrule_transitions(_tzrule *rule, int year, int64_t *start, int64_t *end)
13367db96d56Sopenharmony_ci{
13377db96d56Sopenharmony_ci    assert(rule->start != NULL);
13387db96d56Sopenharmony_ci    assert(rule->end != NULL);
13397db96d56Sopenharmony_ci    *start = rule->start->year_to_timestamp(rule->start, year);
13407db96d56Sopenharmony_ci    *end = rule->end->year_to_timestamp(rule->end, year);
13417db96d56Sopenharmony_ci}
13427db96d56Sopenharmony_ci
13437db96d56Sopenharmony_ci/* Calculate the _ttinfo that applies at a given local time from a _tzrule.
13447db96d56Sopenharmony_ci *
13457db96d56Sopenharmony_ci * This takes a local timestamp and fold for disambiguation purposes; the year
13467db96d56Sopenharmony_ci * could technically be calculated from the timestamp, but given that the
13477db96d56Sopenharmony_ci * callers of this function already have the year information accessible from
13487db96d56Sopenharmony_ci * the datetime struct, it is taken as an additional parameter to reduce
13497db96d56Sopenharmony_ci * unnecessary calculation.
13507db96d56Sopenharmony_ci * */
13517db96d56Sopenharmony_cistatic _ttinfo *
13527db96d56Sopenharmony_cifind_tzrule_ttinfo(_tzrule *rule, int64_t ts, unsigned char fold, int year)
13537db96d56Sopenharmony_ci{
13547db96d56Sopenharmony_ci    if (rule->std_only) {
13557db96d56Sopenharmony_ci        return &(rule->std);
13567db96d56Sopenharmony_ci    }
13577db96d56Sopenharmony_ci
13587db96d56Sopenharmony_ci    int64_t start, end;
13597db96d56Sopenharmony_ci    uint8_t isdst;
13607db96d56Sopenharmony_ci
13617db96d56Sopenharmony_ci    tzrule_transitions(rule, year, &start, &end);
13627db96d56Sopenharmony_ci
13637db96d56Sopenharmony_ci    // With fold = 0, the period (denominated in local time) with the smaller
13647db96d56Sopenharmony_ci    // offset starts at the end of the gap and ends at the end of the fold;
13657db96d56Sopenharmony_ci    // with fold = 1, it runs from the start of the gap to the beginning of the
13667db96d56Sopenharmony_ci    // fold.
13677db96d56Sopenharmony_ci    //
13687db96d56Sopenharmony_ci    // So in order to determine the DST boundaries we need to know both the
13697db96d56Sopenharmony_ci    // fold and whether DST is positive or negative (rare), and it turns out
13707db96d56Sopenharmony_ci    // that this boils down to fold XOR is_positive.
13717db96d56Sopenharmony_ci    if (fold == (rule->dst_diff >= 0)) {
13727db96d56Sopenharmony_ci        end -= rule->dst_diff;
13737db96d56Sopenharmony_ci    }
13747db96d56Sopenharmony_ci    else {
13757db96d56Sopenharmony_ci        start += rule->dst_diff;
13767db96d56Sopenharmony_ci    }
13777db96d56Sopenharmony_ci
13787db96d56Sopenharmony_ci    if (start < end) {
13797db96d56Sopenharmony_ci        isdst = (ts >= start) && (ts < end);
13807db96d56Sopenharmony_ci    }
13817db96d56Sopenharmony_ci    else {
13827db96d56Sopenharmony_ci        isdst = (ts < end) || (ts >= start);
13837db96d56Sopenharmony_ci    }
13847db96d56Sopenharmony_ci
13857db96d56Sopenharmony_ci    if (isdst) {
13867db96d56Sopenharmony_ci        return &(rule->dst);
13877db96d56Sopenharmony_ci    }
13887db96d56Sopenharmony_ci    else {
13897db96d56Sopenharmony_ci        return &(rule->std);
13907db96d56Sopenharmony_ci    }
13917db96d56Sopenharmony_ci}
13927db96d56Sopenharmony_ci
13937db96d56Sopenharmony_ci/* Calculate the ttinfo and fold that applies for a _tzrule at an epoch time.
13947db96d56Sopenharmony_ci *
13957db96d56Sopenharmony_ci * This function can determine the _ttinfo that applies at a given epoch time,
13967db96d56Sopenharmony_ci * (analogous to trans_list_utc), and whether or not the datetime is in a fold.
13977db96d56Sopenharmony_ci * This is to be used in the .fromutc() function.
13987db96d56Sopenharmony_ci *
13997db96d56Sopenharmony_ci * The year is technically a redundant parameter, because it can be calculated
14007db96d56Sopenharmony_ci * from the timestamp, but all callers of this function should have the year
14017db96d56Sopenharmony_ci * in the datetime struct anyway, so taking it as a parameter saves unnecessary
14027db96d56Sopenharmony_ci * calculation.
14037db96d56Sopenharmony_ci **/
14047db96d56Sopenharmony_cistatic _ttinfo *
14057db96d56Sopenharmony_cifind_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year,
14067db96d56Sopenharmony_ci                           unsigned char *fold)
14077db96d56Sopenharmony_ci{
14087db96d56Sopenharmony_ci    if (rule->std_only) {
14097db96d56Sopenharmony_ci        *fold = 0;
14107db96d56Sopenharmony_ci        return &(rule->std);
14117db96d56Sopenharmony_ci    }
14127db96d56Sopenharmony_ci
14137db96d56Sopenharmony_ci    int64_t start, end;
14147db96d56Sopenharmony_ci    uint8_t isdst;
14157db96d56Sopenharmony_ci    tzrule_transitions(rule, year, &start, &end);
14167db96d56Sopenharmony_ci    start -= rule->std.utcoff_seconds;
14177db96d56Sopenharmony_ci    end -= rule->dst.utcoff_seconds;
14187db96d56Sopenharmony_ci
14197db96d56Sopenharmony_ci    if (start < end) {
14207db96d56Sopenharmony_ci        isdst = (ts >= start) && (ts < end);
14217db96d56Sopenharmony_ci    }
14227db96d56Sopenharmony_ci    else {
14237db96d56Sopenharmony_ci        isdst = (ts < end) || (ts >= start);
14247db96d56Sopenharmony_ci    }
14257db96d56Sopenharmony_ci
14267db96d56Sopenharmony_ci    // For positive DST, the ambiguous period is one dst_diff after the end of
14277db96d56Sopenharmony_ci    // DST; for negative DST, the ambiguous period is one dst_diff before the
14287db96d56Sopenharmony_ci    // start of DST.
14297db96d56Sopenharmony_ci    int64_t ambig_start, ambig_end;
14307db96d56Sopenharmony_ci    if (rule->dst_diff > 0) {
14317db96d56Sopenharmony_ci        ambig_start = end;
14327db96d56Sopenharmony_ci        ambig_end = end + rule->dst_diff;
14337db96d56Sopenharmony_ci    }
14347db96d56Sopenharmony_ci    else {
14357db96d56Sopenharmony_ci        ambig_start = start;
14367db96d56Sopenharmony_ci        ambig_end = start - rule->dst_diff;
14377db96d56Sopenharmony_ci    }
14387db96d56Sopenharmony_ci
14397db96d56Sopenharmony_ci    *fold = (ts >= ambig_start) && (ts < ambig_end);
14407db96d56Sopenharmony_ci
14417db96d56Sopenharmony_ci    if (isdst) {
14427db96d56Sopenharmony_ci        return &(rule->dst);
14437db96d56Sopenharmony_ci    }
14447db96d56Sopenharmony_ci    else {
14457db96d56Sopenharmony_ci        return &(rule->std);
14467db96d56Sopenharmony_ci    }
14477db96d56Sopenharmony_ci}
14487db96d56Sopenharmony_ci
14497db96d56Sopenharmony_ci/* Parse a TZ string in the format specified by the POSIX standard:
14507db96d56Sopenharmony_ci *
14517db96d56Sopenharmony_ci *  std offset[dst[offset],start[/time],end[/time]]
14527db96d56Sopenharmony_ci *
14537db96d56Sopenharmony_ci *  std and dst must be 3 or more characters long and must not contain a
14547db96d56Sopenharmony_ci *  leading colon, embedded digits, commas, nor a plus or minus signs; The
14557db96d56Sopenharmony_ci *  spaces between "std" and "offset" are only for display and are not actually
14567db96d56Sopenharmony_ci *  present in the string.
14577db96d56Sopenharmony_ci *
14587db96d56Sopenharmony_ci *  The format of the offset is ``[+|-]hh[:mm[:ss]]``
14597db96d56Sopenharmony_ci *
14607db96d56Sopenharmony_ci * See the POSIX.1 spec: IEE Std 1003.1-2018 §8.3:
14617db96d56Sopenharmony_ci *
14627db96d56Sopenharmony_ci * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
14637db96d56Sopenharmony_ci */
14647db96d56Sopenharmony_cistatic int
14657db96d56Sopenharmony_ciparse_tz_str(PyObject *tz_str_obj, _tzrule *out)
14667db96d56Sopenharmony_ci{
14677db96d56Sopenharmony_ci    PyObject *std_abbr = NULL;
14687db96d56Sopenharmony_ci    PyObject *dst_abbr = NULL;
14697db96d56Sopenharmony_ci    TransitionRuleType *start = NULL;
14707db96d56Sopenharmony_ci    TransitionRuleType *end = NULL;
14717db96d56Sopenharmony_ci    // Initialize offsets to invalid value (> 24 hours)
14727db96d56Sopenharmony_ci    long std_offset = 1 << 20;
14737db96d56Sopenharmony_ci    long dst_offset = 1 << 20;
14747db96d56Sopenharmony_ci
14757db96d56Sopenharmony_ci    const char *tz_str = PyBytes_AsString(tz_str_obj);
14767db96d56Sopenharmony_ci    if (tz_str == NULL) {
14777db96d56Sopenharmony_ci        return -1;
14787db96d56Sopenharmony_ci    }
14797db96d56Sopenharmony_ci    const char *p = tz_str;
14807db96d56Sopenharmony_ci
14817db96d56Sopenharmony_ci    // Read the `std` abbreviation, which must be at least 3 characters long.
14827db96d56Sopenharmony_ci    Py_ssize_t num_chars = parse_abbr(p, &std_abbr);
14837db96d56Sopenharmony_ci    if (num_chars < 1) {
14847db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Invalid STD format in %R", tz_str_obj);
14857db96d56Sopenharmony_ci        goto error;
14867db96d56Sopenharmony_ci    }
14877db96d56Sopenharmony_ci
14887db96d56Sopenharmony_ci    p += num_chars;
14897db96d56Sopenharmony_ci
14907db96d56Sopenharmony_ci    // Now read the STD offset, which is required
14917db96d56Sopenharmony_ci    num_chars = parse_tz_delta(p, &std_offset);
14927db96d56Sopenharmony_ci    if (num_chars < 0) {
14937db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Invalid STD offset in %R", tz_str_obj);
14947db96d56Sopenharmony_ci        goto error;
14957db96d56Sopenharmony_ci    }
14967db96d56Sopenharmony_ci    p += num_chars;
14977db96d56Sopenharmony_ci
14987db96d56Sopenharmony_ci    // If the string ends here, there is no DST, otherwise we must parse the
14997db96d56Sopenharmony_ci    // DST abbreviation and start and end dates and times.
15007db96d56Sopenharmony_ci    if (*p == '\0') {
15017db96d56Sopenharmony_ci        goto complete;
15027db96d56Sopenharmony_ci    }
15037db96d56Sopenharmony_ci
15047db96d56Sopenharmony_ci    num_chars = parse_abbr(p, &dst_abbr);
15057db96d56Sopenharmony_ci    if (num_chars < 1) {
15067db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError, "Invalid DST format in %R", tz_str_obj);
15077db96d56Sopenharmony_ci        goto error;
15087db96d56Sopenharmony_ci    }
15097db96d56Sopenharmony_ci    p += num_chars;
15107db96d56Sopenharmony_ci
15117db96d56Sopenharmony_ci    if (*p == ',') {
15127db96d56Sopenharmony_ci        // From the POSIX standard:
15137db96d56Sopenharmony_ci        //
15147db96d56Sopenharmony_ci        // If no offset follows dst, the alternative time is assumed to be one
15157db96d56Sopenharmony_ci        // hour ahead of standard time.
15167db96d56Sopenharmony_ci        dst_offset = std_offset + 3600;
15177db96d56Sopenharmony_ci    }
15187db96d56Sopenharmony_ci    else {
15197db96d56Sopenharmony_ci        num_chars = parse_tz_delta(p, &dst_offset);
15207db96d56Sopenharmony_ci        if (num_chars < 0) {
15217db96d56Sopenharmony_ci            PyErr_Format(PyExc_ValueError, "Invalid DST offset in %R",
15227db96d56Sopenharmony_ci                         tz_str_obj);
15237db96d56Sopenharmony_ci            goto error;
15247db96d56Sopenharmony_ci        }
15257db96d56Sopenharmony_ci
15267db96d56Sopenharmony_ci        p += num_chars;
15277db96d56Sopenharmony_ci    }
15287db96d56Sopenharmony_ci
15297db96d56Sopenharmony_ci    TransitionRuleType **transitions[2] = {&start, &end};
15307db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; ++i) {
15317db96d56Sopenharmony_ci        if (*p != ',') {
15327db96d56Sopenharmony_ci            PyErr_Format(PyExc_ValueError,
15337db96d56Sopenharmony_ci                         "Missing transition rules in TZ string: %R",
15347db96d56Sopenharmony_ci                         tz_str_obj);
15357db96d56Sopenharmony_ci            goto error;
15367db96d56Sopenharmony_ci        }
15377db96d56Sopenharmony_ci        p++;
15387db96d56Sopenharmony_ci
15397db96d56Sopenharmony_ci        num_chars = parse_transition_rule(p, transitions[i]);
15407db96d56Sopenharmony_ci        if (num_chars < 0) {
15417db96d56Sopenharmony_ci            PyErr_Format(PyExc_ValueError,
15427db96d56Sopenharmony_ci                         "Malformed transition rule in TZ string: %R",
15437db96d56Sopenharmony_ci                         tz_str_obj);
15447db96d56Sopenharmony_ci            goto error;
15457db96d56Sopenharmony_ci        }
15467db96d56Sopenharmony_ci        p += num_chars;
15477db96d56Sopenharmony_ci    }
15487db96d56Sopenharmony_ci
15497db96d56Sopenharmony_ci    if (*p != '\0') {
15507db96d56Sopenharmony_ci        PyErr_Format(PyExc_ValueError,
15517db96d56Sopenharmony_ci                     "Extraneous characters at end of TZ string: %R",
15527db96d56Sopenharmony_ci                     tz_str_obj);
15537db96d56Sopenharmony_ci        goto error;
15547db96d56Sopenharmony_ci    }
15557db96d56Sopenharmony_ci
15567db96d56Sopenharmony_cicomplete:
15577db96d56Sopenharmony_ci    build_tzrule(std_abbr, dst_abbr, std_offset, dst_offset, start, end, out);
15587db96d56Sopenharmony_ci    Py_DECREF(std_abbr);
15597db96d56Sopenharmony_ci    Py_XDECREF(dst_abbr);
15607db96d56Sopenharmony_ci
15617db96d56Sopenharmony_ci    return 0;
15627db96d56Sopenharmony_cierror:
15637db96d56Sopenharmony_ci    Py_XDECREF(std_abbr);
15647db96d56Sopenharmony_ci    if (dst_abbr != NULL && dst_abbr != Py_None) {
15657db96d56Sopenharmony_ci        Py_DECREF(dst_abbr);
15667db96d56Sopenharmony_ci    }
15677db96d56Sopenharmony_ci
15687db96d56Sopenharmony_ci    if (start != NULL) {
15697db96d56Sopenharmony_ci        PyMem_Free(start);
15707db96d56Sopenharmony_ci    }
15717db96d56Sopenharmony_ci
15727db96d56Sopenharmony_ci    if (end != NULL) {
15737db96d56Sopenharmony_ci        PyMem_Free(end);
15747db96d56Sopenharmony_ci    }
15757db96d56Sopenharmony_ci
15767db96d56Sopenharmony_ci    return -1;
15777db96d56Sopenharmony_ci}
15787db96d56Sopenharmony_ci
15797db96d56Sopenharmony_cistatic int
15807db96d56Sopenharmony_ciparse_uint(const char *const p, uint8_t *value)
15817db96d56Sopenharmony_ci{
15827db96d56Sopenharmony_ci    if (!isdigit(*p)) {
15837db96d56Sopenharmony_ci        return -1;
15847db96d56Sopenharmony_ci    }
15857db96d56Sopenharmony_ci
15867db96d56Sopenharmony_ci    *value = (*p) - '0';
15877db96d56Sopenharmony_ci    return 0;
15887db96d56Sopenharmony_ci}
15897db96d56Sopenharmony_ci
15907db96d56Sopenharmony_ci/* Parse the STD and DST abbreviations from a TZ string. */
15917db96d56Sopenharmony_cistatic Py_ssize_t
15927db96d56Sopenharmony_ciparse_abbr(const char *const p, PyObject **abbr)
15937db96d56Sopenharmony_ci{
15947db96d56Sopenharmony_ci    const char *ptr = p;
15957db96d56Sopenharmony_ci    char buff = *ptr;
15967db96d56Sopenharmony_ci    const char *str_start;
15977db96d56Sopenharmony_ci    const char *str_end;
15987db96d56Sopenharmony_ci
15997db96d56Sopenharmony_ci    if (*ptr == '<') {
16007db96d56Sopenharmony_ci        ptr++;
16017db96d56Sopenharmony_ci        str_start = ptr;
16027db96d56Sopenharmony_ci        while ((buff = *ptr) != '>') {
16037db96d56Sopenharmony_ci            // From the POSIX standard:
16047db96d56Sopenharmony_ci            //
16057db96d56Sopenharmony_ci            //   In the quoted form, the first character shall be the less-than
16067db96d56Sopenharmony_ci            //   ( '<' ) character and the last character shall be the
16077db96d56Sopenharmony_ci            //   greater-than ( '>' ) character. All characters between these
16087db96d56Sopenharmony_ci            //   quoting characters shall be alphanumeric characters from the
16097db96d56Sopenharmony_ci            //   portable character set in the current locale, the plus-sign (
16107db96d56Sopenharmony_ci            //   '+' ) character, or the minus-sign ( '-' ) character. The std
16117db96d56Sopenharmony_ci            //   and dst fields in this case shall not include the quoting
16127db96d56Sopenharmony_ci            //   characters.
16137db96d56Sopenharmony_ci            if (!isalpha(buff) && !isdigit(buff) && buff != '+' &&
16147db96d56Sopenharmony_ci                buff != '-') {
16157db96d56Sopenharmony_ci                return -1;
16167db96d56Sopenharmony_ci            }
16177db96d56Sopenharmony_ci            ptr++;
16187db96d56Sopenharmony_ci        }
16197db96d56Sopenharmony_ci        str_end = ptr;
16207db96d56Sopenharmony_ci        ptr++;
16217db96d56Sopenharmony_ci    }
16227db96d56Sopenharmony_ci    else {
16237db96d56Sopenharmony_ci        str_start = p;
16247db96d56Sopenharmony_ci        // From the POSIX standard:
16257db96d56Sopenharmony_ci        //
16267db96d56Sopenharmony_ci        //   In the unquoted form, all characters in these fields shall be
16277db96d56Sopenharmony_ci        //   alphabetic characters from the portable character set in the
16287db96d56Sopenharmony_ci        //   current locale.
16297db96d56Sopenharmony_ci        while (isalpha(*ptr)) {
16307db96d56Sopenharmony_ci            ptr++;
16317db96d56Sopenharmony_ci        }
16327db96d56Sopenharmony_ci        str_end = ptr;
16337db96d56Sopenharmony_ci    }
16347db96d56Sopenharmony_ci
16357db96d56Sopenharmony_ci    *abbr = PyUnicode_FromStringAndSize(str_start, str_end - str_start);
16367db96d56Sopenharmony_ci    if (*abbr == NULL) {
16377db96d56Sopenharmony_ci        return -1;
16387db96d56Sopenharmony_ci    }
16397db96d56Sopenharmony_ci
16407db96d56Sopenharmony_ci    return ptr - p;
16417db96d56Sopenharmony_ci}
16427db96d56Sopenharmony_ci
16437db96d56Sopenharmony_ci/* Parse a UTC offset from a TZ str. */
16447db96d56Sopenharmony_cistatic Py_ssize_t
16457db96d56Sopenharmony_ciparse_tz_delta(const char *const p, long *total_seconds)
16467db96d56Sopenharmony_ci{
16477db96d56Sopenharmony_ci    // From the POSIX spec:
16487db96d56Sopenharmony_ci    //
16497db96d56Sopenharmony_ci    //   Indicates the value added to the local time to arrive at Coordinated
16507db96d56Sopenharmony_ci    //   Universal Time. The offset has the form:
16517db96d56Sopenharmony_ci    //
16527db96d56Sopenharmony_ci    //   hh[:mm[:ss]]
16537db96d56Sopenharmony_ci    //
16547db96d56Sopenharmony_ci    //   One or more digits may be used; the value is always interpreted as a
16557db96d56Sopenharmony_ci    //   decimal number.
16567db96d56Sopenharmony_ci    //
16577db96d56Sopenharmony_ci    // The POSIX spec says that the values for `hour` must be between 0 and 24
16587db96d56Sopenharmony_ci    // hours, but RFC 8536 §3.3.1 specifies that the hours part of the
16597db96d56Sopenharmony_ci    // transition times may be signed and range from -167 to 167.
16607db96d56Sopenharmony_ci    long sign = -1;
16617db96d56Sopenharmony_ci    long hours = 0;
16627db96d56Sopenharmony_ci    long minutes = 0;
16637db96d56Sopenharmony_ci    long seconds = 0;
16647db96d56Sopenharmony_ci
16657db96d56Sopenharmony_ci    const char *ptr = p;
16667db96d56Sopenharmony_ci    char buff = *ptr;
16677db96d56Sopenharmony_ci    if (buff == '-' || buff == '+') {
16687db96d56Sopenharmony_ci        // Negative numbers correspond to *positive* offsets, from the spec:
16697db96d56Sopenharmony_ci        //
16707db96d56Sopenharmony_ci        //   If preceded by a '-', the timezone shall be east of the Prime
16717db96d56Sopenharmony_ci        //   Meridian; otherwise, it shall be west (which may be indicated by
16727db96d56Sopenharmony_ci        //   an optional preceding '+' ).
16737db96d56Sopenharmony_ci        if (buff == '-') {
16747db96d56Sopenharmony_ci            sign = 1;
16757db96d56Sopenharmony_ci        }
16767db96d56Sopenharmony_ci
16777db96d56Sopenharmony_ci        ptr++;
16787db96d56Sopenharmony_ci    }
16797db96d56Sopenharmony_ci
16807db96d56Sopenharmony_ci    // The hour can be 1 or 2 numeric characters
16817db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; ++i) {
16827db96d56Sopenharmony_ci        buff = *ptr;
16837db96d56Sopenharmony_ci        if (!isdigit(buff)) {
16847db96d56Sopenharmony_ci            if (i == 0) {
16857db96d56Sopenharmony_ci                return -1;
16867db96d56Sopenharmony_ci            }
16877db96d56Sopenharmony_ci            else {
16887db96d56Sopenharmony_ci                break;
16897db96d56Sopenharmony_ci            }
16907db96d56Sopenharmony_ci        }
16917db96d56Sopenharmony_ci
16927db96d56Sopenharmony_ci        hours *= 10;
16937db96d56Sopenharmony_ci        hours += buff - '0';
16947db96d56Sopenharmony_ci        ptr++;
16957db96d56Sopenharmony_ci    }
16967db96d56Sopenharmony_ci
16977db96d56Sopenharmony_ci    if (hours > 24 || hours < 0) {
16987db96d56Sopenharmony_ci        return -1;
16997db96d56Sopenharmony_ci    }
17007db96d56Sopenharmony_ci
17017db96d56Sopenharmony_ci    // Minutes and seconds always of the format ":dd"
17027db96d56Sopenharmony_ci    long *outputs[2] = {&minutes, &seconds};
17037db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; ++i) {
17047db96d56Sopenharmony_ci        if (*ptr != ':') {
17057db96d56Sopenharmony_ci            goto complete;
17067db96d56Sopenharmony_ci        }
17077db96d56Sopenharmony_ci        ptr++;
17087db96d56Sopenharmony_ci
17097db96d56Sopenharmony_ci        for (size_t j = 0; j < 2; ++j) {
17107db96d56Sopenharmony_ci            buff = *ptr;
17117db96d56Sopenharmony_ci            if (!isdigit(buff)) {
17127db96d56Sopenharmony_ci                return -1;
17137db96d56Sopenharmony_ci            }
17147db96d56Sopenharmony_ci            *(outputs[i]) *= 10;
17157db96d56Sopenharmony_ci            *(outputs[i]) += buff - '0';
17167db96d56Sopenharmony_ci            ptr++;
17177db96d56Sopenharmony_ci        }
17187db96d56Sopenharmony_ci    }
17197db96d56Sopenharmony_ci
17207db96d56Sopenharmony_cicomplete:
17217db96d56Sopenharmony_ci    *total_seconds = sign * ((hours * 3600) + (minutes * 60) + seconds);
17227db96d56Sopenharmony_ci
17237db96d56Sopenharmony_ci    return ptr - p;
17247db96d56Sopenharmony_ci}
17257db96d56Sopenharmony_ci
17267db96d56Sopenharmony_ci/* Parse the date portion of a transition rule. */
17277db96d56Sopenharmony_cistatic Py_ssize_t
17287db96d56Sopenharmony_ciparse_transition_rule(const char *const p, TransitionRuleType **out)
17297db96d56Sopenharmony_ci{
17307db96d56Sopenharmony_ci    // The full transition rule indicates when to change back and forth between
17317db96d56Sopenharmony_ci    // STD and DST, and has the form:
17327db96d56Sopenharmony_ci    //
17337db96d56Sopenharmony_ci    //   date[/time],date[/time]
17347db96d56Sopenharmony_ci    //
17357db96d56Sopenharmony_ci    // This function parses an individual date[/time] section, and returns
17367db96d56Sopenharmony_ci    // the number of characters that contributed to the transition rule. This
17377db96d56Sopenharmony_ci    // does not include the ',' at the end of the first rule.
17387db96d56Sopenharmony_ci    //
17397db96d56Sopenharmony_ci    // The POSIX spec states that if *time* is not given, the default is 02:00.
17407db96d56Sopenharmony_ci    const char *ptr = p;
17417db96d56Sopenharmony_ci    int8_t hour = 2;
17427db96d56Sopenharmony_ci    int8_t minute = 0;
17437db96d56Sopenharmony_ci    int8_t second = 0;
17447db96d56Sopenharmony_ci
17457db96d56Sopenharmony_ci    // Rules come in one of three flavors:
17467db96d56Sopenharmony_ci    //
17477db96d56Sopenharmony_ci    //   1. Jn: Julian day n, with no leap days.
17487db96d56Sopenharmony_ci    //   2. n: Day of year (0-based, with leap days)
17497db96d56Sopenharmony_ci    //   3. Mm.n.d: Specifying by month, week and day-of-week.
17507db96d56Sopenharmony_ci
17517db96d56Sopenharmony_ci    if (*ptr == 'M') {
17527db96d56Sopenharmony_ci        uint8_t month, week, day;
17537db96d56Sopenharmony_ci        ptr++;
17547db96d56Sopenharmony_ci        if (parse_uint(ptr, &month)) {
17557db96d56Sopenharmony_ci            return -1;
17567db96d56Sopenharmony_ci        }
17577db96d56Sopenharmony_ci        ptr++;
17587db96d56Sopenharmony_ci        if (*ptr != '.') {
17597db96d56Sopenharmony_ci            uint8_t tmp;
17607db96d56Sopenharmony_ci            if (parse_uint(ptr, &tmp)) {
17617db96d56Sopenharmony_ci                return -1;
17627db96d56Sopenharmony_ci            }
17637db96d56Sopenharmony_ci
17647db96d56Sopenharmony_ci            month *= 10;
17657db96d56Sopenharmony_ci            month += tmp;
17667db96d56Sopenharmony_ci            ptr++;
17677db96d56Sopenharmony_ci        }
17687db96d56Sopenharmony_ci
17697db96d56Sopenharmony_ci        uint8_t *values[2] = {&week, &day};
17707db96d56Sopenharmony_ci        for (size_t i = 0; i < 2; ++i) {
17717db96d56Sopenharmony_ci            if (*ptr != '.') {
17727db96d56Sopenharmony_ci                return -1;
17737db96d56Sopenharmony_ci            }
17747db96d56Sopenharmony_ci            ptr++;
17757db96d56Sopenharmony_ci
17767db96d56Sopenharmony_ci            if (parse_uint(ptr, values[i])) {
17777db96d56Sopenharmony_ci                return -1;
17787db96d56Sopenharmony_ci            }
17797db96d56Sopenharmony_ci            ptr++;
17807db96d56Sopenharmony_ci        }
17817db96d56Sopenharmony_ci
17827db96d56Sopenharmony_ci        if (*ptr == '/') {
17837db96d56Sopenharmony_ci            ptr++;
17847db96d56Sopenharmony_ci            Py_ssize_t num_chars =
17857db96d56Sopenharmony_ci                parse_transition_time(ptr, &hour, &minute, &second);
17867db96d56Sopenharmony_ci            if (num_chars < 0) {
17877db96d56Sopenharmony_ci                return -1;
17887db96d56Sopenharmony_ci            }
17897db96d56Sopenharmony_ci            ptr += num_chars;
17907db96d56Sopenharmony_ci        }
17917db96d56Sopenharmony_ci
17927db96d56Sopenharmony_ci        CalendarRule *rv = PyMem_Calloc(1, sizeof(CalendarRule));
17937db96d56Sopenharmony_ci        if (rv == NULL) {
17947db96d56Sopenharmony_ci            return -1;
17957db96d56Sopenharmony_ci        }
17967db96d56Sopenharmony_ci
17977db96d56Sopenharmony_ci        if (calendarrule_new(month, week, day, hour, minute, second, rv)) {
17987db96d56Sopenharmony_ci            PyMem_Free(rv);
17997db96d56Sopenharmony_ci            return -1;
18007db96d56Sopenharmony_ci        }
18017db96d56Sopenharmony_ci
18027db96d56Sopenharmony_ci        *out = (TransitionRuleType *)rv;
18037db96d56Sopenharmony_ci    }
18047db96d56Sopenharmony_ci    else {
18057db96d56Sopenharmony_ci        uint8_t julian = 0;
18067db96d56Sopenharmony_ci        unsigned int day = 0;
18077db96d56Sopenharmony_ci        if (*ptr == 'J') {
18087db96d56Sopenharmony_ci            julian = 1;
18097db96d56Sopenharmony_ci            ptr++;
18107db96d56Sopenharmony_ci        }
18117db96d56Sopenharmony_ci
18127db96d56Sopenharmony_ci        for (size_t i = 0; i < 3; ++i) {
18137db96d56Sopenharmony_ci            if (!isdigit(*ptr)) {
18147db96d56Sopenharmony_ci                if (i == 0) {
18157db96d56Sopenharmony_ci                    return -1;
18167db96d56Sopenharmony_ci                }
18177db96d56Sopenharmony_ci                break;
18187db96d56Sopenharmony_ci            }
18197db96d56Sopenharmony_ci            day *= 10;
18207db96d56Sopenharmony_ci            day += (*ptr) - '0';
18217db96d56Sopenharmony_ci            ptr++;
18227db96d56Sopenharmony_ci        }
18237db96d56Sopenharmony_ci
18247db96d56Sopenharmony_ci        if (*ptr == '/') {
18257db96d56Sopenharmony_ci            ptr++;
18267db96d56Sopenharmony_ci            Py_ssize_t num_chars =
18277db96d56Sopenharmony_ci                parse_transition_time(ptr, &hour, &minute, &second);
18287db96d56Sopenharmony_ci            if (num_chars < 0) {
18297db96d56Sopenharmony_ci                return -1;
18307db96d56Sopenharmony_ci            }
18317db96d56Sopenharmony_ci            ptr += num_chars;
18327db96d56Sopenharmony_ci        }
18337db96d56Sopenharmony_ci
18347db96d56Sopenharmony_ci        DayRule *rv = PyMem_Calloc(1, sizeof(DayRule));
18357db96d56Sopenharmony_ci        if (rv == NULL) {
18367db96d56Sopenharmony_ci            return -1;
18377db96d56Sopenharmony_ci        }
18387db96d56Sopenharmony_ci
18397db96d56Sopenharmony_ci        if (dayrule_new(julian, day, hour, minute, second, rv)) {
18407db96d56Sopenharmony_ci            PyMem_Free(rv);
18417db96d56Sopenharmony_ci            return -1;
18427db96d56Sopenharmony_ci        }
18437db96d56Sopenharmony_ci        *out = (TransitionRuleType *)rv;
18447db96d56Sopenharmony_ci    }
18457db96d56Sopenharmony_ci
18467db96d56Sopenharmony_ci    return ptr - p;
18477db96d56Sopenharmony_ci}
18487db96d56Sopenharmony_ci
18497db96d56Sopenharmony_ci/* Parse the time portion of a transition rule (e.g. following an /) */
18507db96d56Sopenharmony_cistatic Py_ssize_t
18517db96d56Sopenharmony_ciparse_transition_time(const char *const p, int8_t *hour, int8_t *minute,
18527db96d56Sopenharmony_ci                      int8_t *second)
18537db96d56Sopenharmony_ci{
18547db96d56Sopenharmony_ci    // From the spec:
18557db96d56Sopenharmony_ci    //
18567db96d56Sopenharmony_ci    //   The time has the same format as offset except that no leading sign
18577db96d56Sopenharmony_ci    //   ( '-' or '+' ) is allowed.
18587db96d56Sopenharmony_ci    //
18597db96d56Sopenharmony_ci    // The format for the offset is:
18607db96d56Sopenharmony_ci    //
18617db96d56Sopenharmony_ci    //   h[h][:mm[:ss]]
18627db96d56Sopenharmony_ci    //
18637db96d56Sopenharmony_ci    // RFC 8536 also allows transition times to be signed and to range from
18647db96d56Sopenharmony_ci    // -167 to +167, but the current version only supports [0, 99].
18657db96d56Sopenharmony_ci    //
18667db96d56Sopenharmony_ci    // TODO: Support the full range of transition hours.
18677db96d56Sopenharmony_ci    int8_t *components[3] = {hour, minute, second};
18687db96d56Sopenharmony_ci    const char *ptr = p;
18697db96d56Sopenharmony_ci    int8_t sign = 1;
18707db96d56Sopenharmony_ci
18717db96d56Sopenharmony_ci    if (*ptr == '-' || *ptr == '+') {
18727db96d56Sopenharmony_ci        if (*ptr == '-') {
18737db96d56Sopenharmony_ci            sign = -1;
18747db96d56Sopenharmony_ci        }
18757db96d56Sopenharmony_ci        ptr++;
18767db96d56Sopenharmony_ci    }
18777db96d56Sopenharmony_ci
18787db96d56Sopenharmony_ci    for (size_t i = 0; i < 3; ++i) {
18797db96d56Sopenharmony_ci        if (i > 0) {
18807db96d56Sopenharmony_ci            if (*ptr != ':') {
18817db96d56Sopenharmony_ci                break;
18827db96d56Sopenharmony_ci            }
18837db96d56Sopenharmony_ci            ptr++;
18847db96d56Sopenharmony_ci        }
18857db96d56Sopenharmony_ci
18867db96d56Sopenharmony_ci        uint8_t buff = 0;
18877db96d56Sopenharmony_ci        for (size_t j = 0; j < 2; j++) {
18887db96d56Sopenharmony_ci            if (!isdigit(*ptr)) {
18897db96d56Sopenharmony_ci                if (i == 0 && j > 0) {
18907db96d56Sopenharmony_ci                    break;
18917db96d56Sopenharmony_ci                }
18927db96d56Sopenharmony_ci                return -1;
18937db96d56Sopenharmony_ci            }
18947db96d56Sopenharmony_ci
18957db96d56Sopenharmony_ci            buff *= 10;
18967db96d56Sopenharmony_ci            buff += (*ptr) - '0';
18977db96d56Sopenharmony_ci            ptr++;
18987db96d56Sopenharmony_ci        }
18997db96d56Sopenharmony_ci
19007db96d56Sopenharmony_ci        *(components[i]) = sign * buff;
19017db96d56Sopenharmony_ci    }
19027db96d56Sopenharmony_ci
19037db96d56Sopenharmony_ci    return ptr - p;
19047db96d56Sopenharmony_ci}
19057db96d56Sopenharmony_ci
19067db96d56Sopenharmony_ci/* Constructor for a _tzrule.
19077db96d56Sopenharmony_ci *
19087db96d56Sopenharmony_ci * If `dst_abbr` is NULL, this will construct an "STD-only" _tzrule, in which
19097db96d56Sopenharmony_ci * case `dst_offset` will be ignored and `start` and `end` are expected to be
19107db96d56Sopenharmony_ci * NULL as well.
19117db96d56Sopenharmony_ci *
19127db96d56Sopenharmony_ci * Returns 0 on success.
19137db96d56Sopenharmony_ci */
19147db96d56Sopenharmony_cistatic int
19157db96d56Sopenharmony_cibuild_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset,
19167db96d56Sopenharmony_ci             long dst_offset, TransitionRuleType *start,
19177db96d56Sopenharmony_ci             TransitionRuleType *end, _tzrule *out)
19187db96d56Sopenharmony_ci{
19197db96d56Sopenharmony_ci    _tzrule rv = {{0}};
19207db96d56Sopenharmony_ci
19217db96d56Sopenharmony_ci    rv.start = start;
19227db96d56Sopenharmony_ci    rv.end = end;
19237db96d56Sopenharmony_ci
19247db96d56Sopenharmony_ci    if (build_ttinfo(std_offset, 0, std_abbr, &rv.std)) {
19257db96d56Sopenharmony_ci        goto error;
19267db96d56Sopenharmony_ci    }
19277db96d56Sopenharmony_ci
19287db96d56Sopenharmony_ci    if (dst_abbr != NULL) {
19297db96d56Sopenharmony_ci        rv.dst_diff = dst_offset - std_offset;
19307db96d56Sopenharmony_ci        if (build_ttinfo(dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) {
19317db96d56Sopenharmony_ci            goto error;
19327db96d56Sopenharmony_ci        }
19337db96d56Sopenharmony_ci    }
19347db96d56Sopenharmony_ci    else {
19357db96d56Sopenharmony_ci        rv.std_only = 1;
19367db96d56Sopenharmony_ci    }
19377db96d56Sopenharmony_ci
19387db96d56Sopenharmony_ci    *out = rv;
19397db96d56Sopenharmony_ci
19407db96d56Sopenharmony_ci    return 0;
19417db96d56Sopenharmony_cierror:
19427db96d56Sopenharmony_ci    xdecref_ttinfo(&rv.std);
19437db96d56Sopenharmony_ci    xdecref_ttinfo(&rv.dst);
19447db96d56Sopenharmony_ci    return -1;
19457db96d56Sopenharmony_ci}
19467db96d56Sopenharmony_ci
19477db96d56Sopenharmony_ci/* Destructor for _tzrule. */
19487db96d56Sopenharmony_cistatic void
19497db96d56Sopenharmony_cifree_tzrule(_tzrule *tzrule)
19507db96d56Sopenharmony_ci{
19517db96d56Sopenharmony_ci    xdecref_ttinfo(&(tzrule->std));
19527db96d56Sopenharmony_ci    if (!tzrule->std_only) {
19537db96d56Sopenharmony_ci        xdecref_ttinfo(&(tzrule->dst));
19547db96d56Sopenharmony_ci    }
19557db96d56Sopenharmony_ci
19567db96d56Sopenharmony_ci    if (tzrule->start != NULL) {
19577db96d56Sopenharmony_ci        PyMem_Free(tzrule->start);
19587db96d56Sopenharmony_ci    }
19597db96d56Sopenharmony_ci
19607db96d56Sopenharmony_ci    if (tzrule->end != NULL) {
19617db96d56Sopenharmony_ci        PyMem_Free(tzrule->end);
19627db96d56Sopenharmony_ci    }
19637db96d56Sopenharmony_ci}
19647db96d56Sopenharmony_ci
19657db96d56Sopenharmony_ci/* Calculate DST offsets from transitions and UTC offsets
19667db96d56Sopenharmony_ci *
19677db96d56Sopenharmony_ci * This is necessary because each C `ttinfo` only contains the UTC offset,
19687db96d56Sopenharmony_ci * time zone abbreviation and an isdst boolean - it does not include the
19697db96d56Sopenharmony_ci * amount of the DST offset, but we need the amount for the dst() function.
19707db96d56Sopenharmony_ci *
19717db96d56Sopenharmony_ci * Thus function uses heuristics to infer what the offset should be, so it
19727db96d56Sopenharmony_ci * is not guaranteed that this will work for all zones. If we cannot assign
19737db96d56Sopenharmony_ci * a value for a given DST offset, we'll assume it's 1H rather than 0H, so
19747db96d56Sopenharmony_ci * bool(dt.dst()) will always match ttinfo.isdst.
19757db96d56Sopenharmony_ci */
19767db96d56Sopenharmony_cistatic void
19777db96d56Sopenharmony_ciutcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs,
19787db96d56Sopenharmony_ci                 unsigned char *isdsts, size_t num_transitions,
19797db96d56Sopenharmony_ci                 size_t num_ttinfos)
19807db96d56Sopenharmony_ci{
19817db96d56Sopenharmony_ci    size_t dst_count = 0;
19827db96d56Sopenharmony_ci    size_t dst_found = 0;
19837db96d56Sopenharmony_ci    for (size_t i = 0; i < num_ttinfos; ++i) {
19847db96d56Sopenharmony_ci        dst_count++;
19857db96d56Sopenharmony_ci    }
19867db96d56Sopenharmony_ci
19877db96d56Sopenharmony_ci    for (size_t i = 1; i < num_transitions; ++i) {
19887db96d56Sopenharmony_ci        if (dst_count == dst_found) {
19897db96d56Sopenharmony_ci            break;
19907db96d56Sopenharmony_ci        }
19917db96d56Sopenharmony_ci
19927db96d56Sopenharmony_ci        size_t idx = trans_idx[i];
19937db96d56Sopenharmony_ci        size_t comp_idx = trans_idx[i - 1];
19947db96d56Sopenharmony_ci
19957db96d56Sopenharmony_ci        // Only look at DST offsets that have nto been assigned already
19967db96d56Sopenharmony_ci        if (!isdsts[idx] || dstoffs[idx] != 0) {
19977db96d56Sopenharmony_ci            continue;
19987db96d56Sopenharmony_ci        }
19997db96d56Sopenharmony_ci
20007db96d56Sopenharmony_ci        long dstoff = 0;
20017db96d56Sopenharmony_ci        long utcoff = utcoffs[idx];
20027db96d56Sopenharmony_ci
20037db96d56Sopenharmony_ci        if (!isdsts[comp_idx]) {
20047db96d56Sopenharmony_ci            dstoff = utcoff - utcoffs[comp_idx];
20057db96d56Sopenharmony_ci        }
20067db96d56Sopenharmony_ci
20077db96d56Sopenharmony_ci        if (!dstoff && idx < (num_ttinfos - 1)) {
20087db96d56Sopenharmony_ci            comp_idx = trans_idx[i + 1];
20097db96d56Sopenharmony_ci
20107db96d56Sopenharmony_ci            // If the following transition is also DST and we couldn't find
20117db96d56Sopenharmony_ci            // the DST offset by this point, we're going to have to skip it
20127db96d56Sopenharmony_ci            // and hope this transition gets assigned later
20137db96d56Sopenharmony_ci            if (isdsts[comp_idx]) {
20147db96d56Sopenharmony_ci                continue;
20157db96d56Sopenharmony_ci            }
20167db96d56Sopenharmony_ci
20177db96d56Sopenharmony_ci            dstoff = utcoff - utcoffs[comp_idx];
20187db96d56Sopenharmony_ci        }
20197db96d56Sopenharmony_ci
20207db96d56Sopenharmony_ci        if (dstoff) {
20217db96d56Sopenharmony_ci            dst_found++;
20227db96d56Sopenharmony_ci            dstoffs[idx] = dstoff;
20237db96d56Sopenharmony_ci        }
20247db96d56Sopenharmony_ci    }
20257db96d56Sopenharmony_ci
20267db96d56Sopenharmony_ci    if (dst_found < dst_count) {
20277db96d56Sopenharmony_ci        // If there are time zones we didn't find a value for, we'll end up
20287db96d56Sopenharmony_ci        // with dstoff = 0 for something where isdst=1. This is obviously
20297db96d56Sopenharmony_ci        // wrong — one hour will be a much better guess than 0.
20307db96d56Sopenharmony_ci        for (size_t idx = 0; idx < num_ttinfos; ++idx) {
20317db96d56Sopenharmony_ci            if (isdsts[idx] && !dstoffs[idx]) {
20327db96d56Sopenharmony_ci                dstoffs[idx] = 3600;
20337db96d56Sopenharmony_ci            }
20347db96d56Sopenharmony_ci        }
20357db96d56Sopenharmony_ci    }
20367db96d56Sopenharmony_ci}
20377db96d56Sopenharmony_ci
20387db96d56Sopenharmony_ci#define _swap(x, y, buffer) \
20397db96d56Sopenharmony_ci    buffer = x;             \
20407db96d56Sopenharmony_ci    x = y;                  \
20417db96d56Sopenharmony_ci    y = buffer;
20427db96d56Sopenharmony_ci
20437db96d56Sopenharmony_ci/* Calculate transitions in local time from UTC time and offsets.
20447db96d56Sopenharmony_ci *
20457db96d56Sopenharmony_ci * We want to know when each transition occurs, denominated in the number of
20467db96d56Sopenharmony_ci * nominal wall-time seconds between 1970-01-01T00:00:00 and the transition in
20477db96d56Sopenharmony_ci * *local time* (note: this is *not* equivalent to the output of
20487db96d56Sopenharmony_ci * datetime.timestamp, which is the total number of seconds actual elapsed
20497db96d56Sopenharmony_ci * since 1970-01-01T00:00:00Z in UTC).
20507db96d56Sopenharmony_ci *
20517db96d56Sopenharmony_ci * This is an ambiguous question because "local time" can be ambiguous — but it
20527db96d56Sopenharmony_ci * is disambiguated by the `fold` parameter, so we allocate two arrays:
20537db96d56Sopenharmony_ci *
20547db96d56Sopenharmony_ci *  trans_local[0]: The wall-time transitions for fold=0
20557db96d56Sopenharmony_ci *  trans_local[1]: The wall-time transitions for fold=1
20567db96d56Sopenharmony_ci *
20577db96d56Sopenharmony_ci * This returns 0 on success and a negative number of failure. The trans_local
20587db96d56Sopenharmony_ci * arrays must be freed if they are not NULL.
20597db96d56Sopenharmony_ci */
20607db96d56Sopenharmony_cistatic int
20617db96d56Sopenharmony_cits_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff,
20627db96d56Sopenharmony_ci            int64_t *trans_local[2], size_t num_ttinfos,
20637db96d56Sopenharmony_ci            size_t num_transitions)
20647db96d56Sopenharmony_ci{
20657db96d56Sopenharmony_ci    if (num_transitions == 0) {
20667db96d56Sopenharmony_ci        return 0;
20677db96d56Sopenharmony_ci    }
20687db96d56Sopenharmony_ci
20697db96d56Sopenharmony_ci    // Copy the UTC transitions into each array to be modified in place later
20707db96d56Sopenharmony_ci    for (size_t i = 0; i < 2; ++i) {
20717db96d56Sopenharmony_ci        trans_local[i] = PyMem_Malloc(num_transitions * sizeof(int64_t));
20727db96d56Sopenharmony_ci        if (trans_local[i] == NULL) {
20737db96d56Sopenharmony_ci            return -1;
20747db96d56Sopenharmony_ci        }
20757db96d56Sopenharmony_ci
20767db96d56Sopenharmony_ci        memcpy(trans_local[i], trans_utc, num_transitions * sizeof(int64_t));
20777db96d56Sopenharmony_ci    }
20787db96d56Sopenharmony_ci
20797db96d56Sopenharmony_ci    int64_t offset_0, offset_1, buff;
20807db96d56Sopenharmony_ci    if (num_ttinfos > 1) {
20817db96d56Sopenharmony_ci        offset_0 = utcoff[0];
20827db96d56Sopenharmony_ci        offset_1 = utcoff[trans_idx[0]];
20837db96d56Sopenharmony_ci
20847db96d56Sopenharmony_ci        if (offset_1 > offset_0) {
20857db96d56Sopenharmony_ci            _swap(offset_0, offset_1, buff);
20867db96d56Sopenharmony_ci        }
20877db96d56Sopenharmony_ci    }
20887db96d56Sopenharmony_ci    else {
20897db96d56Sopenharmony_ci        offset_0 = utcoff[0];
20907db96d56Sopenharmony_ci        offset_1 = utcoff[0];
20917db96d56Sopenharmony_ci    }
20927db96d56Sopenharmony_ci
20937db96d56Sopenharmony_ci    trans_local[0][0] += offset_0;
20947db96d56Sopenharmony_ci    trans_local[1][0] += offset_1;
20957db96d56Sopenharmony_ci
20967db96d56Sopenharmony_ci    for (size_t i = 1; i < num_transitions; ++i) {
20977db96d56Sopenharmony_ci        offset_0 = utcoff[trans_idx[i - 1]];
20987db96d56Sopenharmony_ci        offset_1 = utcoff[trans_idx[i]];
20997db96d56Sopenharmony_ci
21007db96d56Sopenharmony_ci        if (offset_1 > offset_0) {
21017db96d56Sopenharmony_ci            _swap(offset_1, offset_0, buff);
21027db96d56Sopenharmony_ci        }
21037db96d56Sopenharmony_ci
21047db96d56Sopenharmony_ci        trans_local[0][i] += offset_0;
21057db96d56Sopenharmony_ci        trans_local[1][i] += offset_1;
21067db96d56Sopenharmony_ci    }
21077db96d56Sopenharmony_ci
21087db96d56Sopenharmony_ci    return 0;
21097db96d56Sopenharmony_ci}
21107db96d56Sopenharmony_ci
21117db96d56Sopenharmony_ci/* Simple bisect_right binary search implementation */
21127db96d56Sopenharmony_cistatic size_t
21137db96d56Sopenharmony_ci_bisect(const int64_t value, const int64_t *arr, size_t size)
21147db96d56Sopenharmony_ci{
21157db96d56Sopenharmony_ci    size_t lo = 0;
21167db96d56Sopenharmony_ci    size_t hi = size;
21177db96d56Sopenharmony_ci    size_t m;
21187db96d56Sopenharmony_ci
21197db96d56Sopenharmony_ci    while (lo < hi) {
21207db96d56Sopenharmony_ci        m = (lo + hi) / 2;
21217db96d56Sopenharmony_ci        if (arr[m] > value) {
21227db96d56Sopenharmony_ci            hi = m;
21237db96d56Sopenharmony_ci        }
21247db96d56Sopenharmony_ci        else {
21257db96d56Sopenharmony_ci            lo = m + 1;
21267db96d56Sopenharmony_ci        }
21277db96d56Sopenharmony_ci    }
21287db96d56Sopenharmony_ci
21297db96d56Sopenharmony_ci    return hi;
21307db96d56Sopenharmony_ci}
21317db96d56Sopenharmony_ci
21327db96d56Sopenharmony_ci/* Find the ttinfo rules that apply at a given local datetime. */
21337db96d56Sopenharmony_cistatic _ttinfo *
21347db96d56Sopenharmony_cifind_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt)
21357db96d56Sopenharmony_ci{
21367db96d56Sopenharmony_ci    // datetime.time has a .tzinfo attribute that passes None as the dt
21377db96d56Sopenharmony_ci    // argument; it only really has meaning for fixed-offset zones.
21387db96d56Sopenharmony_ci    if (dt == Py_None) {
21397db96d56Sopenharmony_ci        if (self->fixed_offset) {
21407db96d56Sopenharmony_ci            return &(self->tzrule_after.std);
21417db96d56Sopenharmony_ci        }
21427db96d56Sopenharmony_ci        else {
21437db96d56Sopenharmony_ci            return &NO_TTINFO;
21447db96d56Sopenharmony_ci        }
21457db96d56Sopenharmony_ci    }
21467db96d56Sopenharmony_ci
21477db96d56Sopenharmony_ci    int64_t ts;
21487db96d56Sopenharmony_ci    if (get_local_timestamp(dt, &ts)) {
21497db96d56Sopenharmony_ci        return NULL;
21507db96d56Sopenharmony_ci    }
21517db96d56Sopenharmony_ci
21527db96d56Sopenharmony_ci    unsigned char fold = PyDateTime_DATE_GET_FOLD(dt);
21537db96d56Sopenharmony_ci    assert(fold < 2);
21547db96d56Sopenharmony_ci    int64_t *local_transitions = self->trans_list_wall[fold];
21557db96d56Sopenharmony_ci    size_t num_trans = self->num_transitions;
21567db96d56Sopenharmony_ci
21577db96d56Sopenharmony_ci    if (num_trans && ts < local_transitions[0]) {
21587db96d56Sopenharmony_ci        return self->ttinfo_before;
21597db96d56Sopenharmony_ci    }
21607db96d56Sopenharmony_ci    else if (!num_trans || ts > local_transitions[self->num_transitions - 1]) {
21617db96d56Sopenharmony_ci        return find_tzrule_ttinfo(&(self->tzrule_after), ts, fold,
21627db96d56Sopenharmony_ci                                  PyDateTime_GET_YEAR(dt));
21637db96d56Sopenharmony_ci    }
21647db96d56Sopenharmony_ci    else {
21657db96d56Sopenharmony_ci        size_t idx = _bisect(ts, local_transitions, self->num_transitions) - 1;
21667db96d56Sopenharmony_ci        assert(idx < self->num_transitions);
21677db96d56Sopenharmony_ci        return self->trans_ttinfos[idx];
21687db96d56Sopenharmony_ci    }
21697db96d56Sopenharmony_ci}
21707db96d56Sopenharmony_ci
21717db96d56Sopenharmony_cistatic int
21727db96d56Sopenharmony_ciis_leap_year(int year)
21737db96d56Sopenharmony_ci{
21747db96d56Sopenharmony_ci    const unsigned int ayear = (unsigned int)year;
21757db96d56Sopenharmony_ci    return ayear % 4 == 0 && (ayear % 100 != 0 || ayear % 400 == 0);
21767db96d56Sopenharmony_ci}
21777db96d56Sopenharmony_ci
21787db96d56Sopenharmony_ci/* Calculates ordinal datetime from year, month and day. */
21797db96d56Sopenharmony_cistatic int
21807db96d56Sopenharmony_ciymd_to_ord(int y, int m, int d)
21817db96d56Sopenharmony_ci{
21827db96d56Sopenharmony_ci    y -= 1;
21837db96d56Sopenharmony_ci    int days_before_year = (y * 365) + (y / 4) - (y / 100) + (y / 400);
21847db96d56Sopenharmony_ci    int yearday = DAYS_BEFORE_MONTH[m];
21857db96d56Sopenharmony_ci    if (m > 2 && is_leap_year(y + 1)) {
21867db96d56Sopenharmony_ci        yearday += 1;
21877db96d56Sopenharmony_ci    }
21887db96d56Sopenharmony_ci
21897db96d56Sopenharmony_ci    return days_before_year + yearday + d;
21907db96d56Sopenharmony_ci}
21917db96d56Sopenharmony_ci
21927db96d56Sopenharmony_ci/* Calculate the number of seconds since 1970-01-01 in local time.
21937db96d56Sopenharmony_ci *
21947db96d56Sopenharmony_ci * This gets a datetime in the same "units" as self->trans_list_wall so that we
21957db96d56Sopenharmony_ci * can easily determine which transitions a datetime falls between. See the
21967db96d56Sopenharmony_ci * comment above ts_to_local for more information.
21977db96d56Sopenharmony_ci * */
21987db96d56Sopenharmony_cistatic int
21997db96d56Sopenharmony_ciget_local_timestamp(PyObject *dt, int64_t *local_ts)
22007db96d56Sopenharmony_ci{
22017db96d56Sopenharmony_ci    assert(local_ts != NULL);
22027db96d56Sopenharmony_ci
22037db96d56Sopenharmony_ci    int hour, minute, second;
22047db96d56Sopenharmony_ci    int ord;
22057db96d56Sopenharmony_ci    if (PyDateTime_CheckExact(dt)) {
22067db96d56Sopenharmony_ci        int y = PyDateTime_GET_YEAR(dt);
22077db96d56Sopenharmony_ci        int m = PyDateTime_GET_MONTH(dt);
22087db96d56Sopenharmony_ci        int d = PyDateTime_GET_DAY(dt);
22097db96d56Sopenharmony_ci        hour = PyDateTime_DATE_GET_HOUR(dt);
22107db96d56Sopenharmony_ci        minute = PyDateTime_DATE_GET_MINUTE(dt);
22117db96d56Sopenharmony_ci        second = PyDateTime_DATE_GET_SECOND(dt);
22127db96d56Sopenharmony_ci
22137db96d56Sopenharmony_ci        ord = ymd_to_ord(y, m, d);
22147db96d56Sopenharmony_ci    }
22157db96d56Sopenharmony_ci    else {
22167db96d56Sopenharmony_ci        PyObject *num = PyObject_CallMethod(dt, "toordinal", NULL);
22177db96d56Sopenharmony_ci        if (num == NULL) {
22187db96d56Sopenharmony_ci            return -1;
22197db96d56Sopenharmony_ci        }
22207db96d56Sopenharmony_ci
22217db96d56Sopenharmony_ci        ord = PyLong_AsLong(num);
22227db96d56Sopenharmony_ci        Py_DECREF(num);
22237db96d56Sopenharmony_ci        if (ord == -1 && PyErr_Occurred()) {
22247db96d56Sopenharmony_ci            return -1;
22257db96d56Sopenharmony_ci        }
22267db96d56Sopenharmony_ci
22277db96d56Sopenharmony_ci        num = PyObject_GetAttrString(dt, "hour");
22287db96d56Sopenharmony_ci        if (num == NULL) {
22297db96d56Sopenharmony_ci            return -1;
22307db96d56Sopenharmony_ci        }
22317db96d56Sopenharmony_ci        hour = PyLong_AsLong(num);
22327db96d56Sopenharmony_ci        Py_DECREF(num);
22337db96d56Sopenharmony_ci        if (hour == -1) {
22347db96d56Sopenharmony_ci            return -1;
22357db96d56Sopenharmony_ci        }
22367db96d56Sopenharmony_ci
22377db96d56Sopenharmony_ci        num = PyObject_GetAttrString(dt, "minute");
22387db96d56Sopenharmony_ci        if (num == NULL) {
22397db96d56Sopenharmony_ci            return -1;
22407db96d56Sopenharmony_ci        }
22417db96d56Sopenharmony_ci        minute = PyLong_AsLong(num);
22427db96d56Sopenharmony_ci        Py_DECREF(num);
22437db96d56Sopenharmony_ci        if (minute == -1) {
22447db96d56Sopenharmony_ci            return -1;
22457db96d56Sopenharmony_ci        }
22467db96d56Sopenharmony_ci
22477db96d56Sopenharmony_ci        num = PyObject_GetAttrString(dt, "second");
22487db96d56Sopenharmony_ci        if (num == NULL) {
22497db96d56Sopenharmony_ci            return -1;
22507db96d56Sopenharmony_ci        }
22517db96d56Sopenharmony_ci        second = PyLong_AsLong(num);
22527db96d56Sopenharmony_ci        Py_DECREF(num);
22537db96d56Sopenharmony_ci        if (second == -1) {
22547db96d56Sopenharmony_ci            return -1;
22557db96d56Sopenharmony_ci        }
22567db96d56Sopenharmony_ci    }
22577db96d56Sopenharmony_ci
22587db96d56Sopenharmony_ci    *local_ts = (int64_t)(ord - EPOCHORDINAL) * 86400 +
22597db96d56Sopenharmony_ci                (int64_t)(hour * 3600 + minute * 60 + second);
22607db96d56Sopenharmony_ci
22617db96d56Sopenharmony_ci    return 0;
22627db96d56Sopenharmony_ci}
22637db96d56Sopenharmony_ci
22647db96d56Sopenharmony_ci/////
22657db96d56Sopenharmony_ci// Functions for cache handling
22667db96d56Sopenharmony_ci
22677db96d56Sopenharmony_ci/* Constructor for StrongCacheNode */
22687db96d56Sopenharmony_cistatic StrongCacheNode *
22697db96d56Sopenharmony_cistrong_cache_node_new(PyObject *key, PyObject *zone)
22707db96d56Sopenharmony_ci{
22717db96d56Sopenharmony_ci    StrongCacheNode *node = PyMem_Malloc(sizeof(StrongCacheNode));
22727db96d56Sopenharmony_ci    if (node == NULL) {
22737db96d56Sopenharmony_ci        return NULL;
22747db96d56Sopenharmony_ci    }
22757db96d56Sopenharmony_ci
22767db96d56Sopenharmony_ci    Py_INCREF(key);
22777db96d56Sopenharmony_ci    Py_INCREF(zone);
22787db96d56Sopenharmony_ci
22797db96d56Sopenharmony_ci    node->next = NULL;
22807db96d56Sopenharmony_ci    node->prev = NULL;
22817db96d56Sopenharmony_ci    node->key = key;
22827db96d56Sopenharmony_ci    node->zone = zone;
22837db96d56Sopenharmony_ci
22847db96d56Sopenharmony_ci    return node;
22857db96d56Sopenharmony_ci}
22867db96d56Sopenharmony_ci
22877db96d56Sopenharmony_ci/* Destructor for StrongCacheNode */
22887db96d56Sopenharmony_civoid
22897db96d56Sopenharmony_cistrong_cache_node_free(StrongCacheNode *node)
22907db96d56Sopenharmony_ci{
22917db96d56Sopenharmony_ci    Py_XDECREF(node->key);
22927db96d56Sopenharmony_ci    Py_XDECREF(node->zone);
22937db96d56Sopenharmony_ci
22947db96d56Sopenharmony_ci    PyMem_Free(node);
22957db96d56Sopenharmony_ci}
22967db96d56Sopenharmony_ci
22977db96d56Sopenharmony_ci/* Frees all nodes at or after a specified root in the strong cache.
22987db96d56Sopenharmony_ci *
22997db96d56Sopenharmony_ci * This can be used on the root node to free the entire cache or it can be used
23007db96d56Sopenharmony_ci * to clear all nodes that have been expired (which, if everything is going
23017db96d56Sopenharmony_ci * right, will actually only be 1 node at a time).
23027db96d56Sopenharmony_ci */
23037db96d56Sopenharmony_civoid
23047db96d56Sopenharmony_cistrong_cache_free(StrongCacheNode *root)
23057db96d56Sopenharmony_ci{
23067db96d56Sopenharmony_ci    StrongCacheNode *node = root;
23077db96d56Sopenharmony_ci    StrongCacheNode *next_node;
23087db96d56Sopenharmony_ci    while (node != NULL) {
23097db96d56Sopenharmony_ci        next_node = node->next;
23107db96d56Sopenharmony_ci        strong_cache_node_free(node);
23117db96d56Sopenharmony_ci
23127db96d56Sopenharmony_ci        node = next_node;
23137db96d56Sopenharmony_ci    }
23147db96d56Sopenharmony_ci}
23157db96d56Sopenharmony_ci
23167db96d56Sopenharmony_ci/* Removes a node from the cache and update its neighbors.
23177db96d56Sopenharmony_ci *
23187db96d56Sopenharmony_ci * This is used both when ejecting a node from the cache and when moving it to
23197db96d56Sopenharmony_ci * the front of the cache.
23207db96d56Sopenharmony_ci */
23217db96d56Sopenharmony_cistatic void
23227db96d56Sopenharmony_ciremove_from_strong_cache(StrongCacheNode *node)
23237db96d56Sopenharmony_ci{
23247db96d56Sopenharmony_ci    if (ZONEINFO_STRONG_CACHE == node) {
23257db96d56Sopenharmony_ci        ZONEINFO_STRONG_CACHE = node->next;
23267db96d56Sopenharmony_ci    }
23277db96d56Sopenharmony_ci
23287db96d56Sopenharmony_ci    if (node->prev != NULL) {
23297db96d56Sopenharmony_ci        node->prev->next = node->next;
23307db96d56Sopenharmony_ci    }
23317db96d56Sopenharmony_ci
23327db96d56Sopenharmony_ci    if (node->next != NULL) {
23337db96d56Sopenharmony_ci        node->next->prev = node->prev;
23347db96d56Sopenharmony_ci    }
23357db96d56Sopenharmony_ci
23367db96d56Sopenharmony_ci    node->next = NULL;
23377db96d56Sopenharmony_ci    node->prev = NULL;
23387db96d56Sopenharmony_ci}
23397db96d56Sopenharmony_ci
23407db96d56Sopenharmony_ci/* Retrieves the node associated with a key, if it exists.
23417db96d56Sopenharmony_ci *
23427db96d56Sopenharmony_ci * This traverses the strong cache until it finds a matching key and returns a
23437db96d56Sopenharmony_ci * pointer to the relevant node if found. Returns NULL if no node is found.
23447db96d56Sopenharmony_ci *
23457db96d56Sopenharmony_ci * root may be NULL, indicating an empty cache.
23467db96d56Sopenharmony_ci */
23477db96d56Sopenharmony_cistatic StrongCacheNode *
23487db96d56Sopenharmony_cifind_in_strong_cache(const StrongCacheNode *const root, PyObject *const key)
23497db96d56Sopenharmony_ci{
23507db96d56Sopenharmony_ci    const StrongCacheNode *node = root;
23517db96d56Sopenharmony_ci    while (node != NULL) {
23527db96d56Sopenharmony_ci        int rv = PyObject_RichCompareBool(key, node->key, Py_EQ);
23537db96d56Sopenharmony_ci        if (rv < 0) {
23547db96d56Sopenharmony_ci            return NULL;
23557db96d56Sopenharmony_ci        }
23567db96d56Sopenharmony_ci        if (rv) {
23577db96d56Sopenharmony_ci            return (StrongCacheNode *)node;
23587db96d56Sopenharmony_ci        }
23597db96d56Sopenharmony_ci
23607db96d56Sopenharmony_ci        node = node->next;
23617db96d56Sopenharmony_ci    }
23627db96d56Sopenharmony_ci
23637db96d56Sopenharmony_ci    return NULL;
23647db96d56Sopenharmony_ci}
23657db96d56Sopenharmony_ci
23667db96d56Sopenharmony_ci/* Ejects a given key from the class's strong cache, if applicable.
23677db96d56Sopenharmony_ci *
23687db96d56Sopenharmony_ci * This function is used to enable the per-key functionality in clear_cache.
23697db96d56Sopenharmony_ci */
23707db96d56Sopenharmony_cistatic int
23717db96d56Sopenharmony_cieject_from_strong_cache(const PyTypeObject *const type, PyObject *key)
23727db96d56Sopenharmony_ci{
23737db96d56Sopenharmony_ci    if (type != &PyZoneInfo_ZoneInfoType) {
23747db96d56Sopenharmony_ci        return 0;
23757db96d56Sopenharmony_ci    }
23767db96d56Sopenharmony_ci
23777db96d56Sopenharmony_ci    StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key);
23787db96d56Sopenharmony_ci    if (node != NULL) {
23797db96d56Sopenharmony_ci        remove_from_strong_cache(node);
23807db96d56Sopenharmony_ci
23817db96d56Sopenharmony_ci        strong_cache_node_free(node);
23827db96d56Sopenharmony_ci    }
23837db96d56Sopenharmony_ci    else if (PyErr_Occurred()) {
23847db96d56Sopenharmony_ci        return -1;
23857db96d56Sopenharmony_ci    }
23867db96d56Sopenharmony_ci    return 0;
23877db96d56Sopenharmony_ci}
23887db96d56Sopenharmony_ci
23897db96d56Sopenharmony_ci/* Moves a node to the front of the LRU cache.
23907db96d56Sopenharmony_ci *
23917db96d56Sopenharmony_ci * The strong cache is an LRU cache, so whenever a given node is accessed, if
23927db96d56Sopenharmony_ci * it is not at the front of the cache, it needs to be moved there.
23937db96d56Sopenharmony_ci */
23947db96d56Sopenharmony_cistatic void
23957db96d56Sopenharmony_cimove_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node)
23967db96d56Sopenharmony_ci{
23977db96d56Sopenharmony_ci    StrongCacheNode *root_p = *root;
23987db96d56Sopenharmony_ci    if (root_p == node) {
23997db96d56Sopenharmony_ci        return;
24007db96d56Sopenharmony_ci    }
24017db96d56Sopenharmony_ci
24027db96d56Sopenharmony_ci    remove_from_strong_cache(node);
24037db96d56Sopenharmony_ci
24047db96d56Sopenharmony_ci    node->prev = NULL;
24057db96d56Sopenharmony_ci    node->next = root_p;
24067db96d56Sopenharmony_ci
24077db96d56Sopenharmony_ci    if (root_p != NULL) {
24087db96d56Sopenharmony_ci        root_p->prev = node;
24097db96d56Sopenharmony_ci    }
24107db96d56Sopenharmony_ci
24117db96d56Sopenharmony_ci    *root = node;
24127db96d56Sopenharmony_ci}
24137db96d56Sopenharmony_ci
24147db96d56Sopenharmony_ci/* Retrieves a ZoneInfo from the strong cache if it's present.
24157db96d56Sopenharmony_ci *
24167db96d56Sopenharmony_ci * This function finds the ZoneInfo by key and if found will move the node to
24177db96d56Sopenharmony_ci * the front of the LRU cache and return a new reference to it. It returns NULL
24187db96d56Sopenharmony_ci * if the key is not in the cache.
24197db96d56Sopenharmony_ci *
24207db96d56Sopenharmony_ci * The strong cache is currently only implemented for the base class, so this
24217db96d56Sopenharmony_ci * always returns a cache miss for subclasses.
24227db96d56Sopenharmony_ci */
24237db96d56Sopenharmony_cistatic PyObject *
24247db96d56Sopenharmony_cizone_from_strong_cache(const PyTypeObject *const type, PyObject *const key)
24257db96d56Sopenharmony_ci{
24267db96d56Sopenharmony_ci    if (type != &PyZoneInfo_ZoneInfoType) {
24277db96d56Sopenharmony_ci        return NULL;  // Strong cache currently only implemented for base class
24287db96d56Sopenharmony_ci    }
24297db96d56Sopenharmony_ci
24307db96d56Sopenharmony_ci    StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key);
24317db96d56Sopenharmony_ci
24327db96d56Sopenharmony_ci    if (node != NULL) {
24337db96d56Sopenharmony_ci        move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, node);
24347db96d56Sopenharmony_ci        Py_INCREF(node->zone);
24357db96d56Sopenharmony_ci        return node->zone;
24367db96d56Sopenharmony_ci    }
24377db96d56Sopenharmony_ci
24387db96d56Sopenharmony_ci    return NULL;  // Cache miss
24397db96d56Sopenharmony_ci}
24407db96d56Sopenharmony_ci
24417db96d56Sopenharmony_ci/* Inserts a new key into the strong LRU cache.
24427db96d56Sopenharmony_ci *
24437db96d56Sopenharmony_ci * This function is only to be used after a cache miss — it creates a new node
24447db96d56Sopenharmony_ci * at the front of the cache and ejects any stale entries (keeping the size of
24457db96d56Sopenharmony_ci * the cache to at most ZONEINFO_STRONG_CACHE_MAX_SIZE).
24467db96d56Sopenharmony_ci */
24477db96d56Sopenharmony_cistatic void
24487db96d56Sopenharmony_ciupdate_strong_cache(const PyTypeObject *const type, PyObject *key,
24497db96d56Sopenharmony_ci                    PyObject *zone)
24507db96d56Sopenharmony_ci{
24517db96d56Sopenharmony_ci    if (type != &PyZoneInfo_ZoneInfoType) {
24527db96d56Sopenharmony_ci        return;
24537db96d56Sopenharmony_ci    }
24547db96d56Sopenharmony_ci
24557db96d56Sopenharmony_ci    StrongCacheNode *new_node = strong_cache_node_new(key, zone);
24567db96d56Sopenharmony_ci
24577db96d56Sopenharmony_ci    move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, new_node);
24587db96d56Sopenharmony_ci
24597db96d56Sopenharmony_ci    StrongCacheNode *node = new_node->next;
24607db96d56Sopenharmony_ci    for (size_t i = 1; i < ZONEINFO_STRONG_CACHE_MAX_SIZE; ++i) {
24617db96d56Sopenharmony_ci        if (node == NULL) {
24627db96d56Sopenharmony_ci            return;
24637db96d56Sopenharmony_ci        }
24647db96d56Sopenharmony_ci        node = node->next;
24657db96d56Sopenharmony_ci    }
24667db96d56Sopenharmony_ci
24677db96d56Sopenharmony_ci    // Everything beyond this point needs to be freed
24687db96d56Sopenharmony_ci    if (node != NULL) {
24697db96d56Sopenharmony_ci        if (node->prev != NULL) {
24707db96d56Sopenharmony_ci            node->prev->next = NULL;
24717db96d56Sopenharmony_ci        }
24727db96d56Sopenharmony_ci        strong_cache_free(node);
24737db96d56Sopenharmony_ci    }
24747db96d56Sopenharmony_ci}
24757db96d56Sopenharmony_ci
24767db96d56Sopenharmony_ci/* Clears all entries into a type's strong cache.
24777db96d56Sopenharmony_ci *
24787db96d56Sopenharmony_ci * Because the strong cache is not implemented for subclasses, this is a no-op
24797db96d56Sopenharmony_ci * for everything except the base class.
24807db96d56Sopenharmony_ci */
24817db96d56Sopenharmony_civoid
24827db96d56Sopenharmony_ciclear_strong_cache(const PyTypeObject *const type)
24837db96d56Sopenharmony_ci{
24847db96d56Sopenharmony_ci    if (type != &PyZoneInfo_ZoneInfoType) {
24857db96d56Sopenharmony_ci        return;
24867db96d56Sopenharmony_ci    }
24877db96d56Sopenharmony_ci
24887db96d56Sopenharmony_ci    strong_cache_free(ZONEINFO_STRONG_CACHE);
24897db96d56Sopenharmony_ci    ZONEINFO_STRONG_CACHE = NULL;
24907db96d56Sopenharmony_ci}
24917db96d56Sopenharmony_ci
24927db96d56Sopenharmony_cistatic PyObject *
24937db96d56Sopenharmony_cinew_weak_cache(void)
24947db96d56Sopenharmony_ci{
24957db96d56Sopenharmony_ci    PyObject *weakref_module = PyImport_ImportModule("weakref");
24967db96d56Sopenharmony_ci    if (weakref_module == NULL) {
24977db96d56Sopenharmony_ci        return NULL;
24987db96d56Sopenharmony_ci    }
24997db96d56Sopenharmony_ci
25007db96d56Sopenharmony_ci    PyObject *weak_cache =
25017db96d56Sopenharmony_ci        PyObject_CallMethod(weakref_module, "WeakValueDictionary", "");
25027db96d56Sopenharmony_ci    Py_DECREF(weakref_module);
25037db96d56Sopenharmony_ci    return weak_cache;
25047db96d56Sopenharmony_ci}
25057db96d56Sopenharmony_ci
25067db96d56Sopenharmony_cistatic int
25077db96d56Sopenharmony_ciinitialize_caches(void)
25087db96d56Sopenharmony_ci{
25097db96d56Sopenharmony_ci    // TODO: Move to a PyModule_GetState / PEP 573 based caching system.
25107db96d56Sopenharmony_ci    if (TIMEDELTA_CACHE == NULL) {
25117db96d56Sopenharmony_ci        TIMEDELTA_CACHE = PyDict_New();
25127db96d56Sopenharmony_ci    }
25137db96d56Sopenharmony_ci    else {
25147db96d56Sopenharmony_ci        Py_INCREF(TIMEDELTA_CACHE);
25157db96d56Sopenharmony_ci    }
25167db96d56Sopenharmony_ci
25177db96d56Sopenharmony_ci    if (TIMEDELTA_CACHE == NULL) {
25187db96d56Sopenharmony_ci        return -1;
25197db96d56Sopenharmony_ci    }
25207db96d56Sopenharmony_ci
25217db96d56Sopenharmony_ci    if (ZONEINFO_WEAK_CACHE == NULL) {
25227db96d56Sopenharmony_ci        ZONEINFO_WEAK_CACHE = new_weak_cache();
25237db96d56Sopenharmony_ci    }
25247db96d56Sopenharmony_ci    else {
25257db96d56Sopenharmony_ci        Py_INCREF(ZONEINFO_WEAK_CACHE);
25267db96d56Sopenharmony_ci    }
25277db96d56Sopenharmony_ci
25287db96d56Sopenharmony_ci    if (ZONEINFO_WEAK_CACHE == NULL) {
25297db96d56Sopenharmony_ci        return -1;
25307db96d56Sopenharmony_ci    }
25317db96d56Sopenharmony_ci
25327db96d56Sopenharmony_ci    return 0;
25337db96d56Sopenharmony_ci}
25347db96d56Sopenharmony_ci
25357db96d56Sopenharmony_cistatic PyObject *
25367db96d56Sopenharmony_cizoneinfo_init_subclass(PyTypeObject *cls, PyObject *args, PyObject **kwargs)
25377db96d56Sopenharmony_ci{
25387db96d56Sopenharmony_ci    PyObject *weak_cache = new_weak_cache();
25397db96d56Sopenharmony_ci    if (weak_cache == NULL) {
25407db96d56Sopenharmony_ci        return NULL;
25417db96d56Sopenharmony_ci    }
25427db96d56Sopenharmony_ci
25437db96d56Sopenharmony_ci    if (PyObject_SetAttrString((PyObject *)cls, "_weak_cache",
25447db96d56Sopenharmony_ci                               weak_cache) < 0) {
25457db96d56Sopenharmony_ci        Py_DECREF(weak_cache);
25467db96d56Sopenharmony_ci        return NULL;
25477db96d56Sopenharmony_ci    }
25487db96d56Sopenharmony_ci    Py_DECREF(weak_cache);
25497db96d56Sopenharmony_ci    Py_RETURN_NONE;
25507db96d56Sopenharmony_ci}
25517db96d56Sopenharmony_ci
25527db96d56Sopenharmony_ci/////
25537db96d56Sopenharmony_ci// Specify the ZoneInfo type
25547db96d56Sopenharmony_cistatic PyMethodDef zoneinfo_methods[] = {
25557db96d56Sopenharmony_ci    {"clear_cache", (PyCFunction)(void (*)(void))zoneinfo_clear_cache,
25567db96d56Sopenharmony_ci     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
25577db96d56Sopenharmony_ci     PyDoc_STR("Clear the ZoneInfo cache.")},
25587db96d56Sopenharmony_ci    {"no_cache", (PyCFunction)(void (*)(void))zoneinfo_no_cache,
25597db96d56Sopenharmony_ci     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
25607db96d56Sopenharmony_ci     PyDoc_STR("Get a new instance of ZoneInfo, bypassing the cache.")},
25617db96d56Sopenharmony_ci    {"from_file", (PyCFunction)(void (*)(void))zoneinfo_from_file,
25627db96d56Sopenharmony_ci     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
25637db96d56Sopenharmony_ci     PyDoc_STR("Create a ZoneInfo file from a file object.")},
25647db96d56Sopenharmony_ci    {"utcoffset", (PyCFunction)zoneinfo_utcoffset, METH_O,
25657db96d56Sopenharmony_ci     PyDoc_STR("Retrieve a timedelta representing the UTC offset in a zone at "
25667db96d56Sopenharmony_ci               "the given datetime.")},
25677db96d56Sopenharmony_ci    {"dst", (PyCFunction)zoneinfo_dst, METH_O,
25687db96d56Sopenharmony_ci     PyDoc_STR("Retrieve a timedelta representing the amount of DST applied "
25697db96d56Sopenharmony_ci               "in a zone at the given datetime.")},
25707db96d56Sopenharmony_ci    {"tzname", (PyCFunction)zoneinfo_tzname, METH_O,
25717db96d56Sopenharmony_ci     PyDoc_STR("Retrieve a string containing the abbreviation for the time "
25727db96d56Sopenharmony_ci               "zone that applies in a zone at a given datetime.")},
25737db96d56Sopenharmony_ci    {"fromutc", (PyCFunction)zoneinfo_fromutc, METH_O,
25747db96d56Sopenharmony_ci     PyDoc_STR("Given a datetime with local time in UTC, retrieve an adjusted "
25757db96d56Sopenharmony_ci               "datetime in local time.")},
25767db96d56Sopenharmony_ci    {"__reduce__", (PyCFunction)zoneinfo_reduce, METH_NOARGS,
25777db96d56Sopenharmony_ci     PyDoc_STR("Function for serialization with the pickle protocol.")},
25787db96d56Sopenharmony_ci    {"_unpickle", (PyCFunction)zoneinfo__unpickle, METH_VARARGS | METH_CLASS,
25797db96d56Sopenharmony_ci     PyDoc_STR("Private method used in unpickling.")},
25807db96d56Sopenharmony_ci    {"__init_subclass__", (PyCFunction)(void (*)(void))zoneinfo_init_subclass,
25817db96d56Sopenharmony_ci     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
25827db96d56Sopenharmony_ci     PyDoc_STR("Function to initialize subclasses.")},
25837db96d56Sopenharmony_ci    {NULL} /* Sentinel */
25847db96d56Sopenharmony_ci};
25857db96d56Sopenharmony_ci
25867db96d56Sopenharmony_cistatic PyMemberDef zoneinfo_members[] = {
25877db96d56Sopenharmony_ci    {.name = "key",
25887db96d56Sopenharmony_ci     .offset = offsetof(PyZoneInfo_ZoneInfo, key),
25897db96d56Sopenharmony_ci     .type = T_OBJECT_EX,
25907db96d56Sopenharmony_ci     .flags = READONLY,
25917db96d56Sopenharmony_ci     .doc = NULL},
25927db96d56Sopenharmony_ci    {NULL}, /* Sentinel */
25937db96d56Sopenharmony_ci};
25947db96d56Sopenharmony_ci
25957db96d56Sopenharmony_cistatic PyTypeObject PyZoneInfo_ZoneInfoType = {
25967db96d56Sopenharmony_ci    PyVarObject_HEAD_INIT(NULL, 0)  //
25977db96d56Sopenharmony_ci        .tp_name = "zoneinfo.ZoneInfo",
25987db96d56Sopenharmony_ci    .tp_basicsize = sizeof(PyZoneInfo_ZoneInfo),
25997db96d56Sopenharmony_ci    .tp_weaklistoffset = offsetof(PyZoneInfo_ZoneInfo, weakreflist),
26007db96d56Sopenharmony_ci    .tp_repr = (reprfunc)zoneinfo_repr,
26017db96d56Sopenharmony_ci    .tp_str = (reprfunc)zoneinfo_str,
26027db96d56Sopenharmony_ci    .tp_getattro = PyObject_GenericGetAttr,
26037db96d56Sopenharmony_ci    .tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE),
26047db96d56Sopenharmony_ci    /* .tp_doc = zoneinfo_doc, */
26057db96d56Sopenharmony_ci    .tp_methods = zoneinfo_methods,
26067db96d56Sopenharmony_ci    .tp_members = zoneinfo_members,
26077db96d56Sopenharmony_ci    .tp_new = zoneinfo_new,
26087db96d56Sopenharmony_ci    .tp_dealloc = zoneinfo_dealloc,
26097db96d56Sopenharmony_ci};
26107db96d56Sopenharmony_ci
26117db96d56Sopenharmony_ci/////
26127db96d56Sopenharmony_ci// Specify the _zoneinfo module
26137db96d56Sopenharmony_cistatic PyMethodDef module_methods[] = {{NULL, NULL}};
26147db96d56Sopenharmony_cistatic void
26157db96d56Sopenharmony_cimodule_free(void *m)
26167db96d56Sopenharmony_ci{
26177db96d56Sopenharmony_ci    Py_XDECREF(_tzpath_find_tzfile);
26187db96d56Sopenharmony_ci    _tzpath_find_tzfile = NULL;
26197db96d56Sopenharmony_ci
26207db96d56Sopenharmony_ci    Py_XDECREF(_common_mod);
26217db96d56Sopenharmony_ci    _common_mod = NULL;
26227db96d56Sopenharmony_ci
26237db96d56Sopenharmony_ci    Py_XDECREF(io_open);
26247db96d56Sopenharmony_ci    io_open = NULL;
26257db96d56Sopenharmony_ci
26267db96d56Sopenharmony_ci    xdecref_ttinfo(&NO_TTINFO);
26277db96d56Sopenharmony_ci
26287db96d56Sopenharmony_ci    if (TIMEDELTA_CACHE != NULL && Py_REFCNT(TIMEDELTA_CACHE) > 1) {
26297db96d56Sopenharmony_ci        Py_DECREF(TIMEDELTA_CACHE);
26307db96d56Sopenharmony_ci    } else {
26317db96d56Sopenharmony_ci        Py_CLEAR(TIMEDELTA_CACHE);
26327db96d56Sopenharmony_ci    }
26337db96d56Sopenharmony_ci
26347db96d56Sopenharmony_ci    if (ZONEINFO_WEAK_CACHE != NULL && Py_REFCNT(ZONEINFO_WEAK_CACHE) > 1) {
26357db96d56Sopenharmony_ci        Py_DECREF(ZONEINFO_WEAK_CACHE);
26367db96d56Sopenharmony_ci    } else {
26377db96d56Sopenharmony_ci        Py_CLEAR(ZONEINFO_WEAK_CACHE);
26387db96d56Sopenharmony_ci    }
26397db96d56Sopenharmony_ci
26407db96d56Sopenharmony_ci    clear_strong_cache(&PyZoneInfo_ZoneInfoType);
26417db96d56Sopenharmony_ci}
26427db96d56Sopenharmony_ci
26437db96d56Sopenharmony_cistatic int
26447db96d56Sopenharmony_cizoneinfomodule_exec(PyObject *m)
26457db96d56Sopenharmony_ci{
26467db96d56Sopenharmony_ci    PyDateTime_IMPORT;
26477db96d56Sopenharmony_ci    if (PyDateTimeAPI == NULL) {
26487db96d56Sopenharmony_ci        goto error;
26497db96d56Sopenharmony_ci    }
26507db96d56Sopenharmony_ci    PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType;
26517db96d56Sopenharmony_ci    if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) {
26527db96d56Sopenharmony_ci        goto error;
26537db96d56Sopenharmony_ci    }
26547db96d56Sopenharmony_ci
26557db96d56Sopenharmony_ci    if (PyModule_AddObjectRef(m, "ZoneInfo", (PyObject *)&PyZoneInfo_ZoneInfoType) < 0) {
26567db96d56Sopenharmony_ci        goto error;
26577db96d56Sopenharmony_ci    }
26587db96d56Sopenharmony_ci
26597db96d56Sopenharmony_ci    /* Populate imports */
26607db96d56Sopenharmony_ci    PyObject *_tzpath_module = PyImport_ImportModule("zoneinfo._tzpath");
26617db96d56Sopenharmony_ci    if (_tzpath_module == NULL) {
26627db96d56Sopenharmony_ci        goto error;
26637db96d56Sopenharmony_ci    }
26647db96d56Sopenharmony_ci
26657db96d56Sopenharmony_ci    _tzpath_find_tzfile =
26667db96d56Sopenharmony_ci        PyObject_GetAttrString(_tzpath_module, "find_tzfile");
26677db96d56Sopenharmony_ci    Py_DECREF(_tzpath_module);
26687db96d56Sopenharmony_ci    if (_tzpath_find_tzfile == NULL) {
26697db96d56Sopenharmony_ci        goto error;
26707db96d56Sopenharmony_ci    }
26717db96d56Sopenharmony_ci
26727db96d56Sopenharmony_ci    PyObject *io_module = PyImport_ImportModule("io");
26737db96d56Sopenharmony_ci    if (io_module == NULL) {
26747db96d56Sopenharmony_ci        goto error;
26757db96d56Sopenharmony_ci    }
26767db96d56Sopenharmony_ci
26777db96d56Sopenharmony_ci    io_open = PyObject_GetAttrString(io_module, "open");
26787db96d56Sopenharmony_ci    Py_DECREF(io_module);
26797db96d56Sopenharmony_ci    if (io_open == NULL) {
26807db96d56Sopenharmony_ci        goto error;
26817db96d56Sopenharmony_ci    }
26827db96d56Sopenharmony_ci
26837db96d56Sopenharmony_ci    _common_mod = PyImport_ImportModule("zoneinfo._common");
26847db96d56Sopenharmony_ci    if (_common_mod == NULL) {
26857db96d56Sopenharmony_ci        goto error;
26867db96d56Sopenharmony_ci    }
26877db96d56Sopenharmony_ci
26887db96d56Sopenharmony_ci    if (NO_TTINFO.utcoff == NULL) {
26897db96d56Sopenharmony_ci        NO_TTINFO.utcoff = Py_None;
26907db96d56Sopenharmony_ci        NO_TTINFO.dstoff = Py_None;
26917db96d56Sopenharmony_ci        NO_TTINFO.tzname = Py_None;
26927db96d56Sopenharmony_ci
26937db96d56Sopenharmony_ci        for (size_t i = 0; i < 3; ++i) {
26947db96d56Sopenharmony_ci            Py_INCREF(Py_None);
26957db96d56Sopenharmony_ci        }
26967db96d56Sopenharmony_ci    }
26977db96d56Sopenharmony_ci
26987db96d56Sopenharmony_ci    if (initialize_caches()) {
26997db96d56Sopenharmony_ci        goto error;
27007db96d56Sopenharmony_ci    }
27017db96d56Sopenharmony_ci
27027db96d56Sopenharmony_ci    return 0;
27037db96d56Sopenharmony_ci
27047db96d56Sopenharmony_cierror:
27057db96d56Sopenharmony_ci    return -1;
27067db96d56Sopenharmony_ci}
27077db96d56Sopenharmony_ci
27087db96d56Sopenharmony_cistatic PyModuleDef_Slot zoneinfomodule_slots[] = {
27097db96d56Sopenharmony_ci    {Py_mod_exec, zoneinfomodule_exec}, {0, NULL}};
27107db96d56Sopenharmony_ci
27117db96d56Sopenharmony_cistatic struct PyModuleDef zoneinfomodule = {
27127db96d56Sopenharmony_ci    PyModuleDef_HEAD_INIT,
27137db96d56Sopenharmony_ci    .m_name = "_zoneinfo",
27147db96d56Sopenharmony_ci    .m_doc = "C implementation of the zoneinfo module",
27157db96d56Sopenharmony_ci    .m_size = 0,
27167db96d56Sopenharmony_ci    .m_methods = module_methods,
27177db96d56Sopenharmony_ci    .m_slots = zoneinfomodule_slots,
27187db96d56Sopenharmony_ci    .m_free = (freefunc)module_free};
27197db96d56Sopenharmony_ci
27207db96d56Sopenharmony_ciPyMODINIT_FUNC
27217db96d56Sopenharmony_ciPyInit__zoneinfo(void)
27227db96d56Sopenharmony_ci{
27237db96d56Sopenharmony_ci    return PyModuleDef_Init(&zoneinfomodule);
27247db96d56Sopenharmony_ci}
2725