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