• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "parts.h"
2 
3 #include "datetime.h"             // PyDateTimeAPI
4 
5 
6 static int test_run_counter = 0;
7 
8 static PyObject *
test_datetime_capi(PyObject * self,PyObject * args)9 test_datetime_capi(PyObject *self, PyObject *args)
10 {
11     if (PyDateTimeAPI) {
12         if (test_run_counter) {
13             /* Probably regrtest.py -R */
14             Py_RETURN_NONE;
15         }
16         else {
17             PyErr_SetString(PyExc_AssertionError,
18                             "PyDateTime_CAPI somehow initialized");
19             return NULL;
20         }
21     }
22     test_run_counter++;
23     PyDateTime_IMPORT;
24 
25     if (PyDateTimeAPI == NULL) {
26         return NULL;
27     }
28     // The following C API types need to outlive interpreters, since the
29     // borrowed references to them can be held by users without being updated.
30     assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE));
31     assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE));
32     assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE));
33     assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE));
34     assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE));
35     Py_RETURN_NONE;
36 }
37 
38 /* Functions exposing the C API type checking for testing */
39 #define MAKE_DATETIME_CHECK_FUNC(check_method, exact_method)    \
40 do {                                                            \
41     PyObject *obj;                                              \
42     int exact = 0;                                              \
43     if (!PyArg_ParseTuple(args, "O|p", &obj, &exact)) {         \
44         return NULL;                                            \
45     }                                                           \
46     int rv = exact?exact_method(obj):check_method(obj);         \
47     if (rv) {                                                   \
48         Py_RETURN_TRUE;                                         \
49     }                                                           \
50     Py_RETURN_FALSE;                                            \
51 } while (0)                                                     \
52 
53 static PyObject *
datetime_check_date(PyObject * self,PyObject * args)54 datetime_check_date(PyObject *self, PyObject *args)
55 {
56     MAKE_DATETIME_CHECK_FUNC(PyDate_Check, PyDate_CheckExact);
57 }
58 
59 static PyObject *
datetime_check_time(PyObject * self,PyObject * args)60 datetime_check_time(PyObject *self, PyObject *args)
61 {
62     MAKE_DATETIME_CHECK_FUNC(PyTime_Check, PyTime_CheckExact);
63 }
64 
65 static PyObject *
datetime_check_datetime(PyObject * self,PyObject * args)66 datetime_check_datetime(PyObject *self, PyObject *args)
67 {
68     MAKE_DATETIME_CHECK_FUNC(PyDateTime_Check, PyDateTime_CheckExact);
69 }
70 
71 static PyObject *
datetime_check_delta(PyObject * self,PyObject * args)72 datetime_check_delta(PyObject *self, PyObject *args)
73 {
74     MAKE_DATETIME_CHECK_FUNC(PyDelta_Check, PyDelta_CheckExact);
75 }
76 
77 static PyObject *
datetime_check_tzinfo(PyObject * self,PyObject * args)78 datetime_check_tzinfo(PyObject *self, PyObject *args)
79 {
80     MAKE_DATETIME_CHECK_FUNC(PyTZInfo_Check, PyTZInfo_CheckExact);
81 }
82 #undef MAKE_DATETIME_CHECK_FUNC
83 
84 
85 /* Makes three variations on timezone representing UTC-5:
86    1. timezone with offset and name from PyDateTimeAPI
87    2. timezone with offset and name from PyTimeZone_FromOffsetAndName
88    3. timezone with offset (no name) from PyTimeZone_FromOffset
89 */
90 static PyObject *
make_timezones_capi(PyObject * self,PyObject * args)91 make_timezones_capi(PyObject *self, PyObject *args)
92 {
93     PyObject *offset = PyDelta_FromDSU(0, -18000, 0);
94     PyObject *name = PyUnicode_FromString("EST");
95     if (offset == NULL || name == NULL) {
96         Py_XDECREF(offset);
97         Py_XDECREF(name);
98         return NULL;
99     }
100 
101     PyObject *est_zone_capi = PyDateTimeAPI->TimeZone_FromTimeZone(offset, name);
102     PyObject *est_zone_macro = PyTimeZone_FromOffsetAndName(offset, name);
103     PyObject *est_zone_macro_noname = PyTimeZone_FromOffset(offset);
104     Py_DECREF(offset);
105     Py_DECREF(name);
106     if (est_zone_capi == NULL || est_zone_macro == NULL ||
107         est_zone_macro_noname == NULL)
108     {
109         goto error;
110     }
111     PyObject *rv = PyTuple_New(3);
112     if (rv == NULL) {
113         goto error;
114     }
115 
116     PyTuple_SET_ITEM(rv, 0, est_zone_capi);
117     PyTuple_SET_ITEM(rv, 1, est_zone_macro);
118     PyTuple_SET_ITEM(rv, 2, est_zone_macro_noname);
119 
120     return rv;
121 error:
122     Py_XDECREF(est_zone_capi);
123     Py_XDECREF(est_zone_macro);
124     Py_XDECREF(est_zone_macro_noname);
125     return NULL;
126 }
127 
128 static PyObject *
get_timezones_offset_zero(PyObject * self,PyObject * args)129 get_timezones_offset_zero(PyObject *self, PyObject *args)
130 {
131     PyObject *offset = PyDelta_FromDSU(0, 0, 0);
132     PyObject *name = PyUnicode_FromString("");
133     if (offset == NULL || name == NULL) {
134         Py_XDECREF(offset);
135         Py_XDECREF(name);
136         return NULL;
137     }
138 
139     // These two should return the UTC singleton
140     PyObject *utc_singleton_0 = PyTimeZone_FromOffset(offset);
141     PyObject *utc_singleton_1 = PyTimeZone_FromOffsetAndName(offset, NULL);
142 
143     // This one will return +00:00 zone, but not the UTC singleton
144     PyObject *non_utc_zone = PyTimeZone_FromOffsetAndName(offset, name);
145     Py_DECREF(offset);
146     Py_DECREF(name);
147     if (utc_singleton_0 == NULL || utc_singleton_1 == NULL ||
148         non_utc_zone == NULL)
149     {
150         goto error;
151     }
152 
153     PyObject *rv = PyTuple_New(3);
154     if (rv == NULL) {
155         goto error;
156     }
157     PyTuple_SET_ITEM(rv, 0, utc_singleton_0);
158     PyTuple_SET_ITEM(rv, 1, utc_singleton_1);
159     PyTuple_SET_ITEM(rv, 2, non_utc_zone);
160 
161     return rv;
162 error:
163     Py_XDECREF(utc_singleton_0);
164     Py_XDECREF(utc_singleton_1);
165     Py_XDECREF(non_utc_zone);
166     return NULL;
167 }
168 
169 static PyObject *
get_timezone_utc_capi(PyObject * self,PyObject * args)170 get_timezone_utc_capi(PyObject *self, PyObject *args)
171 {
172     int macro = 0;
173     if (!PyArg_ParseTuple(args, "|p", &macro)) {
174         return NULL;
175     }
176     if (macro) {
177         return Py_NewRef(PyDateTime_TimeZone_UTC);
178     }
179     return Py_NewRef(PyDateTimeAPI->TimeZone_UTC);
180 }
181 
182 static PyObject *
get_date_fromdate(PyObject * self,PyObject * args)183 get_date_fromdate(PyObject *self, PyObject *args)
184 {
185     PyObject *rv = NULL;
186     int macro;
187     int year, month, day;
188 
189     if (!PyArg_ParseTuple(args, "piii", &macro, &year, &month, &day)) {
190         return NULL;
191     }
192 
193     if (macro) {
194         rv = PyDate_FromDate(year, month, day);
195     }
196     else {
197         rv = PyDateTimeAPI->Date_FromDate(
198                 year, month, day,
199                 PyDateTimeAPI->DateType);
200     }
201     return rv;
202 }
203 
204 static PyObject *
get_datetime_fromdateandtime(PyObject * self,PyObject * args)205 get_datetime_fromdateandtime(PyObject *self, PyObject *args)
206 {
207     PyObject *rv = NULL;
208     int macro;
209     int year, month, day;
210     int hour, minute, second, microsecond;
211 
212     if (!PyArg_ParseTuple(args, "piiiiiii",
213                           &macro,
214                           &year, &month, &day,
215                           &hour, &minute, &second, &microsecond)) {
216         return NULL;
217     }
218 
219     if (macro) {
220         rv = PyDateTime_FromDateAndTime(
221                 year, month, day,
222                 hour, minute, second, microsecond);
223     }
224     else {
225         rv = PyDateTimeAPI->DateTime_FromDateAndTime(
226                 year, month, day,
227                 hour, minute, second, microsecond,
228                 Py_None,
229                 PyDateTimeAPI->DateTimeType);
230     }
231     return rv;
232 }
233 
234 static PyObject *
get_datetime_fromdateandtimeandfold(PyObject * self,PyObject * args)235 get_datetime_fromdateandtimeandfold(PyObject *self, PyObject *args)
236 {
237     PyObject *rv = NULL;
238     int macro;
239     int year, month, day;
240     int hour, minute, second, microsecond, fold;
241 
242     if (!PyArg_ParseTuple(args, "piiiiiiii",
243                           &macro,
244                           &year, &month, &day,
245                           &hour, &minute, &second, &microsecond,
246                           &fold)) {
247         return NULL;
248     }
249 
250     if (macro) {
251         rv = PyDateTime_FromDateAndTimeAndFold(
252                 year, month, day,
253                 hour, minute, second, microsecond,
254                 fold);
255     }
256     else {
257         rv = PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(
258                 year, month, day,
259                 hour, minute, second, microsecond,
260                 Py_None,
261                 fold,
262                 PyDateTimeAPI->DateTimeType);
263     }
264     return rv;
265 }
266 
267 static PyObject *
get_time_fromtime(PyObject * self,PyObject * args)268 get_time_fromtime(PyObject *self, PyObject *args)
269 {
270     PyObject *rv = NULL;
271     int macro;
272     int hour, minute, second, microsecond;
273 
274     if (!PyArg_ParseTuple(args, "piiii",
275                           &macro,
276                           &hour, &minute, &second, &microsecond))
277     {
278         return NULL;
279     }
280 
281     if (macro) {
282         rv = PyTime_FromTime(hour, minute, second, microsecond);
283     }
284     else {
285         rv = PyDateTimeAPI->Time_FromTime(
286                 hour, minute, second, microsecond,
287                 Py_None,
288                 PyDateTimeAPI->TimeType);
289     }
290     return rv;
291 }
292 
293 static PyObject *
get_time_fromtimeandfold(PyObject * self,PyObject * args)294 get_time_fromtimeandfold(PyObject *self, PyObject *args)
295 {
296     PyObject *rv = NULL;
297     int macro;
298     int hour, minute, second, microsecond, fold;
299 
300     if (!PyArg_ParseTuple(args, "piiiii",
301                           &macro,
302                           &hour, &minute, &second, &microsecond,
303                           &fold)) {
304         return NULL;
305     }
306 
307     if (macro) {
308         rv = PyTime_FromTimeAndFold(hour, minute, second, microsecond, fold);
309     }
310     else {
311         rv = PyDateTimeAPI->Time_FromTimeAndFold(
312                 hour, minute, second, microsecond,
313                 Py_None,
314                 fold,
315                 PyDateTimeAPI->TimeType);
316     }
317     return rv;
318 }
319 
320 static PyObject *
get_delta_fromdsu(PyObject * self,PyObject * args)321 get_delta_fromdsu(PyObject *self, PyObject *args)
322 {
323     PyObject *rv = NULL;
324     int macro;
325     int days, seconds, microseconds;
326 
327     if (!PyArg_ParseTuple(args, "piii",
328                           &macro,
329                           &days, &seconds, &microseconds)) {
330         return NULL;
331     }
332 
333     if (macro) {
334         rv = PyDelta_FromDSU(days, seconds, microseconds);
335     }
336     else {
337         rv = PyDateTimeAPI->Delta_FromDelta(
338                 days, seconds, microseconds, 1,
339                 PyDateTimeAPI->DeltaType);
340     }
341 
342     return rv;
343 }
344 
345 static PyObject *
get_date_fromtimestamp(PyObject * self,PyObject * args)346 get_date_fromtimestamp(PyObject *self, PyObject *args)
347 {
348     PyObject *tsargs = NULL, *ts = NULL, *rv = NULL;
349     int macro = 0;
350 
351     if (!PyArg_ParseTuple(args, "O|p", &ts, &macro)) {
352         return NULL;
353     }
354 
355     // Construct the argument tuple
356     if ((tsargs = PyTuple_Pack(1, ts)) == NULL) {
357         return NULL;
358     }
359 
360     // Pass along to the API function
361     if (macro) {
362         rv = PyDate_FromTimestamp(tsargs);
363     }
364     else {
365         rv = PyDateTimeAPI->Date_FromTimestamp(
366                 (PyObject *)PyDateTimeAPI->DateType, tsargs
367         );
368     }
369 
370     Py_DECREF(tsargs);
371     return rv;
372 }
373 
374 static PyObject *
get_datetime_fromtimestamp(PyObject * self,PyObject * args)375 get_datetime_fromtimestamp(PyObject *self, PyObject *args)
376 {
377     int macro = 0;
378     int usetz = 0;
379     PyObject *tsargs = NULL, *ts = NULL, *tzinfo = Py_None, *rv = NULL;
380     if (!PyArg_ParseTuple(args, "OO|pp", &ts, &tzinfo, &usetz, &macro)) {
381         return NULL;
382     }
383 
384     // Construct the argument tuple
385     if (usetz) {
386         tsargs = PyTuple_Pack(2, ts, tzinfo);
387     }
388     else {
389         tsargs = PyTuple_Pack(1, ts);
390     }
391 
392     if (tsargs == NULL) {
393         return NULL;
394     }
395 
396     // Pass along to the API function
397     if (macro) {
398         rv = PyDateTime_FromTimestamp(tsargs);
399     }
400     else {
401         rv = PyDateTimeAPI->DateTime_FromTimestamp(
402                 (PyObject *)PyDateTimeAPI->DateTimeType, tsargs, NULL
403         );
404     }
405 
406     Py_DECREF(tsargs);
407     return rv;
408 }
409 
410 static PyObject *
test_PyDateTime_GET(PyObject * self,PyObject * obj)411 test_PyDateTime_GET(PyObject *self, PyObject *obj)
412 {
413     int year, month, day;
414 
415     year = PyDateTime_GET_YEAR(obj);
416     month = PyDateTime_GET_MONTH(obj);
417     day = PyDateTime_GET_DAY(obj);
418 
419     return Py_BuildValue("(iii)", year, month, day);
420 }
421 
422 static PyObject *
test_PyDateTime_DATE_GET(PyObject * self,PyObject * obj)423 test_PyDateTime_DATE_GET(PyObject *self, PyObject *obj)
424 {
425     int hour = PyDateTime_DATE_GET_HOUR(obj);
426     int minute = PyDateTime_DATE_GET_MINUTE(obj);
427     int second = PyDateTime_DATE_GET_SECOND(obj);
428     int microsecond = PyDateTime_DATE_GET_MICROSECOND(obj);
429     PyObject *tzinfo = PyDateTime_DATE_GET_TZINFO(obj);
430 
431     return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo);
432 }
433 
434 static PyObject *
test_PyDateTime_TIME_GET(PyObject * self,PyObject * obj)435 test_PyDateTime_TIME_GET(PyObject *self, PyObject *obj)
436 {
437     int hour = PyDateTime_TIME_GET_HOUR(obj);
438     int minute = PyDateTime_TIME_GET_MINUTE(obj);
439     int second = PyDateTime_TIME_GET_SECOND(obj);
440     int microsecond = PyDateTime_TIME_GET_MICROSECOND(obj);
441     PyObject *tzinfo = PyDateTime_TIME_GET_TZINFO(obj);
442 
443     return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo);
444 }
445 
446 static PyObject *
test_PyDateTime_DELTA_GET(PyObject * self,PyObject * obj)447 test_PyDateTime_DELTA_GET(PyObject *self, PyObject *obj)
448 {
449     int days = PyDateTime_DELTA_GET_DAYS(obj);
450     int seconds = PyDateTime_DELTA_GET_SECONDS(obj);
451     int microseconds = PyDateTime_DELTA_GET_MICROSECONDS(obj);
452 
453     return Py_BuildValue("(iii)", days, seconds, microseconds);
454 }
455 
456 static PyMethodDef test_methods[] = {
457     {"PyDateTime_DATE_GET",         test_PyDateTime_DATE_GET,       METH_O},
458     {"PyDateTime_DELTA_GET",        test_PyDateTime_DELTA_GET,      METH_O},
459     {"PyDateTime_GET",              test_PyDateTime_GET,            METH_O},
460     {"PyDateTime_TIME_GET",         test_PyDateTime_TIME_GET,       METH_O},
461     {"datetime_check_date",         datetime_check_date,            METH_VARARGS},
462     {"datetime_check_datetime",     datetime_check_datetime,        METH_VARARGS},
463     {"datetime_check_delta",        datetime_check_delta,           METH_VARARGS},
464     {"datetime_check_time",         datetime_check_time,            METH_VARARGS},
465     {"datetime_check_tzinfo",       datetime_check_tzinfo,          METH_VARARGS},
466     {"get_date_fromdate",           get_date_fromdate,              METH_VARARGS},
467     {"get_date_fromtimestamp",      get_date_fromtimestamp,         METH_VARARGS},
468     {"get_datetime_fromdateandtime", get_datetime_fromdateandtime,  METH_VARARGS},
469     {"get_datetime_fromdateandtimeandfold", get_datetime_fromdateandtimeandfold, METH_VARARGS},
470     {"get_datetime_fromtimestamp",  get_datetime_fromtimestamp,     METH_VARARGS},
471     {"get_delta_fromdsu",           get_delta_fromdsu,              METH_VARARGS},
472     {"get_time_fromtime",           get_time_fromtime,              METH_VARARGS},
473     {"get_time_fromtimeandfold",    get_time_fromtimeandfold,       METH_VARARGS},
474     {"get_timezone_utc_capi",       get_timezone_utc_capi,          METH_VARARGS},
475     {"get_timezones_offset_zero",   get_timezones_offset_zero,      METH_NOARGS},
476     {"make_timezones_capi",         make_timezones_capi,            METH_NOARGS},
477     {"test_datetime_capi",          test_datetime_capi,             METH_NOARGS},
478     {NULL},
479 };
480 
481 int
_PyTestCapi_Init_DateTime(PyObject * mod)482 _PyTestCapi_Init_DateTime(PyObject *mod)
483 {
484     if (PyModule_AddFunctions(mod, test_methods) < 0) {
485         return -1;
486     }
487     return 0;
488 }
489 
490 
491 /* ---------------------------------------------------------------------------
492  * Test module for subinterpreters.
493  */
494 
495 static int
_testcapi_datetime_exec(PyObject * mod)496 _testcapi_datetime_exec(PyObject *mod)
497 {
498     if (test_datetime_capi(NULL, NULL) == NULL)  {
499         return -1;
500     }
501     return 0;
502 }
503 
504 static PyModuleDef_Slot _testcapi_datetime_slots[] = {
505     {Py_mod_exec, _testcapi_datetime_exec},
506     {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
507     {Py_mod_gil, Py_MOD_GIL_NOT_USED},
508     {0, NULL},
509 };
510 
511 static struct PyModuleDef _testcapi_datetime_module = {
512     PyModuleDef_HEAD_INIT,
513     .m_name = "_testcapi_datetime",
514     .m_size = 0,
515     .m_methods = test_methods,
516     .m_slots = _testcapi_datetime_slots,
517 };
518 
519 PyMODINIT_FUNC
PyInit__testcapi_datetime(void)520 PyInit__testcapi_datetime(void)
521 {
522     return PyModuleDef_Init(&_testcapi_datetime_module);
523 }
524