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, ×tamp)) { 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