LCOV - code coverage report
Current view: | top level - Modules - _zoneinfo.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 78 | 1142 | 6.8 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 10 | 65 | 15.4 %
|  |  | Branches: | 59 | 722 | 8.2 %

               Branch data     Line data    Source code
           1                 :            : #ifndef Py_BUILD_CORE_BUILTIN
           2                 :            : #  define Py_BUILD_CORE_MODULE 1
           3                 :            : #endif
           4                 :            :
           5                 :            : #include "Python.h"
           6                 :            : #include "pycore_long.h"          // _PyLong_GetOne()
           7                 :            : #include "structmember.h"
           8                 :            :
           9                 :            : #include <ctype.h>
          10                 :            : #include <stddef.h>
          11                 :            : #include <stdint.h>
          12                 :            :
          13                 :            : #include "datetime.h"
          14                 :            :
          15                 :            : #include "clinic/_zoneinfo.c.h"
          16                 :            : /*[clinic input]
          17                 :            : module zoneinfo
          18                 :            : class zoneinfo.ZoneInfo "PyObject *" "PyTypeObject *"
          19                 :            : [clinic start generated code]*/
          20                 :            : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=d12c73c0eef36df8]*/
          21                 :            :
          22                 :            :
          23                 :            : typedef struct TransitionRuleType TransitionRuleType;
          24                 :            : typedef struct StrongCacheNode StrongCacheNode;
          25                 :            :
          26                 :            : typedef struct {
          27                 :            :     PyObject *utcoff;
          28                 :            :     PyObject *dstoff;
          29                 :            :     PyObject *tzname;
          30                 :            :     long utcoff_seconds;
          31                 :            : } _ttinfo;
          32                 :            :
          33                 :            : typedef struct {
          34                 :            :     _ttinfo std;
          35                 :            :     _ttinfo dst;
          36                 :            :     int dst_diff;
          37                 :            :     TransitionRuleType *start;
          38                 :            :     TransitionRuleType *end;
          39                 :            :     unsigned char std_only;
          40                 :            : } _tzrule;
          41                 :            :
          42                 :            : typedef struct {
          43                 :            :     PyDateTime_TZInfo base;
          44                 :            :     PyObject *key;
          45                 :            :     PyObject *file_repr;
          46                 :            :     PyObject *weakreflist;
          47                 :            :     size_t num_transitions;
          48                 :            :     size_t num_ttinfos;
          49                 :            :     int64_t *trans_list_utc;
          50                 :            :     int64_t *trans_list_wall[2];
          51                 :            :     _ttinfo **trans_ttinfos;  // References to the ttinfo for each transition
          52                 :            :     _ttinfo *ttinfo_before;
          53                 :            :     _tzrule tzrule_after;
          54                 :            :     _ttinfo *_ttinfos;  // Unique array of ttinfos for ease of deallocation
          55                 :            :     unsigned char fixed_offset;
          56                 :            :     unsigned char source;
          57                 :            : } PyZoneInfo_ZoneInfo;
          58                 :            :
          59                 :            : struct TransitionRuleType {
          60                 :            :     int64_t (*year_to_timestamp)(TransitionRuleType *, int);
          61                 :            : };
          62                 :            :
          63                 :            : typedef struct {
          64                 :            :     TransitionRuleType base;
          65                 :            :     uint8_t month;
          66                 :            :     uint8_t week;
          67                 :            :     uint8_t day;
          68                 :            :     int8_t hour;
          69                 :            :     int8_t minute;
          70                 :            :     int8_t second;
          71                 :            : } CalendarRule;
          72                 :            :
          73                 :            : typedef struct {
          74                 :            :     TransitionRuleType base;
          75                 :            :     uint8_t julian;
          76                 :            :     unsigned int day;
          77                 :            :     int8_t hour;
          78                 :            :     int8_t minute;
          79                 :            :     int8_t second;
          80                 :            : } DayRule;
          81                 :            :
          82                 :            : struct StrongCacheNode {
          83                 :            :     StrongCacheNode *next;
          84                 :            :     StrongCacheNode *prev;
          85                 :            :     PyObject *key;
          86                 :            :     PyObject *zone;
          87                 :            : };
          88                 :            :
          89                 :            : typedef struct {
          90                 :            :     PyTypeObject *ZoneInfoType;
          91                 :            :
          92                 :            :     // Imports
          93                 :            :     PyObject *io_open;
          94                 :            :     PyObject *_tzpath_find_tzfile;
          95                 :            :     PyObject *_common_mod;
          96                 :            :
          97                 :            :     // Caches
          98                 :            :     PyObject *TIMEDELTA_CACHE;
          99                 :            :     PyObject *ZONEINFO_WEAK_CACHE;
         100                 :            :     StrongCacheNode *ZONEINFO_STRONG_CACHE;
         101                 :            :
         102                 :            :     _ttinfo NO_TTINFO;
         103                 :            : } zoneinfo_state;
         104                 :            :
         105                 :            : // Constants
         106                 :            : static const int EPOCHORDINAL = 719163;
         107                 :            : static int DAYS_IN_MONTH[] = {
         108                 :            :     -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
         109                 :            : };
         110                 :            :
         111                 :            : static int DAYS_BEFORE_MONTH[] = {
         112                 :            :     -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
         113                 :            : };
         114                 :            :
         115                 :            : static const int SOURCE_NOCACHE = 0;
         116                 :            : static const int SOURCE_CACHE = 1;
         117                 :            : static const int SOURCE_FILE = 2;
         118                 :            :
         119                 :            : static const size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8;
         120                 :            :
         121                 :            : // Forward declarations
         122                 :            : static int
         123                 :            : load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self,
         124                 :            :           PyObject *file_obj);
         125                 :            : static void
         126                 :            : utcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs,
         127                 :            :                  unsigned char *isdsts, size_t num_transitions,
         128                 :            :                  size_t num_ttinfos);
         129                 :            : static int
         130                 :            : ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff,
         131                 :            :             int64_t *trans_local[2], size_t num_ttinfos,
         132                 :            :             size_t num_transitions);
         133                 :            :
         134                 :            : static int
         135                 :            : parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out);
         136                 :            :
         137                 :            : static Py_ssize_t
         138                 :            : parse_abbr(const char *const p, PyObject **abbr);
         139                 :            : static Py_ssize_t
         140                 :            : parse_tz_delta(const char *const p, long *total_seconds);
         141                 :            : static Py_ssize_t
         142                 :            : parse_transition_time(const char *const p, int8_t *hour, int8_t *minute,
         143                 :            :                       int8_t *second);
         144                 :            : static Py_ssize_t
         145                 :            : parse_transition_rule(const char *const p, TransitionRuleType **out);
         146                 :            :
         147                 :            : static _ttinfo *
         148                 :            : find_tzrule_ttinfo(_tzrule *rule, int64_t ts, unsigned char fold, int year);
         149                 :            : static _ttinfo *
         150                 :            : find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year,
         151                 :            :                            unsigned char *fold);
         152                 :            :
         153                 :            : static int
         154                 :            : build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset,
         155                 :            :              PyObject *tzname, _ttinfo *out);
         156                 :            : static void
         157                 :            : xdecref_ttinfo(_ttinfo *ttinfo);
         158                 :            : static int
         159                 :            : ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1);
         160                 :            :
         161                 :            : static int
         162                 :            : build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr,
         163                 :            :              long std_offset, long dst_offset, TransitionRuleType *start,
         164                 :            :              TransitionRuleType *end, _tzrule *out);
         165                 :            : static void
         166                 :            : free_tzrule(_tzrule *tzrule);
         167                 :            :
         168                 :            : static PyObject *
         169                 :            : load_timedelta(zoneinfo_state *state, long seconds);
         170                 :            :
         171                 :            : static int
         172                 :            : get_local_timestamp(PyObject *dt, int64_t *local_ts);
         173                 :            : static _ttinfo *
         174                 :            : find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt);
         175                 :            :
         176                 :            : static int
         177                 :            : ymd_to_ord(int y, int m, int d);
         178                 :            : static int
         179                 :            : is_leap_year(int year);
         180                 :            :
         181                 :            : static size_t
         182                 :            : _bisect(const int64_t value, const int64_t *arr, size_t size);
         183                 :            :
         184                 :            : static int
         185                 :            : eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
         186                 :            :                         PyObject *key);
         187                 :            : static void
         188                 :            : clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type);
         189                 :            : static void
         190                 :            : update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
         191                 :            :                     PyObject *key, PyObject *zone);
         192                 :            : static PyObject *
         193                 :            : zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
         194                 :            :                        PyObject *const key);
         195                 :            :
         196                 :            : static inline zoneinfo_state *
         197                 :          9 : zoneinfo_get_state(PyObject *mod)
         198                 :            : {
         199                 :          9 :     zoneinfo_state *state = (zoneinfo_state *)PyModule_GetState(mod);
         200                 :            :     assert(state != NULL);
         201                 :          9 :     return state;
         202                 :            : }
         203                 :            :
         204                 :            : static inline zoneinfo_state *
         205                 :          0 : zoneinfo_get_state_by_cls(PyTypeObject *cls)
         206                 :            : {
         207                 :          0 :     zoneinfo_state *state = (zoneinfo_state *)_PyType_GetModuleState(cls);
         208                 :            :     assert(state != NULL);
         209                 :          0 :     return state;
         210                 :            : }
         211                 :            :
         212                 :            : static struct PyModuleDef zoneinfomodule;
         213                 :            :
         214                 :            : static inline zoneinfo_state *
         215                 :          0 : zoneinfo_get_state_by_self(PyTypeObject *self)
         216                 :            : {
         217                 :          0 :     PyObject *mod = PyType_GetModuleByDef(self, &zoneinfomodule);
         218                 :            :     assert(mod != NULL);
         219                 :          0 :     return zoneinfo_get_state(mod);
         220                 :            : }
         221                 :            :
         222                 :            : static PyObject *
         223                 :          0 : zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key)
         224                 :            : {
         225                 :          0 :     PyObject *file_obj = NULL;
         226                 :          0 :     PyObject *file_path = NULL;
         227                 :            :
         228                 :          0 :     file_path = PyObject_CallFunctionObjArgs(state->_tzpath_find_tzfile,
         229                 :            :                                              key, NULL);
         230         [ #  # ]:          0 :     if (file_path == NULL) {
         231                 :          0 :         return NULL;
         232                 :            :     }
         233         [ #  # ]:          0 :     else if (file_path == Py_None) {
         234                 :          0 :         PyObject *meth = state->_common_mod;
         235                 :          0 :         file_obj = PyObject_CallMethod(meth, "load_tzdata", "O", key);
         236         [ #  # ]:          0 :         if (file_obj == NULL) {
         237                 :          0 :             Py_DECREF(file_path);
         238                 :          0 :             return NULL;
         239                 :            :         }
         240                 :            :     }
         241                 :            :
         242                 :          0 :     PyObject *self = (PyObject *)(type->tp_alloc(type, 0));
         243         [ #  # ]:          0 :     if (self == NULL) {
         244                 :          0 :         goto error;
         245                 :            :     }
         246                 :            :
         247         [ #  # ]:          0 :     if (file_obj == NULL) {
         248                 :          0 :         PyObject *func = state->io_open;
         249                 :          0 :         file_obj = PyObject_CallFunction(func, "Os", file_path, "rb");
         250         [ #  # ]:          0 :         if (file_obj == NULL) {
         251                 :          0 :             goto error;
         252                 :            :         }
         253                 :            :     }
         254                 :            :
         255         [ #  # ]:          0 :     if (load_data(state, (PyZoneInfo_ZoneInfo *)self, file_obj)) {
         256                 :          0 :         goto error;
         257                 :            :     }
         258                 :            :
         259                 :          0 :     PyObject *rv = PyObject_CallMethod(file_obj, "close", NULL);
         260                 :          0 :     Py_SETREF(file_obj, NULL);
         261         [ #  # ]:          0 :     if (rv == NULL) {
         262                 :          0 :         goto error;
         263                 :            :     }
         264                 :          0 :     Py_DECREF(rv);
         265                 :            :
         266                 :          0 :     ((PyZoneInfo_ZoneInfo *)self)->key = Py_NewRef(key);
         267                 :            :
         268                 :          0 :     goto cleanup;
         269                 :          0 : error:
         270         [ #  # ]:          0 :     Py_CLEAR(self);
         271                 :          0 : cleanup:
         272         [ #  # ]:          0 :     if (file_obj != NULL) {
         273                 :          0 :         PyObject *exc = PyErr_GetRaisedException();
         274                 :          0 :         PyObject *tmp = PyObject_CallMethod(file_obj, "close", NULL);
         275                 :          0 :         _PyErr_ChainExceptions1(exc);
         276         [ #  # ]:          0 :         if (tmp == NULL) {
         277         [ #  # ]:          0 :             Py_CLEAR(self);
         278                 :            :         }
         279                 :          0 :         Py_XDECREF(tmp);
         280                 :          0 :         Py_DECREF(file_obj);
         281                 :            :     }
         282                 :          0 :     Py_DECREF(file_path);
         283                 :          0 :     return self;
         284                 :            : }
         285                 :            :
         286                 :            : static PyObject *
         287                 :          0 : get_weak_cache(zoneinfo_state *state, PyTypeObject *type)
         288                 :            : {
         289         [ #  # ]:          0 :     if (type == state->ZoneInfoType) {
         290                 :          0 :         return state->ZONEINFO_WEAK_CACHE;
         291                 :            :     }
         292                 :            :     else {
         293                 :            :         PyObject *cache =
         294                 :          0 :             PyObject_GetAttrString((PyObject *)type, "_weak_cache");
         295                 :            :         // We are assuming that the type lives at least as long as the function
         296                 :            :         // that calls get_weak_cache, and that it holds a reference to the
         297                 :            :         // cache, so we'll return a "borrowed reference".
         298                 :          0 :         Py_XDECREF(cache);
         299                 :          0 :         return cache;
         300                 :            :     }
         301                 :            : }
         302                 :            :
         303                 :            : static PyObject *
         304                 :          0 : zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw)
         305                 :            : {
         306                 :          0 :     PyObject *key = NULL;
         307                 :            :     static char *kwlist[] = {"key", NULL};
         308         [ #  # ]:          0 :     if (PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &key) == 0) {
         309                 :          0 :         return NULL;
         310                 :            :     }
         311                 :            :
         312                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_self(type);
         313                 :          0 :     PyObject *instance = zone_from_strong_cache(state, type, key);
         314   [ #  #  #  # ]:          0 :     if (instance != NULL || PyErr_Occurred()) {
         315                 :          0 :         return instance;
         316                 :            :     }
         317                 :            :
         318                 :          0 :     PyObject *weak_cache = get_weak_cache(state, type);
         319                 :          0 :     instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None);
         320         [ #  # ]:          0 :     if (instance == NULL) {
         321                 :          0 :         return NULL;
         322                 :            :     }
         323                 :            :
         324         [ #  # ]:          0 :     if (instance == Py_None) {
         325                 :          0 :         Py_DECREF(instance);
         326                 :          0 :         PyObject *tmp = zoneinfo_new_instance(state, type, key);
         327         [ #  # ]:          0 :         if (tmp == NULL) {
         328                 :          0 :             return NULL;
         329                 :            :         }
         330                 :            :
         331                 :            :         instance =
         332                 :          0 :             PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp);
         333                 :          0 :         Py_DECREF(tmp);
         334         [ #  # ]:          0 :         if (instance == NULL) {
         335                 :          0 :             return NULL;
         336                 :            :         }
         337                 :          0 :         ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
         338                 :            :     }
         339                 :            :
         340                 :          0 :     update_strong_cache(state, type, key, instance);
         341                 :          0 :     return instance;
         342                 :            : }
         343                 :            :
         344                 :            : static int
         345                 :          0 : zoneinfo_traverse(PyZoneInfo_ZoneInfo *self, visitproc visit, void *arg)
         346                 :            : {
         347   [ #  #  #  # ]:          0 :     Py_VISIT(Py_TYPE(self));
         348   [ #  #  #  # ]:          0 :     Py_VISIT(self->key);
         349                 :          0 :     return 0;
         350                 :            : }
         351                 :            :
         352                 :            : static int
         353                 :          0 : zoneinfo_clear(PyZoneInfo_ZoneInfo *self)
         354                 :            : {
         355         [ #  # ]:          0 :     Py_CLEAR(self->key);
         356         [ #  # ]:          0 :     Py_CLEAR(self->file_repr);
         357                 :          0 :     return 0;
         358                 :            : }
         359                 :            :
         360                 :            : static void
         361                 :          0 : zoneinfo_dealloc(PyObject *obj_self)
         362                 :            : {
         363                 :          0 :     PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
         364                 :          0 :     PyTypeObject *tp = Py_TYPE(self);
         365                 :          0 :     PyObject_GC_UnTrack(self);
         366                 :            :
         367         [ #  # ]:          0 :     if (self->weakreflist != NULL) {
         368                 :          0 :         PyObject_ClearWeakRefs(obj_self);
         369                 :            :     }
         370                 :            :
         371         [ #  # ]:          0 :     if (self->trans_list_utc != NULL) {
         372                 :          0 :         PyMem_Free(self->trans_list_utc);
         373                 :            :     }
         374                 :            :
         375         [ #  # ]:          0 :     for (size_t i = 0; i < 2; i++) {
         376         [ #  # ]:          0 :         if (self->trans_list_wall[i] != NULL) {
         377                 :          0 :             PyMem_Free(self->trans_list_wall[i]);
         378                 :            :         }
         379                 :            :     }
         380                 :            :
         381         [ #  # ]:          0 :     if (self->_ttinfos != NULL) {
         382         [ #  # ]:          0 :         for (size_t i = 0; i < self->num_ttinfos; ++i) {
         383                 :          0 :             xdecref_ttinfo(&(self->_ttinfos[i]));
         384                 :            :         }
         385                 :          0 :         PyMem_Free(self->_ttinfos);
         386                 :            :     }
         387                 :            :
         388         [ #  # ]:          0 :     if (self->trans_ttinfos != NULL) {
         389                 :          0 :         PyMem_Free(self->trans_ttinfos);
         390                 :            :     }
         391                 :            :
         392                 :          0 :     free_tzrule(&(self->tzrule_after));
         393                 :            :
         394                 :          0 :     zoneinfo_clear(self);
         395                 :          0 :     tp->tp_free(obj_self);
         396                 :          0 :     Py_DECREF(tp);
         397                 :          0 : }
         398                 :            :
         399                 :            : /*[clinic input]
         400                 :            : @classmethod
         401                 :            : zoneinfo.ZoneInfo.from_file
         402                 :            :
         403                 :            :     cls: defining_class
         404                 :            :     file_obj: object
         405                 :            :     /
         406                 :            :     key: object = None
         407                 :            :
         408                 :            : Create a ZoneInfo file from a file object.
         409                 :            : [clinic start generated code]*/
         410                 :            :
         411                 :            : static PyObject *
         412                 :          0 : zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls,
         413                 :            :                                  PyObject *file_obj, PyObject *key)
         414                 :            : /*[clinic end generated code: output=77887d1d56a48324 input=d26111f29eed6863]*/
         415                 :            : {
         416                 :          0 :     PyObject *file_repr = NULL;
         417                 :          0 :     PyZoneInfo_ZoneInfo *self = NULL;
         418                 :            :
         419                 :          0 :     PyObject *obj_self = (PyObject *)(type->tp_alloc(type, 0));
         420                 :          0 :     self = (PyZoneInfo_ZoneInfo *)obj_self;
         421         [ #  # ]:          0 :     if (self == NULL) {
         422                 :          0 :         return NULL;
         423                 :            :     }
         424                 :            :
         425                 :          0 :     file_repr = PyUnicode_FromFormat("%R", file_obj);
         426         [ #  # ]:          0 :     if (file_repr == NULL) {
         427                 :          0 :         goto error;
         428                 :            :     }
         429                 :            :
         430                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         431         [ #  # ]:          0 :     if (load_data(state, self, file_obj)) {
         432                 :          0 :         goto error;
         433                 :            :     }
         434                 :            :
         435                 :          0 :     self->source = SOURCE_FILE;
         436                 :          0 :     self->file_repr = file_repr;
         437                 :          0 :     self->key = Py_NewRef(key);
         438                 :          0 :     return obj_self;
         439                 :            :
         440                 :          0 : error:
         441                 :          0 :     Py_XDECREF(file_repr);
         442                 :          0 :     Py_XDECREF(self);
         443                 :          0 :     return NULL;
         444                 :            : }
         445                 :            :
         446                 :            : /*[clinic input]
         447                 :            : @classmethod
         448                 :            : zoneinfo.ZoneInfo.no_cache
         449                 :            :
         450                 :            :     cls: defining_class
         451                 :            :     /
         452                 :            :     key: object
         453                 :            :
         454                 :            : Get a new instance of ZoneInfo, bypassing the cache.
         455                 :            : [clinic start generated code]*/
         456                 :            :
         457                 :            : static PyObject *
         458                 :          0 : zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls,
         459                 :            :                                 PyObject *key)
         460                 :            : /*[clinic end generated code: output=b0b09b3344c171b7 input=0238f3d56b1ea3f1]*/
         461                 :            : {
         462                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         463                 :          0 :     PyObject *out = zoneinfo_new_instance(state, type, key);
         464         [ #  # ]:          0 :     if (out != NULL) {
         465                 :          0 :         ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE;
         466                 :            :     }
         467                 :            :
         468                 :          0 :     return out;
         469                 :            : }
         470                 :            :
         471                 :            : /*[clinic input]
         472                 :            : @classmethod
         473                 :            : zoneinfo.ZoneInfo.clear_cache
         474                 :            :
         475                 :            :     cls: defining_class
         476                 :            :     /
         477                 :            :     *
         478                 :            :     only_keys: object = None
         479                 :            :
         480                 :            : Clear the ZoneInfo cache.
         481                 :            : [clinic start generated code]*/
         482                 :            :
         483                 :            : static PyObject *
         484                 :          0 : zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls,
         485                 :            :                                    PyObject *only_keys)
         486                 :            : /*[clinic end generated code: output=114d9b7c8a22e660 input=e32ca3bb396788ba]*/
         487                 :            : {
         488                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         489                 :          0 :     PyObject *weak_cache = get_weak_cache(state, type);
         490                 :            :
         491   [ #  #  #  # ]:          0 :     if (only_keys == NULL || only_keys == Py_None) {
         492                 :          0 :         PyObject *rv = PyObject_CallMethod(weak_cache, "clear", NULL);
         493         [ #  # ]:          0 :         if (rv != NULL) {
         494                 :          0 :             Py_DECREF(rv);
         495                 :            :         }
         496                 :            :
         497                 :          0 :         clear_strong_cache(state, type);
         498                 :            :     }
         499                 :            :     else {
         500                 :          0 :         PyObject *item = NULL;
         501                 :          0 :         PyObject *pop = PyUnicode_FromString("pop");
         502         [ #  # ]:          0 :         if (pop == NULL) {
         503                 :          0 :             return NULL;
         504                 :            :         }
         505                 :            :
         506                 :          0 :         PyObject *iter = PyObject_GetIter(only_keys);
         507         [ #  # ]:          0 :         if (iter == NULL) {
         508                 :          0 :             Py_DECREF(pop);
         509                 :          0 :             return NULL;
         510                 :            :         }
         511                 :            :
         512         [ #  # ]:          0 :         while ((item = PyIter_Next(iter))) {
         513                 :            :             // Remove from strong cache
         514         [ #  # ]:          0 :             if (eject_from_strong_cache(state, type, item) < 0) {
         515                 :          0 :                 Py_DECREF(item);
         516                 :          0 :                 break;
         517                 :            :             }
         518                 :            :
         519                 :            :             // Remove from weak cache
         520                 :          0 :             PyObject *tmp = PyObject_CallMethodObjArgs(weak_cache, pop, item,
         521                 :            :                                                        Py_None, NULL);
         522                 :            :
         523                 :          0 :             Py_DECREF(item);
         524         [ #  # ]:          0 :             if (tmp == NULL) {
         525                 :          0 :                 break;
         526                 :            :             }
         527                 :          0 :             Py_DECREF(tmp);
         528                 :            :         }
         529                 :          0 :         Py_DECREF(iter);
         530                 :          0 :         Py_DECREF(pop);
         531                 :            :     }
         532                 :            :
         533         [ #  # ]:          0 :     if (PyErr_Occurred()) {
         534                 :          0 :         return NULL;
         535                 :            :     }
         536                 :            :
         537                 :          0 :     Py_RETURN_NONE;
         538                 :            : }
         539                 :            :
         540                 :            : /*[clinic input]
         541                 :            : zoneinfo.ZoneInfo.utcoffset
         542                 :            :
         543                 :            :     cls: defining_class
         544                 :            :     dt: object
         545                 :            :     /
         546                 :            :
         547                 :            : Retrieve a timedelta representing the UTC offset in a zone at the given datetime.
         548                 :            : [clinic start generated code]*/
         549                 :            :
         550                 :            : static PyObject *
         551                 :          0 : zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls,
         552                 :            :                                  PyObject *dt)
         553                 :            : /*[clinic end generated code: output=b71016c319ba1f91 input=2bb6c5364938f19c]*/
         554                 :            : {
         555                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         556                 :          0 :     _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
         557         [ #  # ]:          0 :     if (tti == NULL) {
         558                 :          0 :         return NULL;
         559                 :            :     }
         560                 :          0 :     return Py_NewRef(tti->utcoff);
         561                 :            : }
         562                 :            :
         563                 :            : /*[clinic input]
         564                 :            : zoneinfo.ZoneInfo.dst
         565                 :            :
         566                 :            :     cls: defining_class
         567                 :            :     dt: object
         568                 :            :     /
         569                 :            :
         570                 :            : Retrieve a timedelta representing the amount of DST applied in a zone at the given datetime.
         571                 :            : [clinic start generated code]*/
         572                 :            :
         573                 :            : static PyObject *
         574                 :          0 : zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt)
         575                 :            : /*[clinic end generated code: output=cb6168d7723a6ae6 input=2167fb80cf8645c6]*/
         576                 :            : {
         577                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         578                 :          0 :     _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
         579         [ #  # ]:          0 :     if (tti == NULL) {
         580                 :          0 :         return NULL;
         581                 :            :     }
         582                 :          0 :     return Py_NewRef(tti->dstoff);
         583                 :            : }
         584                 :            :
         585                 :            : /*[clinic input]
         586                 :            : zoneinfo.ZoneInfo.tzname
         587                 :            :
         588                 :            :     cls: defining_class
         589                 :            :     dt: object
         590                 :            :     /
         591                 :            :
         592                 :            : Retrieve a string containing the abbreviation for the time zone that applies in a zone at a given datetime.
         593                 :            : [clinic start generated code]*/
         594                 :            :
         595                 :            : static PyObject *
         596                 :          0 : zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls,
         597                 :            :                               PyObject *dt)
         598                 :            : /*[clinic end generated code: output=3b6ae6c3053ea75a input=15a59a4f92ed1f1f]*/
         599                 :            : {
         600                 :          0 :     zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         601                 :          0 :     _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
         602         [ #  # ]:          0 :     if (tti == NULL) {
         603                 :          0 :         return NULL;
         604                 :            :     }
         605                 :          0 :     return Py_NewRef(tti->tzname);
         606                 :            : }
         607                 :            :
         608                 :            : #define GET_DT_TZINFO PyDateTime_DATE_GET_TZINFO
         609                 :            :
         610                 :            : static PyObject *
         611                 :          0 : zoneinfo_fromutc(PyObject *obj_self, PyObject *dt)
         612                 :            : {
         613         [ #  # ]:          0 :     if (!PyDateTime_Check(dt)) {
         614                 :          0 :         PyErr_SetString(PyExc_TypeError,
         615                 :            :                         "fromutc: argument must be a datetime");
         616                 :          0 :         return NULL;
         617                 :            :     }
         618   [ #  #  #  # ]:          0 :     if (GET_DT_TZINFO(dt) != obj_self) {
         619                 :          0 :         PyErr_SetString(PyExc_ValueError,
         620                 :            :                         "fromutc: dt.tzinfo "
         621                 :            :                         "is not self");
         622                 :          0 :         return NULL;
         623                 :            :     }
         624                 :            :
         625                 :          0 :     PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
         626                 :            :
         627                 :            :     int64_t timestamp;
         628         [ #  # ]:          0 :     if (get_local_timestamp(dt, &timestamp)) {
         629                 :          0 :         return NULL;
         630                 :            :     }
         631                 :          0 :     size_t num_trans = self->num_transitions;
         632                 :            :
         633                 :          0 :     _ttinfo *tti = NULL;
         634                 :          0 :     unsigned char fold = 0;
         635                 :            :
         636   [ #  #  #  # ]:          0 :     if (num_trans >= 1 && timestamp < self->trans_list_utc[0]) {
         637                 :          0 :         tti = self->ttinfo_before;
         638                 :            :     }
         639         [ #  # ]:          0 :     else if (num_trans == 0 ||
         640         [ #  # ]:          0 :              timestamp > self->trans_list_utc[num_trans - 1]) {
         641                 :          0 :         tti = find_tzrule_ttinfo_fromutc(&(self->tzrule_after), timestamp,
         642                 :          0 :                                          PyDateTime_GET_YEAR(dt), &fold);
         643                 :            :
         644                 :            :         // Immediately after the last manual transition, the fold/gap is
         645                 :            :         // between self->trans_ttinfos[num_transitions - 1] and whatever
         646                 :            :         // ttinfo applies immediately after the last transition, not between
         647                 :            :         // the STD and DST rules in the tzrule_after, so we may need to
         648                 :            :         // adjust the fold value.
         649         [ #  # ]:          0 :         if (num_trans) {
         650                 :          0 :             _ttinfo *tti_prev = NULL;
         651         [ #  # ]:          0 :             if (num_trans == 1) {
         652                 :          0 :                 tti_prev = self->ttinfo_before;
         653                 :            :             }
         654                 :            :             else {
         655                 :          0 :                 tti_prev = self->trans_ttinfos[num_trans - 2];
         656                 :            :             }
         657                 :          0 :             int64_t diff = tti_prev->utcoff_seconds - tti->utcoff_seconds;
         658         [ #  # ]:          0 :             if (diff > 0 &&
         659         [ #  # ]:          0 :                 timestamp < (self->trans_list_utc[num_trans - 1] + diff)) {
         660                 :          0 :                 fold = 1;
         661                 :            :             }
         662                 :            :         }
         663                 :            :     }
         664                 :            :     else {
         665                 :          0 :         size_t idx = _bisect(timestamp, self->trans_list_utc, num_trans);
         666                 :          0 :         _ttinfo *tti_prev = NULL;
         667                 :            :
         668         [ #  # ]:          0 :         if (idx >= 2) {
         669                 :          0 :             tti_prev = self->trans_ttinfos[idx - 2];
         670                 :          0 :             tti = self->trans_ttinfos[idx - 1];
         671                 :            :         }
         672                 :            :         else {
         673                 :          0 :             tti_prev = self->ttinfo_before;
         674                 :          0 :             tti = self->trans_ttinfos[0];
         675                 :            :         }
         676                 :            :
         677                 :            :         // Detect fold
         678                 :          0 :         int64_t shift =
         679                 :          0 :             (int64_t)(tti_prev->utcoff_seconds - tti->utcoff_seconds);
         680         [ #  # ]:          0 :         if (shift > (timestamp - self->trans_list_utc[idx - 1])) {
         681                 :          0 :             fold = 1;
         682                 :            :         }
         683                 :            :     }
         684                 :            :
         685                 :          0 :     PyObject *tmp = PyNumber_Add(dt, tti->utcoff);
         686         [ #  # ]:          0 :     if (tmp == NULL) {
         687                 :          0 :         return NULL;
         688                 :            :     }
         689                 :            :
         690         [ #  # ]:          0 :     if (fold) {
         691         [ #  # ]:          0 :         if (PyDateTime_CheckExact(tmp)) {
         692                 :          0 :             ((PyDateTime_DateTime *)tmp)->fold = 1;
         693                 :          0 :             dt = tmp;
         694                 :            :         }
         695                 :            :         else {
         696                 :          0 :             PyObject *replace = PyObject_GetAttrString(tmp, "replace");
         697                 :          0 :             PyObject *args = PyTuple_New(0);
         698                 :          0 :             PyObject *kwargs = PyDict_New();
         699                 :            :
         700                 :          0 :             Py_DECREF(tmp);
         701   [ #  #  #  #  :          0 :             if (args == NULL || kwargs == NULL || replace == NULL) {
                       #  # ]
         702                 :          0 :                 Py_XDECREF(args);
         703                 :          0 :                 Py_XDECREF(kwargs);
         704                 :          0 :                 Py_XDECREF(replace);
         705                 :          0 :                 return NULL;
         706                 :            :             }
         707                 :            :
         708                 :          0 :             dt = NULL;
         709         [ #  # ]:          0 :             if (!PyDict_SetItemString(kwargs, "fold", _PyLong_GetOne())) {
         710                 :          0 :                 dt = PyObject_Call(replace, args, kwargs);
         711                 :            :             }
         712                 :            :
         713                 :          0 :             Py_DECREF(args);
         714                 :          0 :             Py_DECREF(kwargs);
         715                 :          0 :             Py_DECREF(replace);
         716                 :            :
         717         [ #  # ]:          0 :             if (dt == NULL) {
         718                 :          0 :                 return NULL;
         719                 :            :             }
         720                 :            :         }
         721                 :            :     }
         722                 :            :     else {
         723                 :          0 :         dt = tmp;
         724                 :            :     }
         725                 :          0 :     return dt;
         726                 :            : }
         727                 :            :
         728                 :            : static PyObject *
         729                 :          0 : zoneinfo_repr(PyZoneInfo_ZoneInfo *self)
         730                 :            : {
         731                 :          0 :     PyObject *rv = NULL;
         732                 :          0 :     const char *type_name = Py_TYPE((PyObject *)self)->tp_name;
         733         [ #  # ]:          0 :     if (!(self->key == Py_None)) {
         734                 :          0 :         rv = PyUnicode_FromFormat("%s(key=%R)", type_name, self->key);
         735                 :            :     }
         736                 :            :     else {
         737                 :            :         assert(PyUnicode_Check(self->file_repr));
         738                 :          0 :         rv = PyUnicode_FromFormat("%s.from_file(%U)", type_name,
         739                 :            :                                   self->file_repr);
         740                 :            :     }
         741                 :            :
         742                 :          0 :     return rv;
         743                 :            : }
         744                 :            :
         745                 :            : static PyObject *
         746                 :          0 : zoneinfo_str(PyZoneInfo_ZoneInfo *self)
         747                 :            : {
         748         [ #  # ]:          0 :     if (!(self->key == Py_None)) {
         749                 :          0 :         return Py_NewRef(self->key);
         750                 :            :     }
         751                 :            :     else {
         752                 :          0 :         return zoneinfo_repr(self);
         753                 :            :     }
         754                 :            : }
         755                 :            :
         756                 :            : /* Pickles the ZoneInfo object by key and source.
         757                 :            :  *
         758                 :            :  * ZoneInfo objects are pickled by reference to the TZif file that they came
         759                 :            :  * from, which means that the exact transitions may be different or the file
         760                 :            :  * may not un-pickle if the data has changed on disk in the interim.
         761                 :            :  *
         762                 :            :  * It is necessary to include a bit indicating whether or not the object
         763                 :            :  * was constructed from the cache, because from-cache objects will hit the
         764                 :            :  * unpickling process's cache, whereas no-cache objects will bypass it.
         765                 :            :  *
         766                 :            :  * Objects constructed from ZoneInfo.from_file cannot be pickled.
         767                 :            :  */
         768                 :            : static PyObject *
         769                 :          0 : zoneinfo_reduce(PyObject *obj_self, PyObject *unused)
         770                 :            : {
         771                 :          0 :     PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
         772         [ #  # ]:          0 :     if (self->source == SOURCE_FILE) {
         773                 :            :         // Objects constructed from files cannot be pickled.
         774                 :            :         PyObject *pickle_error =
         775                 :          0 :             _PyImport_GetModuleAttrString("pickle", "PicklingError");
         776         [ #  # ]:          0 :         if (pickle_error == NULL) {
         777                 :          0 :             return NULL;
         778                 :            :         }
         779                 :            :
         780                 :          0 :         PyErr_Format(pickle_error,
         781                 :            :                      "Cannot pickle a ZoneInfo file from a file stream.");
         782                 :          0 :         Py_DECREF(pickle_error);
         783                 :          0 :         return NULL;
         784                 :            :     }
         785                 :            :
         786                 :          0 :     unsigned char from_cache = self->source == SOURCE_CACHE ? 1 : 0;
         787                 :          0 :     PyObject *constructor = PyObject_GetAttrString(obj_self, "_unpickle");
         788                 :            :
         789         [ #  # ]:          0 :     if (constructor == NULL) {
         790                 :          0 :         return NULL;
         791                 :            :     }
         792                 :            :
         793                 :          0 :     PyObject *rv = Py_BuildValue("O(OB)", constructor, self->key, from_cache);
         794                 :          0 :     Py_DECREF(constructor);
         795                 :          0 :     return rv;
         796                 :            : }
         797                 :            :
         798                 :            : /*[clinic input]
         799                 :            : @classmethod
         800                 :            : zoneinfo.ZoneInfo._unpickle
         801                 :            :
         802                 :            :     cls: defining_class
         803                 :            :     key: object
         804                 :            :     from_cache: unsigned_char(bitwise=True)
         805                 :            :     /
         806                 :            :
         807                 :            : Private method used in unpickling.
         808                 :            : [clinic start generated code]*/
         809                 :            :
         810                 :            : static PyObject *
         811                 :          0 : zoneinfo_ZoneInfo__unpickle_impl(PyTypeObject *type, PyTypeObject *cls,
         812                 :            :                                  PyObject *key, unsigned char from_cache)
         813                 :            : /*[clinic end generated code: output=556712fc709deecb input=6ac8c73eed3de316]*/
         814                 :            : {
         815         [ #  # ]:          0 :     if (from_cache) {
         816                 :          0 :         PyObject *val_args = Py_BuildValue("(O)", key);
         817         [ #  # ]:          0 :         if (val_args == NULL) {
         818                 :          0 :             return NULL;
         819                 :            :         }
         820                 :            :
         821                 :          0 :         PyObject *rv = zoneinfo_new(type, val_args, NULL);
         822                 :            :
         823                 :          0 :         Py_DECREF(val_args);
         824                 :          0 :         return rv;
         825                 :            :     }
         826                 :            :     else {
         827                 :          0 :         zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
         828                 :          0 :         return zoneinfo_new_instance(state, type, key);
         829                 :            :     }
         830                 :            : }
         831                 :            :
         832                 :            : /* It is relatively expensive to construct new timedelta objects, and in most
         833                 :            :  * cases we're looking at a relatively small number of timedeltas, such as
         834                 :            :  * integer number of hours, etc. We will keep a cache so that we construct
         835                 :            :  * a minimal number of these.
         836                 :            :  *
         837                 :            :  * Possibly this should be replaced with an LRU cache so that it's not possible
         838                 :            :  * for the memory usage to explode from this, but in order for this to be a
         839                 :            :  * serious problem, one would need to deliberately craft a malicious time zone
         840                 :            :  * file with many distinct offsets. As of tzdb 2019c, loading every single zone
         841                 :            :  * fills the cache with ~450 timedeltas for a total size of ~12kB.
         842                 :            :  *
         843                 :            :  * This returns a new reference to the timedelta.
         844                 :            :  */
         845                 :            : static PyObject *
         846                 :          0 : load_timedelta(zoneinfo_state *state, long seconds)
         847                 :            : {
         848                 :            :     PyObject *rv;
         849                 :          0 :     PyObject *pyoffset = PyLong_FromLong(seconds);
         850         [ #  # ]:          0 :     if (pyoffset == NULL) {
         851                 :          0 :         return NULL;
         852                 :            :     }
         853                 :          0 :     rv = PyDict_GetItemWithError(state->TIMEDELTA_CACHE, pyoffset);
         854         [ #  # ]:          0 :     if (rv == NULL) {
         855         [ #  # ]:          0 :         if (PyErr_Occurred()) {
         856                 :          0 :             goto error;
         857                 :            :         }
         858                 :          0 :         PyObject *tmp = PyDateTimeAPI->Delta_FromDelta(
         859                 :          0 :             0, seconds, 0, 1, PyDateTimeAPI->DeltaType);
         860                 :            :
         861         [ #  # ]:          0 :         if (tmp == NULL) {
         862                 :          0 :             goto error;
         863                 :            :         }
         864                 :            :
         865                 :          0 :         rv = PyDict_SetDefault(state->TIMEDELTA_CACHE, pyoffset, tmp);
         866                 :          0 :         Py_DECREF(tmp);
         867                 :            :     }
         868                 :            :
         869                 :          0 :     Py_XINCREF(rv);
         870                 :          0 :     Py_DECREF(pyoffset);
         871                 :          0 :     return rv;
         872                 :          0 : error:
         873                 :          0 :     Py_DECREF(pyoffset);
         874                 :          0 :     return NULL;
         875                 :            : }
         876                 :            :
         877                 :            : /* Constructor for _ttinfo object - this starts by initializing the _ttinfo
         878                 :            :  * to { NULL, NULL, NULL }, so that Py_XDECREF will work on partially
         879                 :            :  * initialized _ttinfo objects.
         880                 :            :  */
         881                 :            : static int
         882                 :          0 : build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset,
         883                 :            :              PyObject *tzname, _ttinfo *out)
         884                 :            : {
         885                 :          0 :     out->utcoff = NULL;
         886                 :          0 :     out->dstoff = NULL;
         887                 :          0 :     out->tzname = NULL;
         888                 :            :
         889                 :          0 :     out->utcoff_seconds = utcoffset;
         890                 :          0 :     out->utcoff = load_timedelta(state, utcoffset);
         891         [ #  # ]:          0 :     if (out->utcoff == NULL) {
         892                 :          0 :         return -1;
         893                 :            :     }
         894                 :            :
         895                 :          0 :     out->dstoff = load_timedelta(state, dstoffset);
         896         [ #  # ]:          0 :     if (out->dstoff == NULL) {
         897                 :          0 :         return -1;
         898                 :            :     }
         899                 :            :
         900                 :          0 :     out->tzname = Py_NewRef(tzname);
         901                 :            :
         902                 :          0 :     return 0;
         903                 :            : }
         904                 :            :
         905                 :            : /* Decrease reference count on any non-NULL members of a _ttinfo  */
         906                 :            : static void
         907                 :          0 : xdecref_ttinfo(_ttinfo *ttinfo)
         908                 :            : {
         909         [ #  # ]:          0 :     if (ttinfo != NULL) {
         910                 :          0 :         Py_XDECREF(ttinfo->utcoff);
         911                 :          0 :         Py_XDECREF(ttinfo->dstoff);
         912                 :          0 :         Py_XDECREF(ttinfo->tzname);
         913                 :            :     }
         914                 :          0 : }
         915                 :            :
         916                 :            : /* Equality function for _ttinfo. */
         917                 :            : static int
         918                 :          0 : ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1)
         919                 :            : {
         920                 :            :     int rv;
         921         [ #  # ]:          0 :     if ((rv = PyObject_RichCompareBool(tti0->utcoff, tti1->utcoff, Py_EQ)) <
         922                 :            :         1) {
         923                 :          0 :         goto end;
         924                 :            :     }
         925                 :            :
         926         [ #  # ]:          0 :     if ((rv = PyObject_RichCompareBool(tti0->dstoff, tti1->dstoff, Py_EQ)) <
         927                 :            :         1) {
         928                 :          0 :         goto end;
         929                 :            :     }
         930                 :            :
         931         [ #  # ]:          0 :     if ((rv = PyObject_RichCompareBool(tti0->tzname, tti1->tzname, Py_EQ)) <
         932                 :            :         1) {
         933                 :          0 :         goto end;
         934                 :            :     }
         935                 :          0 : end:
         936                 :          0 :     return rv;
         937                 :            : }
         938                 :            :
         939                 :            : /* Given a file-like object, this populates a ZoneInfo object
         940                 :            :  *
         941                 :            :  * The current version calls into a Python function to read the data from
         942                 :            :  * file into Python objects, and this translates those Python objects into
         943                 :            :  * C values and calculates derived values (e.g. dstoff) in C.
         944                 :            :  *
         945                 :            :  * This returns 0 on success and -1 on failure.
         946                 :            :  *
         947                 :            :  * The function will never return while `self` is partially initialized —
         948                 :            :  * the object only needs to be freed / deallocated if this succeeds.
         949                 :            :  */
         950                 :            : static int
         951                 :          0 : load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj)
         952                 :            : {
         953                 :          0 :     PyObject *data_tuple = NULL;
         954                 :            :
         955                 :          0 :     long *utcoff = NULL;
         956                 :          0 :     long *dstoff = NULL;
         957                 :          0 :     size_t *trans_idx = NULL;
         958                 :          0 :     unsigned char *isdst = NULL;
         959                 :            :
         960                 :          0 :     self->trans_list_utc = NULL;
         961                 :          0 :     self->trans_list_wall[0] = NULL;
         962                 :          0 :     self->trans_list_wall[1] = NULL;
         963                 :          0 :     self->trans_ttinfos = NULL;
         964                 :          0 :     self->_ttinfos = NULL;
         965                 :          0 :     self->file_repr = NULL;
         966                 :            :
         967                 :          0 :     size_t ttinfos_allocated = 0;
         968                 :            :
         969                 :          0 :     data_tuple = PyObject_CallMethod(state->_common_mod, "load_data", "O",
         970                 :            :                                      file_obj);
         971                 :            :
         972         [ #  # ]:          0 :     if (data_tuple == NULL) {
         973                 :          0 :         goto error;
         974                 :            :     }
         975                 :            :
         976         [ #  # ]:          0 :     if (!PyTuple_CheckExact(data_tuple)) {
         977                 :          0 :         PyErr_Format(PyExc_TypeError, "Invalid data result type: %r",
         978                 :            :                      data_tuple);
         979                 :          0 :         goto error;
         980                 :            :     }
         981                 :            :
         982                 :            :     // Unpack the data tuple
         983                 :          0 :     PyObject *trans_idx_list = PyTuple_GetItem(data_tuple, 0);
         984         [ #  # ]:          0 :     if (trans_idx_list == NULL) {
         985                 :          0 :         goto error;
         986                 :            :     }
         987                 :            :
         988                 :          0 :     PyObject *trans_utc = PyTuple_GetItem(data_tuple, 1);
         989         [ #  # ]:          0 :     if (trans_utc == NULL) {
         990                 :          0 :         goto error;
         991                 :            :     }
         992                 :            :
         993                 :          0 :     PyObject *utcoff_list = PyTuple_GetItem(data_tuple, 2);
         994         [ #  # ]:          0 :     if (utcoff_list == NULL) {
         995                 :          0 :         goto error;
         996                 :            :     }
         997                 :            :
         998                 :          0 :     PyObject *isdst_list = PyTuple_GetItem(data_tuple, 3);
         999         [ #  # ]:          0 :     if (isdst_list == NULL) {
        1000                 :          0 :         goto error;
        1001                 :            :     }
        1002                 :            :
        1003                 :          0 :     PyObject *abbr = PyTuple_GetItem(data_tuple, 4);
        1004         [ #  # ]:          0 :     if (abbr == NULL) {
        1005                 :          0 :         goto error;
        1006                 :            :     }
        1007                 :            :
        1008                 :          0 :     PyObject *tz_str = PyTuple_GetItem(data_tuple, 5);
        1009         [ #  # ]:          0 :     if (tz_str == NULL) {
        1010                 :          0 :         goto error;
        1011                 :            :     }
        1012                 :            :
        1013                 :            :     // Load the relevant sizes
        1014                 :          0 :     Py_ssize_t num_transitions = PyTuple_Size(trans_utc);
        1015         [ #  # ]:          0 :     if (num_transitions < 0) {
        1016                 :          0 :         goto error;
        1017                 :            :     }
        1018                 :            :
        1019                 :          0 :     Py_ssize_t num_ttinfos = PyTuple_Size(utcoff_list);
        1020         [ #  # ]:          0 :     if (num_ttinfos < 0) {
        1021                 :          0 :         goto error;
        1022                 :            :     }
        1023                 :            :
        1024                 :          0 :     self->num_transitions = (size_t)num_transitions;
        1025                 :          0 :     self->num_ttinfos = (size_t)num_ttinfos;
        1026                 :            :
        1027                 :            :     // Load the transition indices and list
        1028                 :          0 :     self->trans_list_utc =
        1029                 :          0 :         PyMem_Malloc(self->num_transitions * sizeof(int64_t));
        1030         [ #  # ]:          0 :     if (self->trans_list_utc == NULL) {
        1031                 :          0 :         goto error;
        1032                 :            :     }
        1033                 :          0 :     trans_idx = PyMem_Malloc(self->num_transitions * sizeof(Py_ssize_t));
        1034         [ #  # ]:          0 :     if (trans_idx == NULL) {
        1035                 :          0 :         goto error;
        1036                 :            :     }
        1037                 :            :
        1038         [ #  # ]:          0 :     for (size_t i = 0; i < self->num_transitions; ++i) {
        1039                 :          0 :         PyObject *num = PyTuple_GetItem(trans_utc, i);
        1040         [ #  # ]:          0 :         if (num == NULL) {
        1041                 :          0 :             goto error;
        1042                 :            :         }
        1043                 :          0 :         self->trans_list_utc[i] = PyLong_AsLongLong(num);
        1044   [ #  #  #  # ]:          0 :         if (self->trans_list_utc[i] == -1 && PyErr_Occurred()) {
        1045                 :          0 :             goto error;
        1046                 :            :         }
        1047                 :            :
        1048                 :          0 :         num = PyTuple_GetItem(trans_idx_list, i);
        1049         [ #  # ]:          0 :         if (num == NULL) {
        1050                 :          0 :             goto error;
        1051                 :            :         }
        1052                 :            :
        1053                 :          0 :         Py_ssize_t cur_trans_idx = PyLong_AsSsize_t(num);
        1054         [ #  # ]:          0 :         if (cur_trans_idx == -1) {
        1055                 :          0 :             goto error;
        1056                 :            :         }
        1057                 :            :
        1058                 :          0 :         trans_idx[i] = (size_t)cur_trans_idx;
        1059         [ #  # ]:          0 :         if (trans_idx[i] > self->num_ttinfos) {
        1060                 :          0 :             PyErr_Format(
        1061                 :            :                 PyExc_ValueError,
        1062                 :            :                 "Invalid transition index found while reading TZif: %zd",
        1063                 :            :                 cur_trans_idx);
        1064                 :            :
        1065                 :          0 :             goto error;
        1066                 :            :         }
        1067                 :            :     }
        1068                 :            :
        1069                 :            :     // Load UTC offsets and isdst (size num_ttinfos)
        1070                 :          0 :     utcoff = PyMem_Malloc(self->num_ttinfos * sizeof(long));
        1071                 :          0 :     isdst = PyMem_Malloc(self->num_ttinfos * sizeof(unsigned char));
        1072                 :            :
        1073   [ #  #  #  # ]:          0 :     if (utcoff == NULL || isdst == NULL) {
        1074                 :          0 :         goto error;
        1075                 :            :     }
        1076         [ #  # ]:          0 :     for (size_t i = 0; i < self->num_ttinfos; ++i) {
        1077                 :          0 :         PyObject *num = PyTuple_GetItem(utcoff_list, i);
        1078         [ #  # ]:          0 :         if (num == NULL) {
        1079                 :          0 :             goto error;
        1080                 :            :         }
        1081                 :            :
        1082                 :          0 :         utcoff[i] = PyLong_AsLong(num);
        1083   [ #  #  #  # ]:          0 :         if (utcoff[i] == -1 && PyErr_Occurred()) {
        1084                 :          0 :             goto error;
        1085                 :            :         }
        1086                 :            :
        1087                 :          0 :         num = PyTuple_GetItem(isdst_list, i);
        1088         [ #  # ]:          0 :         if (num == NULL) {
        1089                 :          0 :             goto error;
        1090                 :            :         }
        1091                 :            :
        1092                 :          0 :         int isdst_with_error = PyObject_IsTrue(num);
        1093         [ #  # ]:          0 :         if (isdst_with_error == -1) {
        1094                 :          0 :             goto error;
        1095                 :            :         }
        1096                 :            :         else {
        1097                 :          0 :             isdst[i] = (unsigned char)isdst_with_error;
        1098                 :            :         }
        1099                 :            :     }
        1100                 :            :
        1101                 :          0 :     dstoff = PyMem_Calloc(self->num_ttinfos, sizeof(long));
        1102         [ #  # ]:          0 :     if (dstoff == NULL) {
        1103                 :          0 :         goto error;
        1104                 :            :     }
        1105                 :            :
        1106                 :            :     // Derive dstoff and trans_list_wall from the information we've loaded
        1107                 :          0 :     utcoff_to_dstoff(trans_idx, utcoff, dstoff, isdst, self->num_transitions,
        1108                 :            :                      self->num_ttinfos);
        1109                 :            :
        1110         [ #  # ]:          0 :     if (ts_to_local(trans_idx, self->trans_list_utc, utcoff,
        1111                 :          0 :                     self->trans_list_wall, self->num_ttinfos,
        1112                 :            :                     self->num_transitions)) {
        1113                 :          0 :         goto error;
        1114                 :            :     }
        1115                 :            :
        1116                 :            :     // Build _ttinfo objects from utcoff, dstoff and abbr
        1117                 :          0 :     self->_ttinfos = PyMem_Malloc(self->num_ttinfos * sizeof(_ttinfo));
        1118         [ #  # ]:          0 :     if (self->_ttinfos == NULL) {
        1119                 :          0 :         goto error;
        1120                 :            :     }
        1121         [ #  # ]:          0 :     for (size_t i = 0; i < self->num_ttinfos; ++i) {
        1122                 :          0 :         PyObject *tzname = PyTuple_GetItem(abbr, i);
        1123         [ #  # ]:          0 :         if (tzname == NULL) {
        1124                 :          0 :             goto error;
        1125                 :            :         }
        1126                 :            :
        1127                 :          0 :         ttinfos_allocated++;
        1128                 :          0 :         int rc = build_ttinfo(state, utcoff[i], dstoff[i], tzname,
        1129                 :          0 :                               &(self->_ttinfos[i]));
        1130         [ #  # ]:          0 :         if (rc) {
        1131                 :          0 :             goto error;
        1132                 :            :         }
        1133                 :            :     }
        1134                 :            :
        1135                 :            :     // Build our mapping from transition to the ttinfo that applies
        1136                 :          0 :     self->trans_ttinfos =
        1137                 :          0 :         PyMem_Calloc(self->num_transitions, sizeof(_ttinfo *));
        1138         [ #  # ]:          0 :     if (self->trans_ttinfos == NULL) {
        1139                 :          0 :         goto error;
        1140                 :            :     }
        1141         [ #  # ]:          0 :     for (size_t i = 0; i < self->num_transitions; ++i) {
        1142                 :          0 :         size_t ttinfo_idx = trans_idx[i];
        1143                 :            :         assert(ttinfo_idx < self->num_ttinfos);
        1144                 :          0 :         self->trans_ttinfos[i] = &(self->_ttinfos[ttinfo_idx]);
        1145                 :            :     }
        1146                 :            :
        1147                 :            :     // Set ttinfo_before to the first non-DST transition
        1148         [ #  # ]:          0 :     for (size_t i = 0; i < self->num_ttinfos; ++i) {
        1149         [ #  # ]:          0 :         if (!isdst[i]) {
        1150                 :          0 :             self->ttinfo_before = &(self->_ttinfos[i]);
        1151                 :          0 :             break;
        1152                 :            :         }
        1153                 :            :     }
        1154                 :            :
        1155                 :            :     // If there are only DST ttinfos, pick the first one, if there are no
        1156                 :            :     // ttinfos at all, set ttinfo_before to NULL
        1157   [ #  #  #  # ]:          0 :     if (self->ttinfo_before == NULL && self->num_ttinfos > 0) {
        1158                 :          0 :         self->ttinfo_before = &(self->_ttinfos[0]);
        1159                 :            :     }
        1160                 :            :
        1161   [ #  #  #  # ]:          0 :     if (tz_str != Py_None && PyObject_IsTrue(tz_str)) {
        1162         [ #  # ]:          0 :         if (parse_tz_str(state, tz_str, &(self->tzrule_after))) {
        1163                 :          0 :             goto error;
        1164                 :            :         }
        1165                 :            :     }
        1166                 :            :     else {
        1167         [ #  # ]:          0 :         if (!self->num_ttinfos) {
        1168                 :          0 :             PyErr_Format(PyExc_ValueError, "No time zone information found.");
        1169                 :          0 :             goto error;
        1170                 :            :         }
        1171                 :            :
        1172                 :            :         size_t idx;
        1173         [ #  # ]:          0 :         if (!self->num_transitions) {
        1174                 :          0 :             idx = self->num_ttinfos - 1;
        1175                 :            :         }
        1176                 :            :         else {
        1177                 :          0 :             idx = trans_idx[self->num_transitions - 1];
        1178                 :            :         }
        1179                 :            :
        1180                 :          0 :         _ttinfo *tti = &(self->_ttinfos[idx]);
        1181                 :          0 :         build_tzrule(state, tti->tzname, NULL, tti->utcoff_seconds, 0, NULL,
        1182                 :            :                      NULL, &(self->tzrule_after));
        1183                 :            :
        1184                 :            :         // We've abused the build_tzrule constructor to construct an STD-only
        1185                 :            :         // rule mimicking whatever ttinfo we've picked up, but it's possible
        1186                 :            :         // that the one we've picked up is a DST zone, so we need to make sure
        1187                 :            :         // that the dstoff is set correctly in that case.
        1188         [ #  # ]:          0 :         if (PyObject_IsTrue(tti->dstoff)) {
        1189                 :          0 :             _ttinfo *tti_after = &(self->tzrule_after.std);
        1190                 :          0 :             Py_SETREF(tti_after->dstoff, Py_NewRef(tti->dstoff));
        1191                 :            :         }
        1192                 :            :     }
        1193                 :            :
        1194                 :            :     // Determine if this is a "fixed offset" zone, meaning that the output of
        1195                 :            :     // the utcoffset, dst and tzname functions does not depend on the specific
        1196                 :            :     // datetime passed.
        1197                 :            :     //
        1198                 :            :     // We make three simplifying assumptions here:
        1199                 :            :     //
        1200                 :            :     // 1. If tzrule_after is not std_only, it has transitions that might occur
        1201                 :            :     //    (it is possible to construct TZ strings that specify STD and DST but
        1202                 :            :     //    no transitions ever occur, such as AAA0BBB,0/0,J365/25).
        1203                 :            :     // 2. If self->_ttinfos contains more than one _ttinfo object, the objects
        1204                 :            :     //    represent different offsets.
        1205                 :            :     // 3. self->ttinfos contains no unused _ttinfos (in which case an otherwise
        1206                 :            :     //    fixed-offset zone with extra _ttinfos defined may appear to *not* be
        1207                 :            :     //    a fixed offset zone).
        1208                 :            :     //
        1209                 :            :     // Violations to these assumptions would be fairly exotic, and exotic
        1210                 :            :     // zones should almost certainly not be used with datetime.time (the
        1211                 :            :     // only thing that would be affected by this).
        1212   [ #  #  #  # ]:          0 :     if (self->num_ttinfos > 1 || !self->tzrule_after.std_only) {
        1213                 :          0 :         self->fixed_offset = 0;
        1214                 :            :     }
        1215         [ #  # ]:          0 :     else if (self->num_ttinfos == 0) {
        1216                 :          0 :         self->fixed_offset = 1;
        1217                 :            :     }
        1218                 :            :     else {
        1219                 :            :         int constant_offset =
        1220                 :          0 :             ttinfo_eq(&(self->_ttinfos[0]), &self->tzrule_after.std);
        1221         [ #  # ]:          0 :         if (constant_offset < 0) {
        1222                 :          0 :             goto error;
        1223                 :            :         }
        1224                 :            :         else {
        1225                 :          0 :             self->fixed_offset = constant_offset;
        1226                 :            :         }
        1227                 :            :     }
        1228                 :            :
        1229                 :          0 :     int rv = 0;
        1230                 :          0 :     goto cleanup;
        1231                 :          0 : error:
        1232                 :            :     // These resources only need to be freed if we have failed, if we succeed
        1233                 :            :     // in initializing a PyZoneInfo_ZoneInfo object, we can rely on its dealloc
        1234                 :            :     // method to free the relevant resources.
        1235         [ #  # ]:          0 :     if (self->trans_list_utc != NULL) {
        1236                 :          0 :         PyMem_Free(self->trans_list_utc);
        1237                 :          0 :         self->trans_list_utc = NULL;
        1238                 :            :     }
        1239                 :            :
        1240         [ #  # ]:          0 :     for (size_t i = 0; i < 2; ++i) {
        1241         [ #  # ]:          0 :         if (self->trans_list_wall[i] != NULL) {
        1242                 :          0 :             PyMem_Free(self->trans_list_wall[i]);
        1243                 :          0 :             self->trans_list_wall[i] = NULL;
        1244                 :            :         }
        1245                 :            :     }
        1246                 :            :
        1247         [ #  # ]:          0 :     if (self->_ttinfos != NULL) {
        1248         [ #  # ]:          0 :         for (size_t i = 0; i < ttinfos_allocated; ++i) {
        1249                 :          0 :             xdecref_ttinfo(&(self->_ttinfos[i]));
        1250                 :            :         }
        1251                 :          0 :         PyMem_Free(self->_ttinfos);
        1252                 :          0 :         self->_ttinfos = NULL;
        1253                 :            :     }
        1254                 :            :
        1255         [ #  # ]:          0 :     if (self->trans_ttinfos != NULL) {
        1256                 :          0 :         PyMem_Free(self->trans_ttinfos);
        1257                 :          0 :         self->trans_ttinfos = NULL;
        1258                 :            :     }
        1259                 :            :
        1260                 :          0 :     rv = -1;
        1261                 :          0 : cleanup:
        1262                 :          0 :     Py_XDECREF(data_tuple);
        1263                 :            :
        1264         [ #  # ]:          0 :     if (utcoff != NULL) {
        1265                 :          0 :         PyMem_Free(utcoff);
        1266                 :            :     }
        1267                 :            :
        1268         [ #  # ]:          0 :     if (dstoff != NULL) {
        1269                 :          0 :         PyMem_Free(dstoff);
        1270                 :            :     }
        1271                 :            :
        1272         [ #  # ]:          0 :     if (isdst != NULL) {
        1273                 :          0 :         PyMem_Free(isdst);
        1274                 :            :     }
        1275                 :            :
        1276         [ #  # ]:          0 :     if (trans_idx != NULL) {
        1277                 :          0 :         PyMem_Free(trans_idx);
        1278                 :            :     }
        1279                 :            :
        1280                 :          0 :     return rv;
        1281                 :            : }
        1282                 :            :
        1283                 :            : /* Function to calculate the local timestamp of a transition from the year. */
        1284                 :            : int64_t
        1285                 :          0 : calendarrule_year_to_timestamp(TransitionRuleType *base_self, int year)
        1286                 :            : {
        1287                 :          0 :     CalendarRule *self = (CalendarRule *)base_self;
        1288                 :            :
        1289                 :            :     // We want (year, month, day of month); we have year and month, but we
        1290                 :            :     // need to turn (week, day-of-week) into day-of-month
        1291                 :            :     //
        1292                 :            :     // Week 1 is the first week in which day `day` (where 0 = Sunday) appears.
        1293                 :            :     // Week 5 represents the last occurrence of day `day`, so we need to know
        1294                 :            :     // the first weekday of the month and the number of days in the month.
        1295                 :          0 :     int8_t first_day = (ymd_to_ord(year, self->month, 1) + 6) % 7;
        1296                 :          0 :     uint8_t days_in_month = DAYS_IN_MONTH[self->month];
        1297   [ #  #  #  # ]:          0 :     if (self->month == 2 && is_leap_year(year)) {
        1298                 :          0 :         days_in_month += 1;
        1299                 :            :     }
        1300                 :            :
        1301                 :            :     // This equation seems magical, so I'll break it down:
        1302                 :            :     // 1. calendar says 0 = Monday, POSIX says 0 = Sunday so we need first_day
        1303                 :            :     //    + 1 to get 1 = Monday -> 7 = Sunday, which is still equivalent
        1304                 :            :     //    because this math is mod 7
        1305                 :            :     // 2. Get first day - desired day mod 7 (adjusting by 7 for negative
        1306                 :            :     //    numbers so that -1 % 7 = 6).
        1307                 :            :     // 3. Add 1 because month days are a 1-based index.
        1308                 :          0 :     int8_t month_day = ((int8_t)(self->day) - (first_day + 1)) % 7;
        1309         [ #  # ]:          0 :     if (month_day < 0) {
        1310                 :          0 :         month_day += 7;
        1311                 :            :     }
        1312                 :          0 :     month_day += 1;
        1313                 :            :
        1314                 :            :     // Now use a 0-based index version of `week` to calculate the w-th
        1315                 :            :     // occurrence of `day`
        1316                 :          0 :     month_day += ((int8_t)(self->week) - 1) * 7;
        1317                 :            :
        1318                 :            :     // month_day will only be > days_in_month if w was 5, and `w` means "last
        1319                 :            :     // occurrence of `d`", so now we just check if we over-shot the end of the
        1320                 :            :     // month and if so knock off 1 week.
        1321         [ #  # ]:          0 :     if (month_day > days_in_month) {
        1322                 :          0 :         month_day -= 7;
        1323                 :            :     }
        1324                 :            :
        1325                 :          0 :     int64_t ordinal = ymd_to_ord(year, self->month, month_day) - EPOCHORDINAL;
        1326                 :          0 :     return ((ordinal * 86400) + (int64_t)(self->hour * 3600) +
        1327                 :          0 :             (int64_t)(self->minute * 60) + (int64_t)(self->second));
        1328                 :            : }
        1329                 :            :
        1330                 :            : /* Constructor for CalendarRule. */
        1331                 :            : int
        1332                 :          0 : calendarrule_new(uint8_t month, uint8_t week, uint8_t day, int8_t hour,
        1333                 :            :                  int8_t minute, int8_t second, CalendarRule *out)
        1334                 :            : {
        1335                 :            :     // These bounds come from the POSIX standard, which describes an Mm.n.d
        1336                 :            :     // rule as:
        1337                 :            :     //
        1338                 :            :     //   The d'th day (0 <= d <= 6) of week n of month m of the year (1 <= n <=
        1339                 :            :     //   5, 1 <= m <= 12, where week 5 means "the last d day in month m" which
        1340                 :            :     //   may occur in either the fourth or the fifth week). Week 1 is the first
        1341                 :            :     //   week in which the d'th day occurs. Day zero is Sunday.
        1342   [ #  #  #  # ]:          0 :     if (month <= 0 || month > 12) {
        1343                 :          0 :         PyErr_Format(PyExc_ValueError, "Month must be in (0, 12]");
        1344                 :          0 :         return -1;
        1345                 :            :     }
        1346                 :            :
        1347   [ #  #  #  # ]:          0 :     if (week <= 0 || week > 5) {
        1348                 :          0 :         PyErr_Format(PyExc_ValueError, "Week must be in (0, 5]");
        1349                 :          0 :         return -1;
        1350                 :            :     }
        1351                 :            :
        1352                 :            :     // If the 'day' parameter type is changed to a signed type,
        1353                 :            :     // "day < 0" check must be added.
        1354         [ #  # ]:          0 :     if (/* day < 0 || */ day > 6) {
        1355                 :          0 :         PyErr_Format(PyExc_ValueError, "Day must be in [0, 6]");
        1356                 :          0 :         return -1;
        1357                 :            :     }
        1358                 :            :
        1359                 :          0 :     TransitionRuleType base = {&calendarrule_year_to_timestamp};
        1360                 :            :
        1361                 :          0 :     CalendarRule new_offset = {
        1362                 :            :         .base = base,
        1363                 :            :         .month = month,
        1364                 :            :         .week = week,
        1365                 :            :         .day = day,
        1366                 :            :         .hour = hour,
        1367                 :            :         .minute = minute,
        1368                 :            :         .second = second,
        1369                 :            :     };
        1370                 :            :
        1371                 :          0 :     *out = new_offset;
        1372                 :          0 :     return 0;
        1373                 :            : }
        1374                 :            :
        1375                 :            : /* Function to calculate the local timestamp of a transition from the year.
        1376                 :            :  *
        1377                 :            :  * This translates the day of the year into a local timestamp — either a
        1378                 :            :  * 1-based Julian day, not including leap days, or the 0-based year-day,
        1379                 :            :  * including leap days.
        1380                 :            :  * */
        1381                 :            : int64_t
        1382                 :          0 : dayrule_year_to_timestamp(TransitionRuleType *base_self, int year)
        1383                 :            : {
        1384                 :            :     // The function signature requires a TransitionRuleType pointer, but this
        1385                 :            :     // function is only applicable to DayRule* objects.
        1386                 :          0 :     DayRule *self = (DayRule *)base_self;
        1387                 :            :
        1388                 :            :     // ymd_to_ord calculates the number of days since 0001-01-01, but we want
        1389                 :            :     // to know the number of days since 1970-01-01, so we must subtract off
        1390                 :            :     // the equivalent of ymd_to_ord(1970, 1, 1).
        1391                 :            :     //
        1392                 :            :     // We subtract off an additional 1 day to account for January 1st (we want
        1393                 :            :     // the number of full days *before* the date of the transition - partial
        1394                 :            :     // days are accounted for in the hour, minute and second portions.
        1395                 :          0 :     int64_t days_before_year = ymd_to_ord(year, 1, 1) - EPOCHORDINAL - 1;
        1396                 :            :
        1397                 :            :     // The Julian day specification skips over February 29th in leap years,
        1398                 :            :     // from the POSIX standard:
        1399                 :            :     //
        1400                 :            :     //   Leap days shall not be counted. That is, in all years-including leap
        1401                 :            :     //   years-February 28 is day 59 and March 1 is day 60. It is impossible to
        1402                 :            :     //   refer explicitly to the occasional February 29.
        1403                 :            :     //
        1404                 :            :     // This is actually more useful than you'd think — if you want a rule that
        1405                 :            :     // always transitions on a given calendar day (other than February 29th),
        1406                 :            :     // you would use a Julian day, e.g. J91 always refers to April 1st and J365
        1407                 :            :     // always refers to December 31st.
        1408                 :          0 :     unsigned int day = self->day;
        1409   [ #  #  #  #  :          0 :     if (self->julian && day >= 59 && is_leap_year(year)) {
                       #  # ]
        1410                 :          0 :         day += 1;
        1411                 :            :     }
        1412                 :            :
        1413                 :          0 :     return ((days_before_year + day) * 86400) + (self->hour * 3600) +
        1414                 :          0 :            (self->minute * 60) + self->second;
        1415                 :            : }
        1416                 :            :
        1417                 :            : /* Constructor for DayRule. */
        1418                 :            : static int
        1419                 :          0 : dayrule_new(uint8_t julian, unsigned int day, int8_t hour, int8_t minute,
        1420                 :            :             int8_t second, DayRule *out)
        1421                 :            : {
        1422                 :            :     // The POSIX standard specifies that Julian days must be in the range (1 <=
        1423                 :            :     // n <= 365) and that non-Julian (they call it "0-based Julian") days must
        1424                 :            :     // be in the range (0 <= n <= 365).
        1425   [ #  #  #  # ]:          0 :     if (day < julian || day > 365) {
        1426                 :          0 :         PyErr_Format(PyExc_ValueError, "day must be in [%u, 365], not: %u",
        1427                 :            :                      julian, day);
        1428                 :          0 :         return -1;
        1429                 :            :     }
        1430                 :            :
        1431                 :          0 :     TransitionRuleType base = {
        1432                 :            :         &dayrule_year_to_timestamp,
        1433                 :            :     };
        1434                 :            :
        1435                 :          0 :     DayRule tmp = {
        1436                 :            :         .base = base,
        1437                 :            :         .julian = julian,
        1438                 :            :         .day = day,
        1439                 :            :         .hour = hour,
        1440                 :            :         .minute = minute,
        1441                 :            :         .second = second,
        1442                 :            :     };
        1443                 :            :
        1444                 :          0 :     *out = tmp;
        1445                 :            :
        1446                 :          0 :     return 0;
        1447                 :            : }
        1448                 :            :
        1449                 :            : /* Calculate the start and end rules for a _tzrule in the given year. */
        1450                 :            : static void
        1451                 :          0 : tzrule_transitions(_tzrule *rule, int year, int64_t *start, int64_t *end)
        1452                 :            : {
        1453                 :            :     assert(rule->start != NULL);
        1454                 :            :     assert(rule->end != NULL);
        1455                 :          0 :     *start = rule->start->year_to_timestamp(rule->start, year);
        1456                 :          0 :     *end = rule->end->year_to_timestamp(rule->end, year);
        1457                 :          0 : }
        1458                 :            :
        1459                 :            : /* Calculate the _ttinfo that applies at a given local time from a _tzrule.
        1460                 :            :  *
        1461                 :            :  * This takes a local timestamp and fold for disambiguation purposes; the year
        1462                 :            :  * could technically be calculated from the timestamp, but given that the
        1463                 :            :  * callers of this function already have the year information accessible from
        1464                 :            :  * the datetime struct, it is taken as an additional parameter to reduce
        1465                 :            :  * unnecessary calculation.
        1466                 :            :  * */
        1467                 :            : static _ttinfo *
        1468                 :          0 : find_tzrule_ttinfo(_tzrule *rule, int64_t ts, unsigned char fold, int year)
        1469                 :            : {
        1470         [ #  # ]:          0 :     if (rule->std_only) {
        1471                 :          0 :         return &(rule->std);
        1472                 :            :     }
        1473                 :            :
        1474                 :            :     int64_t start, end;
        1475                 :            :     uint8_t isdst;
        1476                 :            :
        1477                 :          0 :     tzrule_transitions(rule, year, &start, &end);
        1478                 :            :
        1479                 :            :     // With fold = 0, the period (denominated in local time) with the smaller
        1480                 :            :     // offset starts at the end of the gap and ends at the end of the fold;
        1481                 :            :     // with fold = 1, it runs from the start of the gap to the beginning of the
        1482                 :            :     // fold.
        1483                 :            :     //
        1484                 :            :     // So in order to determine the DST boundaries we need to know both the
        1485                 :            :     // fold and whether DST is positive or negative (rare), and it turns out
        1486                 :            :     // that this boils down to fold XOR is_positive.
        1487         [ #  # ]:          0 :     if (fold == (rule->dst_diff >= 0)) {
        1488                 :          0 :         end -= rule->dst_diff;
        1489                 :            :     }
        1490                 :            :     else {
        1491                 :          0 :         start += rule->dst_diff;
        1492                 :            :     }
        1493                 :            :
        1494         [ #  # ]:          0 :     if (start < end) {
        1495   [ #  #  #  # ]:          0 :         isdst = (ts >= start) && (ts < end);
        1496                 :            :     }
        1497                 :            :     else {
        1498   [ #  #  #  # ]:          0 :         isdst = (ts < end) || (ts >= start);
        1499                 :            :     }
        1500                 :            :
        1501         [ #  # ]:          0 :     if (isdst) {
        1502                 :          0 :         return &(rule->dst);
        1503                 :            :     }
        1504                 :            :     else {
        1505                 :          0 :         return &(rule->std);
        1506                 :            :     }
        1507                 :            : }
        1508                 :            :
        1509                 :            : /* Calculate the ttinfo and fold that applies for a _tzrule at an epoch time.
        1510                 :            :  *
        1511                 :            :  * This function can determine the _ttinfo that applies at a given epoch time,
        1512                 :            :  * (analogous to trans_list_utc), and whether or not the datetime is in a fold.
        1513                 :            :  * This is to be used in the .fromutc() function.
        1514                 :            :  *
        1515                 :            :  * The year is technically a redundant parameter, because it can be calculated
        1516                 :            :  * from the timestamp, but all callers of this function should have the year
        1517                 :            :  * in the datetime struct anyway, so taking it as a parameter saves unnecessary
        1518                 :            :  * calculation.
        1519                 :            :  **/
        1520                 :            : static _ttinfo *
        1521                 :          0 : find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year,
        1522                 :            :                            unsigned char *fold)
        1523                 :            : {
        1524         [ #  # ]:          0 :     if (rule->std_only) {
        1525                 :          0 :         *fold = 0;
        1526                 :          0 :         return &(rule->std);
        1527                 :            :     }
        1528                 :            :
        1529                 :            :     int64_t start, end;
        1530                 :            :     uint8_t isdst;
        1531                 :          0 :     tzrule_transitions(rule, year, &start, &end);
        1532                 :          0 :     start -= rule->std.utcoff_seconds;
        1533                 :          0 :     end -= rule->dst.utcoff_seconds;
        1534                 :            :
        1535         [ #  # ]:          0 :     if (start < end) {
        1536   [ #  #  #  # ]:          0 :         isdst = (ts >= start) && (ts < end);
        1537                 :            :     }
        1538                 :            :     else {
        1539   [ #  #  #  # ]:          0 :         isdst = (ts < end) || (ts >= start);
        1540                 :            :     }
        1541                 :            :
        1542                 :            :     // For positive DST, the ambiguous period is one dst_diff after the end of
        1543                 :            :     // DST; for negative DST, the ambiguous period is one dst_diff before the
        1544                 :            :     // start of DST.
        1545                 :            :     int64_t ambig_start, ambig_end;
        1546         [ #  # ]:          0 :     if (rule->dst_diff > 0) {
        1547                 :          0 :         ambig_start = end;
        1548                 :          0 :         ambig_end = end + rule->dst_diff;
        1549                 :            :     }
        1550                 :            :     else {
        1551                 :          0 :         ambig_start = start;
        1552                 :          0 :         ambig_end = start - rule->dst_diff;
        1553                 :            :     }
        1554                 :            :
        1555   [ #  #  #  # ]:          0 :     *fold = (ts >= ambig_start) && (ts < ambig_end);
        1556                 :            :
        1557         [ #  # ]:          0 :     if (isdst) {
        1558                 :          0 :         return &(rule->dst);
        1559                 :            :     }
        1560                 :            :     else {
        1561                 :          0 :         return &(rule->std);
        1562                 :            :     }
        1563                 :            : }
        1564                 :            :
        1565                 :            : /* Parse a TZ string in the format specified by the POSIX standard:
        1566                 :            :  *
        1567                 :            :  *  std offset[dst[offset],start[/time],end[/time]]
        1568                 :            :  *
        1569                 :            :  *  std and dst must be 3 or more characters long and must not contain a
        1570                 :            :  *  leading colon, embedded digits, commas, nor a plus or minus signs; The
        1571                 :            :  *  spaces between "std" and "offset" are only for display and are not actually
        1572                 :            :  *  present in the string.
        1573                 :            :  *
        1574                 :            :  *  The format of the offset is ``[+|-]hh[:mm[:ss]]``
        1575                 :            :  *
        1576                 :            :  * See the POSIX.1 spec: IEE Std 1003.1-2018 §8.3:
        1577                 :            :  *
        1578                 :            :  * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
        1579                 :            :  */
        1580                 :            : static int
        1581                 :          0 : parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out)
        1582                 :            : {
        1583                 :          0 :     PyObject *std_abbr = NULL;
        1584                 :          0 :     PyObject *dst_abbr = NULL;
        1585                 :          0 :     TransitionRuleType *start = NULL;
        1586                 :          0 :     TransitionRuleType *end = NULL;
        1587                 :            :     // Initialize offsets to invalid value (> 24 hours)
        1588                 :          0 :     long std_offset = 1 << 20;
        1589                 :          0 :     long dst_offset = 1 << 20;
        1590                 :            :
        1591                 :          0 :     const char *tz_str = PyBytes_AsString(tz_str_obj);
        1592         [ #  # ]:          0 :     if (tz_str == NULL) {
        1593                 :          0 :         return -1;
        1594                 :            :     }
        1595                 :          0 :     const char *p = tz_str;
        1596                 :            :
        1597                 :            :     // Read the `std` abbreviation, which must be at least 3 characters long.
        1598                 :          0 :     Py_ssize_t num_chars = parse_abbr(p, &std_abbr);
        1599         [ #  # ]:          0 :     if (num_chars < 1) {
        1600                 :          0 :         PyErr_Format(PyExc_ValueError, "Invalid STD format in %R", tz_str_obj);
        1601                 :          0 :         goto error;
        1602                 :            :     }
        1603                 :            :
        1604                 :          0 :     p += num_chars;
        1605                 :            :
        1606                 :            :     // Now read the STD offset, which is required
        1607                 :          0 :     num_chars = parse_tz_delta(p, &std_offset);
        1608         [ #  # ]:          0 :     if (num_chars < 0) {
        1609                 :          0 :         PyErr_Format(PyExc_ValueError, "Invalid STD offset in %R", tz_str_obj);
        1610                 :          0 :         goto error;
        1611                 :            :     }
        1612                 :          0 :     p += num_chars;
        1613                 :            :
        1614                 :            :     // If the string ends here, there is no DST, otherwise we must parse the
        1615                 :            :     // DST abbreviation and start and end dates and times.
        1616         [ #  # ]:          0 :     if (*p == '\0') {
        1617                 :          0 :         goto complete;
        1618                 :            :     }
        1619                 :            :
        1620                 :          0 :     num_chars = parse_abbr(p, &dst_abbr);
        1621         [ #  # ]:          0 :     if (num_chars < 1) {
        1622                 :          0 :         PyErr_Format(PyExc_ValueError, "Invalid DST format in %R", tz_str_obj);
        1623                 :          0 :         goto error;
        1624                 :            :     }
        1625                 :          0 :     p += num_chars;
        1626                 :            :
        1627         [ #  # ]:          0 :     if (*p == ',') {
        1628                 :            :         // From the POSIX standard:
        1629                 :            :         //
        1630                 :            :         // If no offset follows dst, the alternative time is assumed to be one
        1631                 :            :         // hour ahead of standard time.
        1632                 :          0 :         dst_offset = std_offset + 3600;
        1633                 :            :     }
        1634                 :            :     else {
        1635                 :          0 :         num_chars = parse_tz_delta(p, &dst_offset);
        1636         [ #  # ]:          0 :         if (num_chars < 0) {
        1637                 :          0 :             PyErr_Format(PyExc_ValueError, "Invalid DST offset in %R",
        1638                 :            :                          tz_str_obj);
        1639                 :          0 :             goto error;
        1640                 :            :         }
        1641                 :            :
        1642                 :          0 :         p += num_chars;
        1643                 :            :     }
        1644                 :            :
        1645                 :          0 :     TransitionRuleType **transitions[2] = {&start, &end};
        1646         [ #  # ]:          0 :     for (size_t i = 0; i < 2; ++i) {
        1647         [ #  # ]:          0 :         if (*p != ',') {
        1648                 :          0 :             PyErr_Format(PyExc_ValueError,
        1649                 :            :                          "Missing transition rules in TZ string: %R",
        1650                 :            :                          tz_str_obj);
        1651                 :          0 :             goto error;
        1652                 :            :         }
        1653                 :          0 :         p++;
        1654                 :            :
        1655                 :          0 :         num_chars = parse_transition_rule(p, transitions[i]);
        1656         [ #  # ]:          0 :         if (num_chars < 0) {
        1657                 :          0 :             PyErr_Format(PyExc_ValueError,
        1658                 :            :                          "Malformed transition rule in TZ string: %R",
        1659                 :            :                          tz_str_obj);
        1660                 :          0 :             goto error;
        1661                 :            :         }
        1662                 :          0 :         p += num_chars;
        1663                 :            :     }
        1664                 :            :
        1665         [ #  # ]:          0 :     if (*p != '\0') {
        1666                 :          0 :         PyErr_Format(PyExc_ValueError,
        1667                 :            :                      "Extraneous characters at end of TZ string: %R",
        1668                 :            :                      tz_str_obj);
        1669                 :          0 :         goto error;
        1670                 :            :     }
        1671                 :            :
        1672                 :          0 : complete:
        1673                 :          0 :     build_tzrule(state, std_abbr, dst_abbr, std_offset, dst_offset,
        1674                 :            :                  start, end, out);
        1675                 :          0 :     Py_DECREF(std_abbr);
        1676                 :          0 :     Py_XDECREF(dst_abbr);
        1677                 :            :
        1678                 :          0 :     return 0;
        1679                 :          0 : error:
        1680                 :          0 :     Py_XDECREF(std_abbr);
        1681   [ #  #  #  # ]:          0 :     if (dst_abbr != NULL && dst_abbr != Py_None) {
        1682                 :          0 :         Py_DECREF(dst_abbr);
        1683                 :            :     }
        1684                 :            :
        1685         [ #  # ]:          0 :     if (start != NULL) {
        1686                 :          0 :         PyMem_Free(start);
        1687                 :            :     }
        1688                 :            :
        1689         [ #  # ]:          0 :     if (end != NULL) {
        1690                 :          0 :         PyMem_Free(end);
        1691                 :            :     }
        1692                 :            :
        1693                 :          0 :     return -1;
        1694                 :            : }
        1695                 :            :
        1696                 :            : static int
        1697                 :          0 : parse_uint(const char *const p, uint8_t *value)
        1698                 :            : {
        1699         [ #  # ]:          0 :     if (!isdigit(*p)) {
        1700                 :          0 :         return -1;
        1701                 :            :     }
        1702                 :            :
        1703                 :          0 :     *value = (*p) - '0';
        1704                 :          0 :     return 0;
        1705                 :            : }
        1706                 :            :
        1707                 :            : /* Parse the STD and DST abbreviations from a TZ string. */
        1708                 :            : static Py_ssize_t
        1709                 :          0 : parse_abbr(const char *const p, PyObject **abbr)
        1710                 :            : {
        1711                 :          0 :     const char *ptr = p;
        1712                 :          0 :     char buff = *ptr;
        1713                 :            :     const char *str_start;
        1714                 :            :     const char *str_end;
        1715                 :            :
        1716         [ #  # ]:          0 :     if (*ptr == '<') {
        1717                 :          0 :         ptr++;
        1718                 :          0 :         str_start = ptr;
        1719         [ #  # ]:          0 :         while ((buff = *ptr) != '>') {
        1720                 :            :             // From the POSIX standard:
        1721                 :            :             //
        1722                 :            :             //   In the quoted form, the first character shall be the less-than
        1723                 :            :             //   ( '<' ) character and the last character shall be the
        1724                 :            :             //   greater-than ( '>' ) character. All characters between these
        1725                 :            :             //   quoting characters shall be alphanumeric characters from the
        1726                 :            :             //   portable character set in the current locale, the plus-sign (
        1727                 :            :             //   '+' ) character, or the minus-sign ( '-' ) character. The std
        1728                 :            :             //   and dst fields in this case shall not include the quoting
        1729                 :            :             //   characters.
        1730   [ #  #  #  #  :          0 :             if (!isalpha(buff) && !isdigit(buff) && buff != '+' &&
                 #  #  #  # ]
        1731                 :            :                 buff != '-') {
        1732                 :          0 :                 return -1;
        1733                 :            :             }
        1734                 :          0 :             ptr++;
        1735                 :            :         }
        1736                 :          0 :         str_end = ptr;
        1737                 :          0 :         ptr++;
        1738                 :            :     }
        1739                 :            :     else {
        1740                 :          0 :         str_start = p;
        1741                 :            :         // From the POSIX standard:
        1742                 :            :         //
        1743                 :            :         //   In the unquoted form, all characters in these fields shall be
        1744                 :            :         //   alphabetic characters from the portable character set in the
        1745                 :            :         //   current locale.
        1746         [ #  # ]:          0 :         while (isalpha(*ptr)) {
        1747                 :          0 :             ptr++;
        1748                 :            :         }
        1749                 :          0 :         str_end = ptr;
        1750                 :            :     }
        1751                 :            :
        1752                 :          0 :     *abbr = PyUnicode_FromStringAndSize(str_start, str_end - str_start);
        1753         [ #  # ]:          0 :     if (*abbr == NULL) {
        1754                 :          0 :         return -1;
        1755                 :            :     }
        1756                 :            :
        1757                 :          0 :     return ptr - p;
        1758                 :            : }
        1759                 :            :
        1760                 :            : /* Parse a UTC offset from a TZ str. */
        1761                 :            : static Py_ssize_t
        1762                 :          0 : parse_tz_delta(const char *const p, long *total_seconds)
        1763                 :            : {
        1764                 :            :     // From the POSIX spec:
        1765                 :            :     //
        1766                 :            :     //   Indicates the value added to the local time to arrive at Coordinated
        1767                 :            :     //   Universal Time. The offset has the form:
        1768                 :            :     //
        1769                 :            :     //   hh[:mm[:ss]]
        1770                 :            :     //
        1771                 :            :     //   One or more digits may be used; the value is always interpreted as a
        1772                 :            :     //   decimal number.
        1773                 :            :     //
        1774                 :            :     // The POSIX spec says that the values for `hour` must be between 0 and 24
        1775                 :            :     // hours, but RFC 8536 §3.3.1 specifies that the hours part of the
        1776                 :            :     // transition times may be signed and range from -167 to 167.
        1777                 :          0 :     long sign = -1;
        1778                 :          0 :     long hours = 0;
        1779                 :          0 :     long minutes = 0;
        1780                 :          0 :     long seconds = 0;
        1781                 :            :
        1782                 :          0 :     const char *ptr = p;
        1783                 :          0 :     char buff = *ptr;
        1784   [ #  #  #  # ]:          0 :     if (buff == '-' || buff == '+') {
        1785                 :            :         // Negative numbers correspond to *positive* offsets, from the spec:
        1786                 :            :         //
        1787                 :            :         //   If preceded by a '-', the timezone shall be east of the Prime
        1788                 :            :         //   Meridian; otherwise, it shall be west (which may be indicated by
        1789                 :            :         //   an optional preceding '+' ).
        1790         [ #  # ]:          0 :         if (buff == '-') {
        1791                 :          0 :             sign = 1;
        1792                 :            :         }
        1793                 :            :
        1794                 :          0 :         ptr++;
        1795                 :            :     }
        1796                 :            :
        1797                 :            :     // The hour can be 1 or 2 numeric characters
        1798         [ #  # ]:          0 :     for (size_t i = 0; i < 2; ++i) {
        1799                 :          0 :         buff = *ptr;
        1800         [ #  # ]:          0 :         if (!isdigit(buff)) {
        1801         [ #  # ]:          0 :             if (i == 0) {
        1802                 :          0 :                 return -1;
        1803                 :            :             }
        1804                 :            :             else {
        1805                 :          0 :                 break;
        1806                 :            :             }
        1807                 :            :         }
        1808                 :            :
        1809                 :          0 :         hours *= 10;
        1810                 :          0 :         hours += buff - '0';
        1811                 :          0 :         ptr++;
        1812                 :            :     }
        1813                 :            :
        1814   [ #  #  #  # ]:          0 :     if (hours > 24 || hours < 0) {
        1815                 :          0 :         return -1;
        1816                 :            :     }
        1817                 :            :
        1818                 :            :     // Minutes and seconds always of the format ":dd"
        1819                 :          0 :     long *outputs[2] = {&minutes, &seconds};
        1820         [ #  # ]:          0 :     for (size_t i = 0; i < 2; ++i) {
        1821         [ #  # ]:          0 :         if (*ptr != ':') {
        1822                 :          0 :             goto complete;
        1823                 :            :         }
        1824                 :          0 :         ptr++;
        1825                 :            :
        1826         [ #  # ]:          0 :         for (size_t j = 0; j < 2; ++j) {
        1827                 :          0 :             buff = *ptr;
        1828         [ #  # ]:          0 :             if (!isdigit(buff)) {
        1829                 :          0 :                 return -1;
        1830                 :            :             }
        1831                 :          0 :             *(outputs[i]) *= 10;
        1832                 :          0 :             *(outputs[i]) += buff - '0';
        1833                 :          0 :             ptr++;
        1834                 :            :         }
        1835                 :            :     }
        1836                 :            :
        1837                 :          0 : complete:
        1838                 :          0 :     *total_seconds = sign * ((hours * 3600) + (minutes * 60) + seconds);
        1839                 :            :
        1840                 :          0 :     return ptr - p;
        1841                 :            : }
        1842                 :            :
        1843                 :            : /* Parse the date portion of a transition rule. */
        1844                 :            : static Py_ssize_t
        1845                 :          0 : parse_transition_rule(const char *const p, TransitionRuleType **out)
        1846                 :            : {
        1847                 :            :     // The full transition rule indicates when to change back and forth between
        1848                 :            :     // STD and DST, and has the form:
        1849                 :            :     //
        1850                 :            :     //   date[/time],date[/time]
        1851                 :            :     //
        1852                 :            :     // This function parses an individual date[/time] section, and returns
        1853                 :            :     // the number of characters that contributed to the transition rule. This
        1854                 :            :     // does not include the ',' at the end of the first rule.
        1855                 :            :     //
        1856                 :            :     // The POSIX spec states that if *time* is not given, the default is 02:00.
        1857                 :          0 :     const char *ptr = p;
        1858                 :          0 :     int8_t hour = 2;
        1859                 :          0 :     int8_t minute = 0;
        1860                 :          0 :     int8_t second = 0;
        1861                 :            :
        1862                 :            :     // Rules come in one of three flavors:
        1863                 :            :     //
        1864                 :            :     //   1. Jn: Julian day n, with no leap days.
        1865                 :            :     //   2. n: Day of year (0-based, with leap days)
        1866                 :            :     //   3. Mm.n.d: Specifying by month, week and day-of-week.
        1867                 :            :
        1868         [ #  # ]:          0 :     if (*ptr == 'M') {
        1869                 :            :         uint8_t month, week, day;
        1870                 :          0 :         ptr++;
        1871         [ #  # ]:          0 :         if (parse_uint(ptr, &month)) {
        1872                 :          0 :             return -1;
        1873                 :            :         }
        1874                 :          0 :         ptr++;
        1875         [ #  # ]:          0 :         if (*ptr != '.') {
        1876                 :            :             uint8_t tmp;
        1877         [ #  # ]:          0 :             if (parse_uint(ptr, &tmp)) {
        1878                 :          0 :                 return -1;
        1879                 :            :             }
        1880                 :            :
        1881                 :          0 :             month *= 10;
        1882                 :          0 :             month += tmp;
        1883                 :          0 :             ptr++;
        1884                 :            :         }
        1885                 :            :
        1886                 :          0 :         uint8_t *values[2] = {&week, &day};
        1887         [ #  # ]:          0 :         for (size_t i = 0; i < 2; ++i) {
        1888         [ #  # ]:          0 :             if (*ptr != '.') {
        1889                 :          0 :                 return -1;
        1890                 :            :             }
        1891                 :          0 :             ptr++;
        1892                 :            :
        1893         [ #  # ]:          0 :             if (parse_uint(ptr, values[i])) {
        1894                 :          0 :                 return -1;
        1895                 :            :             }
        1896                 :          0 :             ptr++;
        1897                 :            :         }
        1898                 :            :
        1899         [ #  # ]:          0 :         if (*ptr == '/') {
        1900                 :          0 :             ptr++;
        1901                 :            :             Py_ssize_t num_chars =
        1902                 :          0 :                 parse_transition_time(ptr, &hour, &minute, &second);
        1903         [ #  # ]:          0 :             if (num_chars < 0) {
        1904                 :          0 :                 return -1;
        1905                 :            :             }
        1906                 :          0 :             ptr += num_chars;
        1907                 :            :         }
        1908                 :            :
        1909                 :          0 :         CalendarRule *rv = PyMem_Calloc(1, sizeof(CalendarRule));
        1910         [ #  # ]:          0 :         if (rv == NULL) {
        1911                 :          0 :             return -1;
        1912                 :            :         }
        1913                 :            :
        1914         [ #  # ]:          0 :         if (calendarrule_new(month, week, day, hour, minute, second, rv)) {
        1915                 :          0 :             PyMem_Free(rv);
        1916                 :          0 :             return -1;
        1917                 :            :         }
        1918                 :            :
        1919                 :          0 :         *out = (TransitionRuleType *)rv;
        1920                 :            :     }
        1921                 :            :     else {
        1922                 :          0 :         uint8_t julian = 0;
        1923                 :          0 :         unsigned int day = 0;
        1924         [ #  # ]:          0 :         if (*ptr == 'J') {
        1925                 :          0 :             julian = 1;
        1926                 :          0 :             ptr++;
        1927                 :            :         }
        1928                 :            :
        1929         [ #  # ]:          0 :         for (size_t i = 0; i < 3; ++i) {
        1930         [ #  # ]:          0 :             if (!isdigit(*ptr)) {
        1931         [ #  # ]:          0 :                 if (i == 0) {
        1932                 :          0 :                     return -1;
        1933                 :            :                 }
        1934                 :          0 :                 break;
        1935                 :            :             }
        1936                 :          0 :             day *= 10;
        1937                 :          0 :             day += (*ptr) - '0';
        1938                 :          0 :             ptr++;
        1939                 :            :         }
        1940                 :            :
        1941         [ #  # ]:          0 :         if (*ptr == '/') {
        1942                 :          0 :             ptr++;
        1943                 :            :             Py_ssize_t num_chars =
        1944                 :          0 :                 parse_transition_time(ptr, &hour, &minute, &second);
        1945         [ #  # ]:          0 :             if (num_chars < 0) {
        1946                 :          0 :                 return -1;
        1947                 :            :             }
        1948                 :          0 :             ptr += num_chars;
        1949                 :            :         }
        1950                 :            :
        1951                 :          0 :         DayRule *rv = PyMem_Calloc(1, sizeof(DayRule));
        1952         [ #  # ]:          0 :         if (rv == NULL) {
        1953                 :          0 :             return -1;
        1954                 :            :         }
        1955                 :            :
        1956         [ #  # ]:          0 :         if (dayrule_new(julian, day, hour, minute, second, rv)) {
        1957                 :          0 :             PyMem_Free(rv);
        1958                 :          0 :             return -1;
        1959                 :            :         }
        1960                 :          0 :         *out = (TransitionRuleType *)rv;
        1961                 :            :     }
        1962                 :            :
        1963                 :          0 :     return ptr - p;
        1964                 :            : }
        1965                 :            :
        1966                 :            : /* Parse the time portion of a transition rule (e.g. following an /) */
        1967                 :            : static Py_ssize_t
        1968                 :          0 : parse_transition_time(const char *const p, int8_t *hour, int8_t *minute,
        1969                 :            :                       int8_t *second)
        1970                 :            : {
        1971                 :            :     // From the spec:
        1972                 :            :     //
        1973                 :            :     //   The time has the same format as offset except that no leading sign
        1974                 :            :     //   ( '-' or '+' ) is allowed.
        1975                 :            :     //
        1976                 :            :     // The format for the offset is:
        1977                 :            :     //
        1978                 :            :     //   h[h][:mm[:ss]]
        1979                 :            :     //
        1980                 :            :     // RFC 8536 also allows transition times to be signed and to range from
        1981                 :            :     // -167 to +167, but the current version only supports [0, 99].
        1982                 :            :     //
        1983                 :            :     // TODO: Support the full range of transition hours.
        1984                 :          0 :     int8_t *components[3] = {hour, minute, second};
        1985                 :          0 :     const char *ptr = p;
        1986                 :          0 :     int8_t sign = 1;
        1987                 :            :
        1988   [ #  #  #  # ]:          0 :     if (*ptr == '-' || *ptr == '+') {
        1989         [ #  # ]:          0 :         if (*ptr == '-') {
        1990                 :          0 :             sign = -1;
        1991                 :            :         }
        1992                 :          0 :         ptr++;
        1993                 :            :     }
        1994                 :            :
        1995         [ #  # ]:          0 :     for (size_t i = 0; i < 3; ++i) {
        1996         [ #  # ]:          0 :         if (i > 0) {
        1997         [ #  # ]:          0 :             if (*ptr != ':') {
        1998                 :          0 :                 break;
        1999                 :            :             }
        2000                 :          0 :             ptr++;
        2001                 :            :         }
        2002                 :            :
        2003                 :          0 :         uint8_t buff = 0;
        2004         [ #  # ]:          0 :         for (size_t j = 0; j < 2; j++) {
        2005         [ #  # ]:          0 :             if (!isdigit(*ptr)) {
        2006   [ #  #  #  # ]:          0 :                 if (i == 0 && j > 0) {
        2007                 :          0 :                     break;
        2008                 :            :                 }
        2009                 :          0 :                 return -1;
        2010                 :            :             }
        2011                 :            :
        2012                 :          0 :             buff *= 10;
        2013                 :          0 :             buff += (*ptr) - '0';
        2014                 :          0 :             ptr++;
        2015                 :            :         }
        2016                 :            :
        2017                 :          0 :         *(components[i]) = sign * buff;
        2018                 :            :     }
        2019                 :            :
        2020                 :          0 :     return ptr - p;
        2021                 :            : }
        2022                 :            :
        2023                 :            : /* Constructor for a _tzrule.
        2024                 :            :  *
        2025                 :            :  * If `dst_abbr` is NULL, this will construct an "STD-only" _tzrule, in which
        2026                 :            :  * case `dst_offset` will be ignored and `start` and `end` are expected to be
        2027                 :            :  * NULL as well.
        2028                 :            :  *
        2029                 :            :  * Returns 0 on success.
        2030                 :            :  */
        2031                 :            : static int
        2032                 :          0 : build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr,
        2033                 :            :              long std_offset, long dst_offset, TransitionRuleType *start,
        2034                 :            :              TransitionRuleType *end, _tzrule *out)
        2035                 :            : {
        2036                 :          0 :     _tzrule rv = {{0}};
        2037                 :            :
        2038                 :          0 :     rv.start = start;
        2039                 :          0 :     rv.end = end;
        2040                 :            :
        2041         [ #  # ]:          0 :     if (build_ttinfo(state, std_offset, 0, std_abbr, &rv.std)) {
        2042                 :          0 :         goto error;
        2043                 :            :     }
        2044                 :            :
        2045         [ #  # ]:          0 :     if (dst_abbr != NULL) {
        2046                 :          0 :         rv.dst_diff = dst_offset - std_offset;
        2047         [ #  # ]:          0 :         if (build_ttinfo(state, dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) {
        2048                 :          0 :             goto error;
        2049                 :            :         }
        2050                 :            :     }
        2051                 :            :     else {
        2052                 :          0 :         rv.std_only = 1;
        2053                 :            :     }
        2054                 :            :
        2055                 :          0 :     *out = rv;
        2056                 :            :
        2057                 :          0 :     return 0;
        2058                 :          0 : error:
        2059                 :          0 :     xdecref_ttinfo(&rv.std);
        2060                 :          0 :     xdecref_ttinfo(&rv.dst);
        2061                 :          0 :     return -1;
        2062                 :            : }
        2063                 :            :
        2064                 :            : /* Destructor for _tzrule. */
        2065                 :            : static void
        2066                 :          0 : free_tzrule(_tzrule *tzrule)
        2067                 :            : {
        2068                 :          0 :     xdecref_ttinfo(&(tzrule->std));
        2069         [ #  # ]:          0 :     if (!tzrule->std_only) {
        2070                 :          0 :         xdecref_ttinfo(&(tzrule->dst));
        2071                 :            :     }
        2072                 :            :
        2073         [ #  # ]:          0 :     if (tzrule->start != NULL) {
        2074                 :          0 :         PyMem_Free(tzrule->start);
        2075                 :            :     }
        2076                 :            :
        2077         [ #  # ]:          0 :     if (tzrule->end != NULL) {
        2078                 :          0 :         PyMem_Free(tzrule->end);
        2079                 :            :     }
        2080                 :          0 : }
        2081                 :            :
        2082                 :            : /* Calculate DST offsets from transitions and UTC offsets
        2083                 :            :  *
        2084                 :            :  * This is necessary because each C `ttinfo` only contains the UTC offset,
        2085                 :            :  * time zone abbreviation and an isdst boolean - it does not include the
        2086                 :            :  * amount of the DST offset, but we need the amount for the dst() function.
        2087                 :            :  *
        2088                 :            :  * Thus function uses heuristics to infer what the offset should be, so it
        2089                 :            :  * is not guaranteed that this will work for all zones. If we cannot assign
        2090                 :            :  * a value for a given DST offset, we'll assume it's 1H rather than 0H, so
        2091                 :            :  * bool(dt.dst()) will always match ttinfo.isdst.
        2092                 :            :  */
        2093                 :            : static void
        2094                 :          0 : utcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs,
        2095                 :            :                  unsigned char *isdsts, size_t num_transitions,
        2096                 :            :                  size_t num_ttinfos)
        2097                 :            : {
        2098                 :          0 :     size_t dst_count = 0;
        2099                 :          0 :     size_t dst_found = 0;
        2100         [ #  # ]:          0 :     for (size_t i = 0; i < num_ttinfos; ++i) {
        2101                 :          0 :         dst_count++;
        2102                 :            :     }
        2103                 :            :
        2104         [ #  # ]:          0 :     for (size_t i = 1; i < num_transitions; ++i) {
        2105         [ #  # ]:          0 :         if (dst_count == dst_found) {
        2106                 :          0 :             break;
        2107                 :            :         }
        2108                 :            :
        2109                 :          0 :         size_t idx = trans_idx[i];
        2110                 :          0 :         size_t comp_idx = trans_idx[i - 1];
        2111                 :            :
        2112                 :            :         // Only look at DST offsets that have nto been assigned already
        2113   [ #  #  #  # ]:          0 :         if (!isdsts[idx] || dstoffs[idx] != 0) {
        2114                 :          0 :             continue;
        2115                 :            :         }
        2116                 :            :
        2117                 :          0 :         long dstoff = 0;
        2118                 :          0 :         long utcoff = utcoffs[idx];
        2119                 :            :
        2120         [ #  # ]:          0 :         if (!isdsts[comp_idx]) {
        2121                 :          0 :             dstoff = utcoff - utcoffs[comp_idx];
        2122                 :            :         }
        2123                 :            :
        2124   [ #  #  #  # ]:          0 :         if (!dstoff && idx < (num_ttinfos - 1)) {
        2125                 :          0 :             comp_idx = trans_idx[i + 1];
        2126                 :            :
        2127                 :            :             // If the following transition is also DST and we couldn't find
        2128                 :            :             // the DST offset by this point, we're going to have to skip it
        2129                 :            :             // and hope this transition gets assigned later
        2130         [ #  # ]:          0 :             if (isdsts[comp_idx]) {
        2131                 :          0 :                 continue;
        2132                 :            :             }
        2133                 :            :
        2134                 :          0 :             dstoff = utcoff - utcoffs[comp_idx];
        2135                 :            :         }
        2136                 :            :
        2137         [ #  # ]:          0 :         if (dstoff) {
        2138                 :          0 :             dst_found++;
        2139                 :          0 :             dstoffs[idx] = dstoff;
        2140                 :            :         }
        2141                 :            :     }
        2142                 :            :
        2143         [ #  # ]:          0 :     if (dst_found < dst_count) {
        2144                 :            :         // If there are time zones we didn't find a value for, we'll end up
        2145                 :            :         // with dstoff = 0 for something where isdst=1. This is obviously
        2146                 :            :         // wrong — one hour will be a much better guess than 0.
        2147         [ #  # ]:          0 :         for (size_t idx = 0; idx < num_ttinfos; ++idx) {
        2148   [ #  #  #  # ]:          0 :             if (isdsts[idx] && !dstoffs[idx]) {
        2149                 :          0 :                 dstoffs[idx] = 3600;
        2150                 :            :             }
        2151                 :            :         }
        2152                 :            :     }
        2153                 :          0 : }
        2154                 :            :
        2155                 :            : #define _swap(x, y, buffer) \
        2156                 :            :     buffer = x;             \
        2157                 :            :     x = y;                  \
        2158                 :            :     y = buffer;
        2159                 :            :
        2160                 :            : /* Calculate transitions in local time from UTC time and offsets.
        2161                 :            :  *
        2162                 :            :  * We want to know when each transition occurs, denominated in the number of
        2163                 :            :  * nominal wall-time seconds between 1970-01-01T00:00:00 and the transition in
        2164                 :            :  * *local time* (note: this is *not* equivalent to the output of
        2165                 :            :  * datetime.timestamp, which is the total number of seconds actual elapsed
        2166                 :            :  * since 1970-01-01T00:00:00Z in UTC).
        2167                 :            :  *
        2168                 :            :  * This is an ambiguous question because "local time" can be ambiguous — but it
        2169                 :            :  * is disambiguated by the `fold` parameter, so we allocate two arrays:
        2170                 :            :  *
        2171                 :            :  *  trans_local[0]: The wall-time transitions for fold=0
        2172                 :            :  *  trans_local[1]: The wall-time transitions for fold=1
        2173                 :            :  *
        2174                 :            :  * This returns 0 on success and a negative number of failure. The trans_local
        2175                 :            :  * arrays must be freed if they are not NULL.
        2176                 :            :  */
        2177                 :            : static int
        2178                 :          0 : ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff,
        2179                 :            :             int64_t *trans_local[2], size_t num_ttinfos,
        2180                 :            :             size_t num_transitions)
        2181                 :            : {
        2182         [ #  # ]:          0 :     if (num_transitions == 0) {
        2183                 :          0 :         return 0;
        2184                 :            :     }
        2185                 :            :
        2186                 :            :     // Copy the UTC transitions into each array to be modified in place later
        2187         [ #  # ]:          0 :     for (size_t i = 0; i < 2; ++i) {
        2188                 :          0 :         trans_local[i] = PyMem_Malloc(num_transitions * sizeof(int64_t));
        2189         [ #  # ]:          0 :         if (trans_local[i] == NULL) {
        2190                 :          0 :             return -1;
        2191                 :            :         }
        2192                 :            :
        2193                 :          0 :         memcpy(trans_local[i], trans_utc, num_transitions * sizeof(int64_t));
        2194                 :            :     }
        2195                 :            :
        2196                 :            :     int64_t offset_0, offset_1, buff;
        2197         [ #  # ]:          0 :     if (num_ttinfos > 1) {
        2198                 :          0 :         offset_0 = utcoff[0];
        2199                 :          0 :         offset_1 = utcoff[trans_idx[0]];
        2200                 :            :
        2201         [ #  # ]:          0 :         if (offset_1 > offset_0) {
        2202                 :          0 :             _swap(offset_0, offset_1, buff);
        2203                 :            :         }
        2204                 :            :     }
        2205                 :            :     else {
        2206                 :          0 :         offset_0 = utcoff[0];
        2207                 :          0 :         offset_1 = utcoff[0];
        2208                 :            :     }
        2209                 :            :
        2210                 :          0 :     trans_local[0][0] += offset_0;
        2211                 :          0 :     trans_local[1][0] += offset_1;
        2212                 :            :
        2213         [ #  # ]:          0 :     for (size_t i = 1; i < num_transitions; ++i) {
        2214                 :          0 :         offset_0 = utcoff[trans_idx[i - 1]];
        2215                 :          0 :         offset_1 = utcoff[trans_idx[i]];
        2216                 :            :
        2217         [ #  # ]:          0 :         if (offset_1 > offset_0) {
        2218                 :          0 :             _swap(offset_1, offset_0, buff);
        2219                 :            :         }
        2220                 :            :
        2221                 :          0 :         trans_local[0][i] += offset_0;
        2222                 :          0 :         trans_local[1][i] += offset_1;
        2223                 :            :     }
        2224                 :            :
        2225                 :          0 :     return 0;
        2226                 :            : }
        2227                 :            :
        2228                 :            : /* Simple bisect_right binary search implementation */
        2229                 :            : static size_t
        2230                 :          0 : _bisect(const int64_t value, const int64_t *arr, size_t size)
        2231                 :            : {
        2232                 :          0 :     size_t lo = 0;
        2233                 :          0 :     size_t hi = size;
        2234                 :            :     size_t m;
        2235                 :            :
        2236         [ #  # ]:          0 :     while (lo < hi) {
        2237                 :          0 :         m = (lo + hi) / 2;
        2238         [ #  # ]:          0 :         if (arr[m] > value) {
        2239                 :          0 :             hi = m;
        2240                 :            :         }
        2241                 :            :         else {
        2242                 :          0 :             lo = m + 1;
        2243                 :            :         }
        2244                 :            :     }
        2245                 :            :
        2246                 :          0 :     return hi;
        2247                 :            : }
        2248                 :            :
        2249                 :            : /* Find the ttinfo rules that apply at a given local datetime. */
        2250                 :            : static _ttinfo *
        2251                 :          0 : find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt)
        2252                 :            : {
        2253                 :            :     // datetime.time has a .tzinfo attribute that passes None as the dt
        2254                 :            :     // argument; it only really has meaning for fixed-offset zones.
        2255         [ #  # ]:          0 :     if (dt == Py_None) {
        2256         [ #  # ]:          0 :         if (self->fixed_offset) {
        2257                 :          0 :             return &(self->tzrule_after.std);
        2258                 :            :         }
        2259                 :            :         else {
        2260                 :          0 :             return &(state->NO_TTINFO);
        2261                 :            :         }
        2262                 :            :     }
        2263                 :            :
        2264                 :            :     int64_t ts;
        2265         [ #  # ]:          0 :     if (get_local_timestamp(dt, &ts)) {
        2266                 :          0 :         return NULL;
        2267                 :            :     }
        2268                 :            :
        2269                 :          0 :     unsigned char fold = PyDateTime_DATE_GET_FOLD(dt);
        2270                 :            :     assert(fold < 2);
        2271                 :          0 :     int64_t *local_transitions = self->trans_list_wall[fold];
        2272                 :          0 :     size_t num_trans = self->num_transitions;
        2273                 :            :
        2274   [ #  #  #  # ]:          0 :     if (num_trans && ts < local_transitions[0]) {
        2275                 :          0 :         return self->ttinfo_before;
        2276                 :            :     }
        2277   [ #  #  #  # ]:          0 :     else if (!num_trans || ts > local_transitions[self->num_transitions - 1]) {
        2278                 :          0 :         return find_tzrule_ttinfo(&(self->tzrule_after), ts, fold,
        2279                 :          0 :                                   PyDateTime_GET_YEAR(dt));
        2280                 :            :     }
        2281                 :            :     else {
        2282                 :          0 :         size_t idx = _bisect(ts, local_transitions, self->num_transitions) - 1;
        2283                 :            :         assert(idx < self->num_transitions);
        2284                 :          0 :         return self->trans_ttinfos[idx];
        2285                 :            :     }
        2286                 :            : }
        2287                 :            :
        2288                 :            : static int
        2289                 :          0 : is_leap_year(int year)
        2290                 :            : {
        2291                 :          0 :     const unsigned int ayear = (unsigned int)year;
        2292   [ #  #  #  #  :          0 :     return ayear % 4 == 0 && (ayear % 100 != 0 || ayear % 400 == 0);
                       #  # ]
        2293                 :            : }
        2294                 :            :
        2295                 :            : /* Calculates ordinal datetime from year, month and day. */
        2296                 :            : static int
        2297                 :          0 : ymd_to_ord(int y, int m, int d)
        2298                 :            : {
        2299                 :          0 :     y -= 1;
        2300                 :          0 :     int days_before_year = (y * 365) + (y / 4) - (y / 100) + (y / 400);
        2301                 :          0 :     int yearday = DAYS_BEFORE_MONTH[m];
        2302   [ #  #  #  # ]:          0 :     if (m > 2 && is_leap_year(y + 1)) {
        2303                 :          0 :         yearday += 1;
        2304                 :            :     }
        2305                 :            :
        2306                 :          0 :     return days_before_year + yearday + d;
        2307                 :            : }
        2308                 :            :
        2309                 :            : /* Calculate the number of seconds since 1970-01-01 in local time.
        2310                 :            :  *
        2311                 :            :  * This gets a datetime in the same "units" as self->trans_list_wall so that we
        2312                 :            :  * can easily determine which transitions a datetime falls between. See the
        2313                 :            :  * comment above ts_to_local for more information.
        2314                 :            :  * */
        2315                 :            : static int
        2316                 :          0 : get_local_timestamp(PyObject *dt, int64_t *local_ts)
        2317                 :            : {
        2318                 :            :     assert(local_ts != NULL);
        2319                 :            :
        2320                 :            :     int hour, minute, second;
        2321                 :            :     int ord;
        2322         [ #  # ]:          0 :     if (PyDateTime_CheckExact(dt)) {
        2323                 :          0 :         int y = PyDateTime_GET_YEAR(dt);
        2324                 :          0 :         int m = PyDateTime_GET_MONTH(dt);
        2325                 :          0 :         int d = PyDateTime_GET_DAY(dt);
        2326                 :          0 :         hour = PyDateTime_DATE_GET_HOUR(dt);
        2327                 :          0 :         minute = PyDateTime_DATE_GET_MINUTE(dt);
        2328                 :          0 :         second = PyDateTime_DATE_GET_SECOND(dt);
        2329                 :            :
        2330                 :          0 :         ord = ymd_to_ord(y, m, d);
        2331                 :            :     }
        2332                 :            :     else {
        2333                 :          0 :         PyObject *num = PyObject_CallMethod(dt, "toordinal", NULL);
        2334         [ #  # ]:          0 :         if (num == NULL) {
        2335                 :          0 :             return -1;
        2336                 :            :         }
        2337                 :            :
        2338                 :          0 :         ord = PyLong_AsLong(num);
        2339                 :          0 :         Py_DECREF(num);
        2340   [ #  #  #  # ]:          0 :         if (ord == -1 && PyErr_Occurred()) {
        2341                 :          0 :             return -1;
        2342                 :            :         }
        2343                 :            :
        2344                 :          0 :         num = PyObject_GetAttrString(dt, "hour");
        2345         [ #  # ]:          0 :         if (num == NULL) {
        2346                 :          0 :             return -1;
        2347                 :            :         }
        2348                 :          0 :         hour = PyLong_AsLong(num);
        2349                 :          0 :         Py_DECREF(num);
        2350         [ #  # ]:          0 :         if (hour == -1) {
        2351                 :          0 :             return -1;
        2352                 :            :         }
        2353                 :            :
        2354                 :          0 :         num = PyObject_GetAttrString(dt, "minute");
        2355         [ #  # ]:          0 :         if (num == NULL) {
        2356                 :          0 :             return -1;
        2357                 :            :         }
        2358                 :          0 :         minute = PyLong_AsLong(num);
        2359                 :          0 :         Py_DECREF(num);
        2360         [ #  # ]:          0 :         if (minute == -1) {
        2361                 :          0 :             return -1;
        2362                 :            :         }
        2363                 :            :
        2364                 :          0 :         num = PyObject_GetAttrString(dt, "second");
        2365         [ #  # ]:          0 :         if (num == NULL) {
        2366                 :          0 :             return -1;
        2367                 :            :         }
        2368                 :          0 :         second = PyLong_AsLong(num);
        2369                 :          0 :         Py_DECREF(num);
        2370         [ #  # ]:          0 :         if (second == -1) {
        2371                 :          0 :             return -1;
        2372                 :            :         }
        2373                 :            :     }
        2374                 :            :
        2375                 :          0 :     *local_ts = (int64_t)(ord - EPOCHORDINAL) * 86400 +
        2376                 :          0 :                 (int64_t)(hour * 3600 + minute * 60 + second);
        2377                 :            :
        2378                 :          0 :     return 0;
        2379                 :            : }
        2380                 :            :
        2381                 :            : /////
        2382                 :            : // Functions for cache handling
        2383                 :            :
        2384                 :            : /* Constructor for StrongCacheNode */
        2385                 :            : static StrongCacheNode *
        2386                 :          0 : strong_cache_node_new(PyObject *key, PyObject *zone)
        2387                 :            : {
        2388                 :          0 :     StrongCacheNode *node = PyMem_Malloc(sizeof(StrongCacheNode));
        2389         [ #  # ]:          0 :     if (node == NULL) {
        2390                 :          0 :         return NULL;
        2391                 :            :     }
        2392                 :            :
        2393                 :          0 :     node->next = NULL;
        2394                 :          0 :     node->prev = NULL;
        2395                 :          0 :     node->key = Py_NewRef(key);
        2396                 :          0 :     node->zone = Py_NewRef(zone);
        2397                 :            :
        2398                 :          0 :     return node;
        2399                 :            : }
        2400                 :            :
        2401                 :            : /* Destructor for StrongCacheNode */
        2402                 :            : void
        2403                 :          0 : strong_cache_node_free(StrongCacheNode *node)
        2404                 :            : {
        2405                 :          0 :     Py_XDECREF(node->key);
        2406                 :          0 :     Py_XDECREF(node->zone);
        2407                 :            :
        2408                 :          0 :     PyMem_Free(node);
        2409                 :          0 : }
        2410                 :            :
        2411                 :            : /* Frees all nodes at or after a specified root in the strong cache.
        2412                 :            :  *
        2413                 :            :  * This can be used on the root node to free the entire cache or it can be used
        2414                 :            :  * to clear all nodes that have been expired (which, if everything is going
        2415                 :            :  * right, will actually only be 1 node at a time).
        2416                 :            :  */
        2417                 :            : void
        2418                 :          2 : strong_cache_free(StrongCacheNode *root)
        2419                 :            : {
        2420                 :          2 :     StrongCacheNode *node = root;
        2421                 :            :     StrongCacheNode *next_node;
        2422         [ -  + ]:          2 :     while (node != NULL) {
        2423                 :          0 :         next_node = node->next;
        2424                 :          0 :         strong_cache_node_free(node);
        2425                 :            :
        2426                 :          0 :         node = next_node;
        2427                 :            :     }
        2428                 :          2 : }
        2429                 :            :
        2430                 :            : /* Removes a node from the cache and update its neighbors.
        2431                 :            :  *
        2432                 :            :  * This is used both when ejecting a node from the cache and when moving it to
        2433                 :            :  * the front of the cache.
        2434                 :            :  */
        2435                 :            : static void
        2436                 :          0 : remove_from_strong_cache(zoneinfo_state *state, StrongCacheNode *node)
        2437                 :            : {
        2438         [ #  # ]:          0 :     if (state->ZONEINFO_STRONG_CACHE == node) {
        2439                 :          0 :         state->ZONEINFO_STRONG_CACHE = node->next;
        2440                 :            :     }
        2441                 :            :
        2442         [ #  # ]:          0 :     if (node->prev != NULL) {
        2443                 :          0 :         node->prev->next = node->next;
        2444                 :            :     }
        2445                 :            :
        2446         [ #  # ]:          0 :     if (node->next != NULL) {
        2447                 :          0 :         node->next->prev = node->prev;
        2448                 :            :     }
        2449                 :            :
        2450                 :          0 :     node->next = NULL;
        2451                 :          0 :     node->prev = NULL;
        2452                 :          0 : }
        2453                 :            :
        2454                 :            : /* Retrieves the node associated with a key, if it exists.
        2455                 :            :  *
        2456                 :            :  * This traverses the strong cache until it finds a matching key and returns a
        2457                 :            :  * pointer to the relevant node if found. Returns NULL if no node is found.
        2458                 :            :  *
        2459                 :            :  * root may be NULL, indicating an empty cache.
        2460                 :            :  */
        2461                 :            : static StrongCacheNode *
        2462                 :          0 : find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key)
        2463                 :            : {
        2464                 :          0 :     const StrongCacheNode *node = root;
        2465         [ #  # ]:          0 :     while (node != NULL) {
        2466                 :          0 :         int rv = PyObject_RichCompareBool(key, node->key, Py_EQ);
        2467         [ #  # ]:          0 :         if (rv < 0) {
        2468                 :          0 :             return NULL;
        2469                 :            :         }
        2470         [ #  # ]:          0 :         if (rv) {
        2471                 :          0 :             return (StrongCacheNode *)node;
        2472                 :            :         }
        2473                 :            :
        2474                 :          0 :         node = node->next;
        2475                 :            :     }
        2476                 :            :
        2477                 :          0 :     return NULL;
        2478                 :            : }
        2479                 :            :
        2480                 :            : /* Ejects a given key from the class's strong cache, if applicable.
        2481                 :            :  *
        2482                 :            :  * This function is used to enable the per-key functionality in clear_cache.
        2483                 :            :  */
        2484                 :            : static int
        2485                 :          0 : eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
        2486                 :            :                         PyObject *key)
        2487                 :            : {
        2488         [ #  # ]:          0 :     if (type != state->ZoneInfoType) {
        2489                 :          0 :         return 0;
        2490                 :            :     }
        2491                 :            :
        2492                 :          0 :     StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE;
        2493                 :          0 :     StrongCacheNode *node = find_in_strong_cache(cache, key);
        2494         [ #  # ]:          0 :     if (node != NULL) {
        2495                 :          0 :         remove_from_strong_cache(state, node);
        2496                 :            :
        2497                 :          0 :         strong_cache_node_free(node);
        2498                 :            :     }
        2499         [ #  # ]:          0 :     else if (PyErr_Occurred()) {
        2500                 :          0 :         return -1;
        2501                 :            :     }
        2502                 :          0 :     return 0;
        2503                 :            : }
        2504                 :            :
        2505                 :            : /* Moves a node to the front of the LRU cache.
        2506                 :            :  *
        2507                 :            :  * The strong cache is an LRU cache, so whenever a given node is accessed, if
        2508                 :            :  * it is not at the front of the cache, it needs to be moved there.
        2509                 :            :  */
        2510                 :            : static void
        2511                 :          0 : move_strong_cache_node_to_front(zoneinfo_state *state, StrongCacheNode **root,
        2512                 :            :                                 StrongCacheNode *node)
        2513                 :            : {
        2514                 :          0 :     StrongCacheNode *root_p = *root;
        2515         [ #  # ]:          0 :     if (root_p == node) {
        2516                 :          0 :         return;
        2517                 :            :     }
        2518                 :            :
        2519                 :          0 :     remove_from_strong_cache(state, node);
        2520                 :            :
        2521                 :          0 :     node->prev = NULL;
        2522                 :          0 :     node->next = root_p;
        2523                 :            :
        2524         [ #  # ]:          0 :     if (root_p != NULL) {
        2525                 :          0 :         root_p->prev = node;
        2526                 :            :     }
        2527                 :            :
        2528                 :          0 :     *root = node;
        2529                 :            : }
        2530                 :            :
        2531                 :            : /* Retrieves a ZoneInfo from the strong cache if it's present.
        2532                 :            :  *
        2533                 :            :  * This function finds the ZoneInfo by key and if found will move the node to
        2534                 :            :  * the front of the LRU cache and return a new reference to it. It returns NULL
        2535                 :            :  * if the key is not in the cache.
        2536                 :            :  *
        2537                 :            :  * The strong cache is currently only implemented for the base class, so this
        2538                 :            :  * always returns a cache miss for subclasses.
        2539                 :            :  */
        2540                 :            : static PyObject *
        2541                 :          0 : zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
        2542                 :            :                        PyObject *const key)
        2543                 :            : {
        2544         [ #  # ]:          0 :     if (type != state->ZoneInfoType) {
        2545                 :          0 :         return NULL;  // Strong cache currently only implemented for base class
        2546                 :            :     }
        2547                 :            :
        2548                 :          0 :     StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE;
        2549                 :          0 :     StrongCacheNode *node = find_in_strong_cache(cache, key);
        2550                 :            :
        2551         [ #  # ]:          0 :     if (node != NULL) {
        2552                 :          0 :         StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE);
        2553                 :          0 :         move_strong_cache_node_to_front(state, root, node);
        2554                 :          0 :         return Py_NewRef(node->zone);
        2555                 :            :     }
        2556                 :            :
        2557                 :          0 :     return NULL;  // Cache miss
        2558                 :            : }
        2559                 :            :
        2560                 :            : /* Inserts a new key into the strong LRU cache.
        2561                 :            :  *
        2562                 :            :  * This function is only to be used after a cache miss — it creates a new node
        2563                 :            :  * at the front of the cache and ejects any stale entries (keeping the size of
        2564                 :            :  * the cache to at most ZONEINFO_STRONG_CACHE_MAX_SIZE).
        2565                 :            :  */
        2566                 :            : static void
        2567                 :          0 : update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type,
        2568                 :            :                     PyObject *key, PyObject *zone)
        2569                 :            : {
        2570         [ #  # ]:          0 :     if (type != state->ZoneInfoType) {
        2571                 :          0 :         return;
        2572                 :            :     }
        2573                 :            :
        2574                 :          0 :     StrongCacheNode *new_node = strong_cache_node_new(key, zone);
        2575                 :          0 :     StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE);
        2576                 :          0 :     move_strong_cache_node_to_front(state, root, new_node);
        2577                 :            :
        2578                 :          0 :     StrongCacheNode *node = new_node->next;
        2579         [ #  # ]:          0 :     for (size_t i = 1; i < ZONEINFO_STRONG_CACHE_MAX_SIZE; ++i) {
        2580         [ #  # ]:          0 :         if (node == NULL) {
        2581                 :          0 :             return;
        2582                 :            :         }
        2583                 :          0 :         node = node->next;
        2584                 :            :     }
        2585                 :            :
        2586                 :            :     // Everything beyond this point needs to be freed
        2587         [ #  # ]:          0 :     if (node != NULL) {
        2588         [ #  # ]:          0 :         if (node->prev != NULL) {
        2589                 :          0 :             node->prev->next = NULL;
        2590                 :            :         }
        2591                 :          0 :         strong_cache_free(node);
        2592                 :            :     }
        2593                 :            : }
        2594                 :            :
        2595                 :            : /* Clears all entries into a type's strong cache.
        2596                 :            :  *
        2597                 :            :  * Because the strong cache is not implemented for subclasses, this is a no-op
        2598                 :            :  * for everything except the base class.
        2599                 :            :  */
        2600                 :            : void
        2601                 :          2 : clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type)
        2602                 :            : {
        2603         [ -  + ]:          2 :     if (type != state->ZoneInfoType) {
        2604                 :          0 :         return;
        2605                 :            :     }
        2606                 :            :
        2607                 :          2 :     strong_cache_free(state->ZONEINFO_STRONG_CACHE);
        2608                 :          2 :     state->ZONEINFO_STRONG_CACHE = NULL;
        2609                 :            : }
        2610                 :            :
        2611                 :            : static PyObject *
        2612                 :          1 : new_weak_cache(void)
        2613                 :            : {
        2614                 :            :     PyObject *WeakValueDictionary =
        2615                 :          1 :             _PyImport_GetModuleAttrString("weakref", "WeakValueDictionary");
        2616         [ -  + ]:          1 :     if (WeakValueDictionary == NULL) {
        2617                 :          0 :         return NULL;
        2618                 :            :     }
        2619                 :          1 :     PyObject *weak_cache = PyObject_CallNoArgs(WeakValueDictionary);
        2620                 :          1 :     Py_DECREF(WeakValueDictionary);
        2621                 :          1 :     return weak_cache;
        2622                 :            : }
        2623                 :            :
        2624                 :            : // This function is not idempotent and must be called on a new module object.
        2625                 :            : static int
        2626                 :          1 : initialize_caches(zoneinfo_state *state)
        2627                 :            : {
        2628                 :          1 :     state->TIMEDELTA_CACHE = PyDict_New();
        2629         [ -  + ]:          1 :     if (state->TIMEDELTA_CACHE == NULL) {
        2630                 :          0 :         return -1;
        2631                 :            :     }
        2632                 :            :
        2633                 :          1 :     state->ZONEINFO_WEAK_CACHE = new_weak_cache();
        2634         [ -  + ]:          1 :     if (state->ZONEINFO_WEAK_CACHE == NULL) {
        2635                 :          0 :         return -1;
        2636                 :            :     }
        2637                 :            :
        2638                 :          1 :     return 0;
        2639                 :            : }
        2640                 :            :
        2641                 :            : static PyObject *
        2642                 :          0 : zoneinfo_init_subclass(PyTypeObject *cls, PyObject *args, PyObject **kwargs)
        2643                 :            : {
        2644                 :          0 :     PyObject *weak_cache = new_weak_cache();
        2645         [ #  # ]:          0 :     if (weak_cache == NULL) {
        2646                 :          0 :         return NULL;
        2647                 :            :     }
        2648                 :            :
        2649         [ #  # ]:          0 :     if (PyObject_SetAttrString((PyObject *)cls, "_weak_cache",
        2650                 :            :                                weak_cache) < 0) {
        2651                 :          0 :         Py_DECREF(weak_cache);
        2652                 :          0 :         return NULL;
        2653                 :            :     }
        2654                 :          0 :     Py_DECREF(weak_cache);
        2655                 :          0 :     Py_RETURN_NONE;
        2656                 :            : }
        2657                 :            :
        2658                 :            : /////
        2659                 :            : // Specify the ZoneInfo type
        2660                 :            : static PyMethodDef zoneinfo_methods[] = {
        2661                 :            :     ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF
        2662                 :            :     ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF
        2663                 :            :     ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF
        2664                 :            :     ZONEINFO_ZONEINFO_UTCOFFSET_METHODDEF
        2665                 :            :     ZONEINFO_ZONEINFO_DST_METHODDEF
        2666                 :            :     ZONEINFO_ZONEINFO_TZNAME_METHODDEF
        2667                 :            :     {"fromutc", (PyCFunction)zoneinfo_fromutc, METH_O,
        2668                 :            :      PyDoc_STR("Given a datetime with local time in UTC, retrieve an adjusted "
        2669                 :            :                "datetime in local time.")},
        2670                 :            :     {"__reduce__", (PyCFunction)zoneinfo_reduce, METH_NOARGS,
        2671                 :            :      PyDoc_STR("Function for serialization with the pickle protocol.")},
        2672                 :            :     ZONEINFO_ZONEINFO__UNPICKLE_METHODDEF
        2673                 :            :     {"__init_subclass__", (PyCFunction)(void (*)(void))zoneinfo_init_subclass,
        2674                 :            :      METH_VARARGS | METH_KEYWORDS | METH_CLASS,
        2675                 :            :      PyDoc_STR("Function to initialize subclasses.")},
        2676                 :            :     {NULL} /* Sentinel */
        2677                 :            : };
        2678                 :            :
        2679                 :            : static PyMemberDef zoneinfo_members[] = {
        2680                 :            :     {.name = "key",
        2681                 :            :      .offset = offsetof(PyZoneInfo_ZoneInfo, key),
        2682                 :            :      .type = T_OBJECT_EX,
        2683                 :            :      .flags = READONLY,
        2684                 :            :      .doc = NULL},
        2685                 :            :     {.name = "__weaklistoffset__",
        2686                 :            :      .offset = offsetof(PyZoneInfo_ZoneInfo, weakreflist),
        2687                 :            :      .type = T_PYSSIZET,
        2688                 :            :      .flags = READONLY},
        2689                 :            :     {NULL}, /* Sentinel */
        2690                 :            : };
        2691                 :            :
        2692                 :            : static PyType_Slot zoneinfo_slots[] = {
        2693                 :            :     {Py_tp_repr, zoneinfo_repr},
        2694                 :            :     {Py_tp_str, zoneinfo_str},
        2695                 :            :     {Py_tp_getattro, PyObject_GenericGetAttr},
        2696                 :            :     {Py_tp_methods, zoneinfo_methods},
        2697                 :            :     {Py_tp_members, zoneinfo_members},
        2698                 :            :     {Py_tp_new, zoneinfo_new},
        2699                 :            :     {Py_tp_dealloc, zoneinfo_dealloc},
        2700                 :            :     {Py_tp_traverse, zoneinfo_traverse},
        2701                 :            :     {Py_tp_clear, zoneinfo_clear},
        2702                 :            :     {0, NULL},
        2703                 :            : };
        2704                 :            :
        2705                 :            : static PyType_Spec zoneinfo_spec = {
        2706                 :            :     .name = "zoneinfo.ZoneInfo",
        2707                 :            :     .basicsize = sizeof(PyZoneInfo_ZoneInfo),
        2708                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
        2709                 :            :               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
        2710                 :            :     .slots = zoneinfo_slots,
        2711                 :            : };
        2712                 :            :
        2713                 :            : /////
        2714                 :            : // Specify the _zoneinfo module
        2715                 :            : static PyMethodDef module_methods[] = {{NULL, NULL}};
        2716                 :            :
        2717                 :            : static int
        2718                 :          6 : module_traverse(PyObject *mod, visitproc visit, void *arg)
        2719                 :            : {
        2720                 :          6 :     zoneinfo_state *state = zoneinfo_get_state(mod);
        2721                 :            :
        2722   [ +  +  -  + ]:          6 :     Py_VISIT(state->ZoneInfoType);
        2723   [ +  +  -  + ]:          6 :     Py_VISIT(state->io_open);
        2724   [ +  +  -  + ]:          6 :     Py_VISIT(state->_tzpath_find_tzfile);
        2725   [ +  +  -  + ]:          6 :     Py_VISIT(state->_common_mod);
        2726   [ +  +  -  + ]:          6 :     Py_VISIT(state->TIMEDELTA_CACHE);
        2727   [ +  +  -  + ]:          6 :     Py_VISIT(state->ZONEINFO_WEAK_CACHE);
        2728                 :            :
        2729                 :          6 :     StrongCacheNode *node = state->ZONEINFO_STRONG_CACHE;
        2730         [ -  + ]:          6 :     while (node != NULL) {
        2731                 :          0 :         StrongCacheNode *next = node->next;
        2732   [ #  #  #  # ]:          0 :         Py_VISIT(node->key);
        2733   [ #  #  #  # ]:          0 :         Py_VISIT(node->zone);
        2734                 :          0 :         node = next;
        2735                 :            :     }
        2736                 :            :
        2737   [ +  +  -  + ]:          6 :     Py_VISIT(state->NO_TTINFO.utcoff);
        2738   [ +  +  -  + ]:          6 :     Py_VISIT(state->NO_TTINFO.dstoff);
        2739   [ +  +  -  + ]:          6 :     Py_VISIT(state->NO_TTINFO.tzname);
        2740                 :            :
        2741                 :          6 :     return 0;
        2742                 :            : }
        2743                 :            :
        2744                 :            : static int
        2745                 :          2 : module_clear(PyObject *mod)
        2746                 :            : {
        2747                 :          2 :     zoneinfo_state *state = zoneinfo_get_state(mod);
        2748                 :            :
        2749         [ +  + ]:          2 :     Py_CLEAR(state->ZoneInfoType);
        2750         [ +  + ]:          2 :     Py_CLEAR(state->io_open);
        2751         [ +  + ]:          2 :     Py_CLEAR(state->_tzpath_find_tzfile);
        2752         [ +  + ]:          2 :     Py_CLEAR(state->_common_mod);
        2753         [ +  + ]:          2 :     Py_CLEAR(state->TIMEDELTA_CACHE);
        2754         [ +  + ]:          2 :     Py_CLEAR(state->ZONEINFO_WEAK_CACHE);
        2755                 :          2 :     clear_strong_cache(state, state->ZoneInfoType);
        2756         [ +  + ]:          2 :     Py_CLEAR(state->NO_TTINFO.utcoff);
        2757         [ +  + ]:          2 :     Py_CLEAR(state->NO_TTINFO.dstoff);
        2758         [ +  + ]:          2 :     Py_CLEAR(state->NO_TTINFO.tzname);
        2759                 :            :
        2760                 :          2 :     return 0;
        2761                 :            : }
        2762                 :            :
        2763                 :            : static void
        2764                 :          1 : module_free(void *mod)
        2765                 :            : {
        2766                 :          1 :     (void)module_clear((PyObject *)mod);
        2767                 :          1 : }
        2768                 :            :
        2769                 :            : static int
        2770                 :          1 : zoneinfomodule_exec(PyObject *m)
        2771                 :            : {
        2772                 :          1 :     PyDateTime_IMPORT;
        2773         [ -  + ]:          1 :     if (PyDateTimeAPI == NULL) {
        2774                 :          0 :         goto error;
        2775                 :            :     }
        2776                 :            :
        2777                 :          1 :     zoneinfo_state *state = zoneinfo_get_state(m);
        2778                 :          1 :     PyObject *base = (PyObject *)PyDateTimeAPI->TZInfoType;
        2779                 :          1 :     state->ZoneInfoType = (PyTypeObject *)PyType_FromModuleAndSpec(m,
        2780                 :            :                                                         &zoneinfo_spec, base);
        2781         [ -  + ]:          1 :     if (state->ZoneInfoType == NULL) {
        2782                 :          0 :         goto error;
        2783                 :            :     }
        2784                 :            :
        2785                 :          1 :     int rc = PyModule_AddObjectRef(m, "ZoneInfo",
        2786                 :          1 :                                    (PyObject *)state->ZoneInfoType);
        2787         [ -  + ]:          1 :     if (rc < 0) {
        2788                 :          0 :         goto error;
        2789                 :            :     }
        2790                 :            :
        2791                 :            :     /* Populate imports */
        2792                 :          1 :     state->_tzpath_find_tzfile =
        2793                 :          1 :         _PyImport_GetModuleAttrString("zoneinfo._tzpath", "find_tzfile");
        2794         [ -  + ]:          1 :     if (state->_tzpath_find_tzfile == NULL) {
        2795                 :          0 :         goto error;
        2796                 :            :     }
        2797                 :            :
        2798                 :          1 :     state->io_open = _PyImport_GetModuleAttrString("io", "open");
        2799         [ -  + ]:          1 :     if (state->io_open == NULL) {
        2800                 :          0 :         goto error;
        2801                 :            :     }
        2802                 :            :
        2803                 :          1 :     state->_common_mod = PyImport_ImportModule("zoneinfo._common");
        2804         [ -  + ]:          1 :     if (state->_common_mod == NULL) {
        2805                 :          0 :         goto error;
        2806                 :            :     }
        2807                 :            :
        2808         [ +  - ]:          1 :     if (state->NO_TTINFO.utcoff == NULL) {
        2809                 :          1 :         state->NO_TTINFO.utcoff = Py_NewRef(Py_None);
        2810                 :          1 :         state->NO_TTINFO.dstoff = Py_NewRef(Py_None);
        2811                 :          1 :         state->NO_TTINFO.tzname = Py_NewRef(Py_None);
        2812                 :            :     }
        2813                 :            :
        2814         [ -  + ]:          1 :     if (initialize_caches(state)) {
        2815                 :          0 :         goto error;
        2816                 :            :     }
        2817                 :            :
        2818                 :          1 :     return 0;
        2819                 :            :
        2820                 :          0 : error:
        2821                 :          0 :     return -1;
        2822                 :            : }
        2823                 :            :
        2824                 :            : static PyModuleDef_Slot zoneinfomodule_slots[] = {
        2825                 :            :     {Py_mod_exec, zoneinfomodule_exec}, {0, NULL}};
        2826                 :            :
        2827                 :            : static struct PyModuleDef zoneinfomodule = {
        2828                 :            :     .m_base = PyModuleDef_HEAD_INIT,
        2829                 :            :     .m_name = "_zoneinfo",
        2830                 :            :     .m_doc = "C implementation of the zoneinfo module",
        2831                 :            :     .m_size = sizeof(zoneinfo_state),
        2832                 :            :     .m_methods = module_methods,
        2833                 :            :     .m_slots = zoneinfomodule_slots,
        2834                 :            :     .m_traverse = module_traverse,
        2835                 :            :     .m_clear = module_clear,
        2836                 :            :     .m_free = module_free,
        2837                 :            : };
        2838                 :            :
        2839                 :            : PyMODINIT_FUNC
        2840                 :          1 : PyInit__zoneinfo(void)
        2841                 :            : {
        2842                 :          1 :     return PyModuleDef_Init(&zoneinfomodule);
        2843                 :            : }

Generated by: LCOV version 1.14