mirror of https://github.com/python/cpython
524 lines
15 KiB
C
524 lines
15 KiB
C
#include "parts.h"
|
|
|
|
#include "datetime.h" // PyDateTimeAPI
|
|
|
|
|
|
static int test_run_counter = 0;
|
|
|
|
static PyObject *
|
|
test_datetime_capi(PyObject *self, PyObject *args)
|
|
{
|
|
if (PyDateTimeAPI) {
|
|
if (test_run_counter) {
|
|
/* Probably regrtest.py -R */
|
|
Py_RETURN_NONE;
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_AssertionError,
|
|
"PyDateTime_CAPI somehow initialized");
|
|
return NULL;
|
|
}
|
|
}
|
|
test_run_counter++;
|
|
PyDateTime_IMPORT;
|
|
|
|
if (PyDateTimeAPI == NULL) {
|
|
return NULL;
|
|
}
|
|
// The following C API types need to outlive interpreters, since the
|
|
// borrowed references to them can be held by users without being updated.
|
|
assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE));
|
|
assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE));
|
|
assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE));
|
|
assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE));
|
|
assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE));
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
/* Functions exposing the C API type checking for testing */
|
|
#define MAKE_DATETIME_CHECK_FUNC(check_method, exact_method) \
|
|
do { \
|
|
PyObject *obj; \
|
|
int exact = 0; \
|
|
if (!PyArg_ParseTuple(args, "O|p", &obj, &exact)) { \
|
|
return NULL; \
|
|
} \
|
|
int rv = exact?exact_method(obj):check_method(obj); \
|
|
if (rv) { \
|
|
Py_RETURN_TRUE; \
|
|
} \
|
|
Py_RETURN_FALSE; \
|
|
} while (0) \
|
|
|
|
static PyObject *
|
|
datetime_check_date(PyObject *self, PyObject *args)
|
|
{
|
|
MAKE_DATETIME_CHECK_FUNC(PyDate_Check, PyDate_CheckExact);
|
|
}
|
|
|
|
static PyObject *
|
|
datetime_check_time(PyObject *self, PyObject *args)
|
|
{
|
|
MAKE_DATETIME_CHECK_FUNC(PyTime_Check, PyTime_CheckExact);
|
|
}
|
|
|
|
static PyObject *
|
|
datetime_check_datetime(PyObject *self, PyObject *args)
|
|
{
|
|
MAKE_DATETIME_CHECK_FUNC(PyDateTime_Check, PyDateTime_CheckExact);
|
|
}
|
|
|
|
static PyObject *
|
|
datetime_check_delta(PyObject *self, PyObject *args)
|
|
{
|
|
MAKE_DATETIME_CHECK_FUNC(PyDelta_Check, PyDelta_CheckExact);
|
|
}
|
|
|
|
static PyObject *
|
|
datetime_check_tzinfo(PyObject *self, PyObject *args)
|
|
{
|
|
MAKE_DATETIME_CHECK_FUNC(PyTZInfo_Check, PyTZInfo_CheckExact);
|
|
}
|
|
#undef MAKE_DATETIME_CHECK_FUNC
|
|
|
|
|
|
/* Makes three variations on timezone representing UTC-5:
|
|
1. timezone with offset and name from PyDateTimeAPI
|
|
2. timezone with offset and name from PyTimeZone_FromOffsetAndName
|
|
3. timezone with offset (no name) from PyTimeZone_FromOffset
|
|
*/
|
|
static PyObject *
|
|
make_timezones_capi(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *offset = PyDelta_FromDSU(0, -18000, 0);
|
|
PyObject *name = PyUnicode_FromString("EST");
|
|
if (offset == NULL || name == NULL) {
|
|
Py_XDECREF(offset);
|
|
Py_XDECREF(name);
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *est_zone_capi = PyDateTimeAPI->TimeZone_FromTimeZone(offset, name);
|
|
PyObject *est_zone_macro = PyTimeZone_FromOffsetAndName(offset, name);
|
|
PyObject *est_zone_macro_noname = PyTimeZone_FromOffset(offset);
|
|
Py_DECREF(offset);
|
|
Py_DECREF(name);
|
|
if (est_zone_capi == NULL || est_zone_macro == NULL ||
|
|
est_zone_macro_noname == NULL)
|
|
{
|
|
goto error;
|
|
}
|
|
PyObject *rv = PyTuple_New(3);
|
|
if (rv == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
PyTuple_SET_ITEM(rv, 0, est_zone_capi);
|
|
PyTuple_SET_ITEM(rv, 1, est_zone_macro);
|
|
PyTuple_SET_ITEM(rv, 2, est_zone_macro_noname);
|
|
|
|
return rv;
|
|
error:
|
|
Py_XDECREF(est_zone_capi);
|
|
Py_XDECREF(est_zone_macro);
|
|
Py_XDECREF(est_zone_macro_noname);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *
|
|
get_timezones_offset_zero(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *offset = PyDelta_FromDSU(0, 0, 0);
|
|
PyObject *name = PyUnicode_FromString("");
|
|
if (offset == NULL || name == NULL) {
|
|
Py_XDECREF(offset);
|
|
Py_XDECREF(name);
|
|
return NULL;
|
|
}
|
|
|
|
// These two should return the UTC singleton
|
|
PyObject *utc_singleton_0 = PyTimeZone_FromOffset(offset);
|
|
PyObject *utc_singleton_1 = PyTimeZone_FromOffsetAndName(offset, NULL);
|
|
|
|
// This one will return +00:00 zone, but not the UTC singleton
|
|
PyObject *non_utc_zone = PyTimeZone_FromOffsetAndName(offset, name);
|
|
Py_DECREF(offset);
|
|
Py_DECREF(name);
|
|
if (utc_singleton_0 == NULL || utc_singleton_1 == NULL ||
|
|
non_utc_zone == NULL)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
PyObject *rv = PyTuple_New(3);
|
|
if (rv == NULL) {
|
|
goto error;
|
|
}
|
|
PyTuple_SET_ITEM(rv, 0, utc_singleton_0);
|
|
PyTuple_SET_ITEM(rv, 1, utc_singleton_1);
|
|
PyTuple_SET_ITEM(rv, 2, non_utc_zone);
|
|
|
|
return rv;
|
|
error:
|
|
Py_XDECREF(utc_singleton_0);
|
|
Py_XDECREF(utc_singleton_1);
|
|
Py_XDECREF(non_utc_zone);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *
|
|
get_timezone_utc_capi(PyObject *self, PyObject *args)
|
|
{
|
|
int macro = 0;
|
|
if (!PyArg_ParseTuple(args, "|p", ¯o)) {
|
|
return NULL;
|
|
}
|
|
if (macro) {
|
|
return Py_NewRef(PyDateTime_TimeZone_UTC);
|
|
}
|
|
return Py_NewRef(PyDateTimeAPI->TimeZone_UTC);
|
|
}
|
|
|
|
static PyObject *
|
|
get_date_fromdate(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int year, month, day;
|
|
|
|
if (!PyArg_ParseTuple(args, "piii", ¯o, &year, &month, &day)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyDate_FromDate(year, month, day);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->Date_FromDate(
|
|
year, month, day,
|
|
PyDateTimeAPI->DateType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_datetime_fromdateandtime(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int year, month, day;
|
|
int hour, minute, second, microsecond;
|
|
|
|
if (!PyArg_ParseTuple(args, "piiiiiii",
|
|
¯o,
|
|
&year, &month, &day,
|
|
&hour, &minute, &second, µsecond)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyDateTime_FromDateAndTime(
|
|
year, month, day,
|
|
hour, minute, second, microsecond);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->DateTime_FromDateAndTime(
|
|
year, month, day,
|
|
hour, minute, second, microsecond,
|
|
Py_None,
|
|
PyDateTimeAPI->DateTimeType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_datetime_fromdateandtimeandfold(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int year, month, day;
|
|
int hour, minute, second, microsecond, fold;
|
|
|
|
if (!PyArg_ParseTuple(args, "piiiiiiii",
|
|
¯o,
|
|
&year, &month, &day,
|
|
&hour, &minute, &second, µsecond,
|
|
&fold)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyDateTime_FromDateAndTimeAndFold(
|
|
year, month, day,
|
|
hour, minute, second, microsecond,
|
|
fold);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(
|
|
year, month, day,
|
|
hour, minute, second, microsecond,
|
|
Py_None,
|
|
fold,
|
|
PyDateTimeAPI->DateTimeType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_time_fromtime(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int hour, minute, second, microsecond;
|
|
|
|
if (!PyArg_ParseTuple(args, "piiii",
|
|
¯o,
|
|
&hour, &minute, &second, µsecond))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyTime_FromTime(hour, minute, second, microsecond);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->Time_FromTime(
|
|
hour, minute, second, microsecond,
|
|
Py_None,
|
|
PyDateTimeAPI->TimeType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_time_fromtimeandfold(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int hour, minute, second, microsecond, fold;
|
|
|
|
if (!PyArg_ParseTuple(args, "piiiii",
|
|
¯o,
|
|
&hour, &minute, &second, µsecond,
|
|
&fold)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyTime_FromTimeAndFold(hour, minute, second, microsecond, fold);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->Time_FromTimeAndFold(
|
|
hour, minute, second, microsecond,
|
|
Py_None,
|
|
fold,
|
|
PyDateTimeAPI->TimeType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_delta_fromdsu(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *rv = NULL;
|
|
int macro;
|
|
int days, seconds, microseconds;
|
|
|
|
if (!PyArg_ParseTuple(args, "piii",
|
|
¯o,
|
|
&days, &seconds, µseconds)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (macro) {
|
|
rv = PyDelta_FromDSU(days, seconds, microseconds);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->Delta_FromDelta(
|
|
days, seconds, microseconds, 1,
|
|
PyDateTimeAPI->DeltaType);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_date_fromtimestamp(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *tsargs = NULL, *ts = NULL, *rv = NULL;
|
|
int macro = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|p", &ts, ¯o)) {
|
|
return NULL;
|
|
}
|
|
|
|
// Construct the argument tuple
|
|
if ((tsargs = PyTuple_Pack(1, ts)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Pass along to the API function
|
|
if (macro) {
|
|
rv = PyDate_FromTimestamp(tsargs);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->Date_FromTimestamp(
|
|
(PyObject *)PyDateTimeAPI->DateType, tsargs
|
|
);
|
|
}
|
|
|
|
Py_DECREF(tsargs);
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
get_datetime_fromtimestamp(PyObject *self, PyObject *args)
|
|
{
|
|
int macro = 0;
|
|
int usetz = 0;
|
|
PyObject *tsargs = NULL, *ts = NULL, *tzinfo = Py_None, *rv = NULL;
|
|
if (!PyArg_ParseTuple(args, "OO|pp", &ts, &tzinfo, &usetz, ¯o)) {
|
|
return NULL;
|
|
}
|
|
|
|
// Construct the argument tuple
|
|
if (usetz) {
|
|
tsargs = PyTuple_Pack(2, ts, tzinfo);
|
|
}
|
|
else {
|
|
tsargs = PyTuple_Pack(1, ts);
|
|
}
|
|
|
|
if (tsargs == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Pass along to the API function
|
|
if (macro) {
|
|
rv = PyDateTime_FromTimestamp(tsargs);
|
|
}
|
|
else {
|
|
rv = PyDateTimeAPI->DateTime_FromTimestamp(
|
|
(PyObject *)PyDateTimeAPI->DateTimeType, tsargs, NULL
|
|
);
|
|
}
|
|
|
|
Py_DECREF(tsargs);
|
|
return rv;
|
|
}
|
|
|
|
static PyObject *
|
|
test_PyDateTime_GET(PyObject *self, PyObject *obj)
|
|
{
|
|
int year, month, day;
|
|
|
|
year = PyDateTime_GET_YEAR(obj);
|
|
month = PyDateTime_GET_MONTH(obj);
|
|
day = PyDateTime_GET_DAY(obj);
|
|
|
|
return Py_BuildValue("(iii)", year, month, day);
|
|
}
|
|
|
|
static PyObject *
|
|
test_PyDateTime_DATE_GET(PyObject *self, PyObject *obj)
|
|
{
|
|
int hour = PyDateTime_DATE_GET_HOUR(obj);
|
|
int minute = PyDateTime_DATE_GET_MINUTE(obj);
|
|
int second = PyDateTime_DATE_GET_SECOND(obj);
|
|
int microsecond = PyDateTime_DATE_GET_MICROSECOND(obj);
|
|
PyObject *tzinfo = PyDateTime_DATE_GET_TZINFO(obj);
|
|
|
|
return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo);
|
|
}
|
|
|
|
static PyObject *
|
|
test_PyDateTime_TIME_GET(PyObject *self, PyObject *obj)
|
|
{
|
|
int hour = PyDateTime_TIME_GET_HOUR(obj);
|
|
int minute = PyDateTime_TIME_GET_MINUTE(obj);
|
|
int second = PyDateTime_TIME_GET_SECOND(obj);
|
|
int microsecond = PyDateTime_TIME_GET_MICROSECOND(obj);
|
|
PyObject *tzinfo = PyDateTime_TIME_GET_TZINFO(obj);
|
|
|
|
return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo);
|
|
}
|
|
|
|
static PyObject *
|
|
test_PyDateTime_DELTA_GET(PyObject *self, PyObject *obj)
|
|
{
|
|
int days = PyDateTime_DELTA_GET_DAYS(obj);
|
|
int seconds = PyDateTime_DELTA_GET_SECONDS(obj);
|
|
int microseconds = PyDateTime_DELTA_GET_MICROSECONDS(obj);
|
|
|
|
return Py_BuildValue("(iii)", days, seconds, microseconds);
|
|
}
|
|
|
|
static PyMethodDef test_methods[] = {
|
|
{"PyDateTime_DATE_GET", test_PyDateTime_DATE_GET, METH_O},
|
|
{"PyDateTime_DELTA_GET", test_PyDateTime_DELTA_GET, METH_O},
|
|
{"PyDateTime_GET", test_PyDateTime_GET, METH_O},
|
|
{"PyDateTime_TIME_GET", test_PyDateTime_TIME_GET, METH_O},
|
|
{"datetime_check_date", datetime_check_date, METH_VARARGS},
|
|
{"datetime_check_datetime", datetime_check_datetime, METH_VARARGS},
|
|
{"datetime_check_delta", datetime_check_delta, METH_VARARGS},
|
|
{"datetime_check_time", datetime_check_time, METH_VARARGS},
|
|
{"datetime_check_tzinfo", datetime_check_tzinfo, METH_VARARGS},
|
|
{"get_date_fromdate", get_date_fromdate, METH_VARARGS},
|
|
{"get_date_fromtimestamp", get_date_fromtimestamp, METH_VARARGS},
|
|
{"get_datetime_fromdateandtime", get_datetime_fromdateandtime, METH_VARARGS},
|
|
{"get_datetime_fromdateandtimeandfold", get_datetime_fromdateandtimeandfold, METH_VARARGS},
|
|
{"get_datetime_fromtimestamp", get_datetime_fromtimestamp, METH_VARARGS},
|
|
{"get_delta_fromdsu", get_delta_fromdsu, METH_VARARGS},
|
|
{"get_time_fromtime", get_time_fromtime, METH_VARARGS},
|
|
{"get_time_fromtimeandfold", get_time_fromtimeandfold, METH_VARARGS},
|
|
{"get_timezone_utc_capi", get_timezone_utc_capi, METH_VARARGS},
|
|
{"get_timezones_offset_zero", get_timezones_offset_zero, METH_NOARGS},
|
|
{"make_timezones_capi", make_timezones_capi, METH_NOARGS},
|
|
{"test_datetime_capi", test_datetime_capi, METH_NOARGS},
|
|
{NULL},
|
|
};
|
|
|
|
int
|
|
_PyTestCapi_Init_DateTime(PyObject *mod)
|
|
{
|
|
if (PyModule_AddFunctions(mod, test_methods) < 0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Test module for subinterpreters.
|
|
*/
|
|
|
|
static int
|
|
_testcapi_datetime_exec(PyObject *mod)
|
|
{
|
|
if (test_datetime_capi(NULL, NULL) == NULL) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static PyModuleDef_Slot _testcapi_datetime_slots[] = {
|
|
{Py_mod_exec, _testcapi_datetime_exec},
|
|
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
|
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
{0, NULL},
|
|
};
|
|
|
|
static struct PyModuleDef _testcapi_datetime_module = {
|
|
PyModuleDef_HEAD_INIT,
|
|
.m_name = "_testcapi_datetime",
|
|
.m_size = 0,
|
|
.m_methods = test_methods,
|
|
.m_slots = _testcapi_datetime_slots,
|
|
};
|
|
|
|
PyMODINIT_FUNC
|
|
PyInit__testcapi_datetime(void)
|
|
{
|
|
return PyModuleDef_Init(&_testcapi_datetime_module);
|
|
}
|