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