2002-12-16 16:18:38 -04:00
|
|
|
/* C implementation for the date/time type documented at
|
|
|
|
* http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage
|
|
|
|
*/
|
|
|
|
|
2018-11-13 04:02:25 -04:00
|
|
|
/* bpo-35081: Defining this prevents including the C API capsule;
|
|
|
|
* internal versions of the Py*_Check macros which do not require
|
|
|
|
* the capsule are defined below */
|
|
|
|
#define _PY_DATETIME_IMPL
|
|
|
|
|
2021-10-22 10:36:28 -03:00
|
|
|
#ifndef Py_BUILD_CORE_BUILTIN
|
|
|
|
# define Py_BUILD_CORE_MODULE 1
|
|
|
|
#endif
|
2022-02-08 16:39:07 -04:00
|
|
|
#define NEEDS_PY_IDENTIFIER
|
2021-10-22 10:36:28 -03:00
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
#include "Python.h"
|
2020-10-27 13:12:53 -03:00
|
|
|
#include "pycore_long.h" // _PyLong_GetOne()
|
2020-06-15 20:28:07 -03:00
|
|
|
#include "pycore_object.h" // _PyObject_Init()
|
2018-11-13 04:02:25 -04:00
|
|
|
#include "datetime.h"
|
2020-04-14 21:35:41 -03:00
|
|
|
#include "structmember.h" // PyMemberDef
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
2015-03-29 19:09:18 -03:00
|
|
|
#ifdef MS_WINDOWS
|
|
|
|
# include <winsock2.h> /* struct timeval */
|
|
|
|
#endif
|
|
|
|
|
2018-11-13 04:02:25 -04:00
|
|
|
#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType)
|
2020-02-17 06:09:15 -04:00
|
|
|
#define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType)
|
2018-11-13 04:02:25 -04:00
|
|
|
|
|
|
|
#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType)
|
2020-02-17 06:09:15 -04:00
|
|
|
#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateTimeType)
|
2018-11-13 04:02:25 -04:00
|
|
|
|
|
|
|
#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType)
|
2020-02-17 06:09:15 -04:00
|
|
|
#define PyTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TimeType)
|
2018-11-13 04:02:25 -04:00
|
|
|
|
|
|
|
#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType)
|
2020-02-17 06:09:15 -04:00
|
|
|
#define PyDelta_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DeltaType)
|
2018-11-13 04:02:25 -04:00
|
|
|
|
|
|
|
#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType)
|
2020-02-17 06:09:15 -04:00
|
|
|
#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TZInfoType)
|
2018-11-13 04:02:25 -04:00
|
|
|
|
2019-08-22 16:24:25 -03:00
|
|
|
#define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType)
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2014-01-07 16:41:53 -04:00
|
|
|
/*[clinic input]
|
2013-11-23 19:37:55 -04:00
|
|
|
module datetime
|
2014-01-26 00:43:29 -04:00
|
|
|
class datetime.datetime "PyDateTime_DateTime *" "&PyDateTime_DateTimeType"
|
2018-09-24 05:39:02 -03:00
|
|
|
class datetime.date "PyDateTime_Date *" "&PyDateTime_DateType"
|
2020-05-16 11:02:59 -03:00
|
|
|
class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "&PyDateTime_IsoCalendarDateType"
|
2014-01-07 16:41:53 -04:00
|
|
|
[clinic start generated code]*/
|
2020-05-16 11:02:59 -03:00
|
|
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=81bec0fa19837f63]*/
|
2013-11-23 19:37:55 -04:00
|
|
|
|
2015-04-03 17:53:51 -03:00
|
|
|
#include "clinic/_datetimemodule.c.h"
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* We require that C int be at least 32 bits, and use int virtually
|
|
|
|
* everywhere. In just a few cases we use a temp long, where a Python
|
|
|
|
* API returns a C long. In such cases, we have to ensure that the
|
|
|
|
* final result fits in a C int (this can be an issue on 64-bit boxes).
|
|
|
|
*/
|
|
|
|
#if SIZEOF_INT < 4
|
2010-07-23 16:25:47 -03:00
|
|
|
# error "_datetime.c requires that C int have at least 32 bits"
|
2002-12-16 16:18:38 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define MINYEAR 1
|
|
|
|
#define MAXYEAR 9999
|
2010-05-27 18:42:58 -03:00
|
|
|
#define MAXORDINAL 3652059 /* date(9999,12,31).toordinal() */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Nine decimal digits is easy to communicate, and leaves enough room
|
|
|
|
* so that two delta days can be added w/o fear of overflowing a signed
|
|
|
|
* 32-bit int, and with plenty of room left over to absorb any possible
|
|
|
|
* carries from adding seconds.
|
|
|
|
*/
|
|
|
|
#define MAX_DELTA_DAYS 999999999
|
|
|
|
|
|
|
|
/* Rename the long macros in datetime.h to more reasonable short names. */
|
2010-05-09 12:52:27 -03:00
|
|
|
#define GET_YEAR PyDateTime_GET_YEAR
|
|
|
|
#define GET_MONTH PyDateTime_GET_MONTH
|
|
|
|
#define GET_DAY PyDateTime_GET_DAY
|
|
|
|
#define DATE_GET_HOUR PyDateTime_DATE_GET_HOUR
|
|
|
|
#define DATE_GET_MINUTE PyDateTime_DATE_GET_MINUTE
|
|
|
|
#define DATE_GET_SECOND PyDateTime_DATE_GET_SECOND
|
|
|
|
#define DATE_GET_MICROSECOND PyDateTime_DATE_GET_MICROSECOND
|
2016-07-22 19:47:04 -03:00
|
|
|
#define DATE_GET_FOLD PyDateTime_DATE_GET_FOLD
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Date accessors for date and datetime. */
|
2010-05-09 12:52:27 -03:00
|
|
|
#define SET_YEAR(o, v) (((o)->data[0] = ((v) & 0xff00) >> 8), \
|
|
|
|
((o)->data[1] = ((v) & 0x00ff)))
|
|
|
|
#define SET_MONTH(o, v) (PyDateTime_GET_MONTH(o) = (v))
|
|
|
|
#define SET_DAY(o, v) (PyDateTime_GET_DAY(o) = (v))
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Date/Time accessors for datetime. */
|
2010-05-09 12:52:27 -03:00
|
|
|
#define DATE_SET_HOUR(o, v) (PyDateTime_DATE_GET_HOUR(o) = (v))
|
|
|
|
#define DATE_SET_MINUTE(o, v) (PyDateTime_DATE_GET_MINUTE(o) = (v))
|
|
|
|
#define DATE_SET_SECOND(o, v) (PyDateTime_DATE_GET_SECOND(o) = (v))
|
|
|
|
#define DATE_SET_MICROSECOND(o, v) \
|
|
|
|
(((o)->data[7] = ((v) & 0xff0000) >> 16), \
|
|
|
|
((o)->data[8] = ((v) & 0x00ff00) >> 8), \
|
|
|
|
((o)->data[9] = ((v) & 0x0000ff)))
|
2016-07-22 19:47:04 -03:00
|
|
|
#define DATE_SET_FOLD(o, v) (PyDateTime_DATE_GET_FOLD(o) = (v))
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Time accessors for time. */
|
2010-05-09 12:52:27 -03:00
|
|
|
#define TIME_GET_HOUR PyDateTime_TIME_GET_HOUR
|
|
|
|
#define TIME_GET_MINUTE PyDateTime_TIME_GET_MINUTE
|
|
|
|
#define TIME_GET_SECOND PyDateTime_TIME_GET_SECOND
|
|
|
|
#define TIME_GET_MICROSECOND PyDateTime_TIME_GET_MICROSECOND
|
2016-07-22 19:47:04 -03:00
|
|
|
#define TIME_GET_FOLD PyDateTime_TIME_GET_FOLD
|
2010-05-09 12:52:27 -03:00
|
|
|
#define TIME_SET_HOUR(o, v) (PyDateTime_TIME_GET_HOUR(o) = (v))
|
|
|
|
#define TIME_SET_MINUTE(o, v) (PyDateTime_TIME_GET_MINUTE(o) = (v))
|
|
|
|
#define TIME_SET_SECOND(o, v) (PyDateTime_TIME_GET_SECOND(o) = (v))
|
|
|
|
#define TIME_SET_MICROSECOND(o, v) \
|
|
|
|
(((o)->data[3] = ((v) & 0xff0000) >> 16), \
|
|
|
|
((o)->data[4] = ((v) & 0x00ff00) >> 8), \
|
|
|
|
((o)->data[5] = ((v) & 0x0000ff)))
|
2016-07-22 19:47:04 -03:00
|
|
|
#define TIME_SET_FOLD(o, v) (PyDateTime_TIME_GET_FOLD(o) = (v))
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Delta accessors for timedelta. */
|
2010-05-09 12:52:27 -03:00
|
|
|
#define GET_TD_DAYS(o) (((PyDateTime_Delta *)(o))->days)
|
|
|
|
#define GET_TD_SECONDS(o) (((PyDateTime_Delta *)(o))->seconds)
|
|
|
|
#define GET_TD_MICROSECONDS(o) (((PyDateTime_Delta *)(o))->microseconds)
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
#define SET_TD_DAYS(o, v) ((o)->days = (v))
|
|
|
|
#define SET_TD_SECONDS(o, v) ((o)->seconds = (v))
|
2002-12-16 16:18:38 -04:00
|
|
|
#define SET_TD_MICROSECONDS(o, v) ((o)->microseconds = (v))
|
|
|
|
|
2020-09-23 15:43:45 -03:00
|
|
|
#define HASTZINFO _PyDateTime_HAS_TZINFO
|
|
|
|
#define GET_TIME_TZINFO PyDateTime_TIME_GET_TZINFO
|
|
|
|
#define GET_DT_TZINFO PyDateTime_DATE_GET_TZINFO
|
2004-03-21 19:38:41 -04:00
|
|
|
/* M is a char or int claiming to be a valid month. The macro is equivalent
|
|
|
|
* to the two-sided Python test
|
2010-05-09 12:52:27 -03:00
|
|
|
* 1 <= M <= 12
|
2004-03-21 19:38:41 -04:00
|
|
|
*/
|
|
|
|
#define MONTH_IS_SANE(M) ((unsigned int)(M) - 1 < 12)
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* Forward declarations. */
|
|
|
|
static PyTypeObject PyDateTime_DateType;
|
|
|
|
static PyTypeObject PyDateTime_DateTimeType;
|
|
|
|
static PyTypeObject PyDateTime_DeltaType;
|
2020-05-16 11:02:59 -03:00
|
|
|
static PyTypeObject PyDateTime_IsoCalendarDateType;
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyTypeObject PyDateTime_TimeType;
|
|
|
|
static PyTypeObject PyDateTime_TZInfoType;
|
2010-06-14 11:15:50 -03:00
|
|
|
static PyTypeObject PyDateTime_TimeZoneType;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2017-02-10 05:34:02 -04:00
|
|
|
static int check_tzinfo_subclass(PyObject *p);
|
|
|
|
|
2013-11-07 13:46:53 -04:00
|
|
|
_Py_IDENTIFIER(as_integer_ratio);
|
|
|
|
_Py_IDENTIFIER(fromutc);
|
|
|
|
_Py_IDENTIFIER(isoformat);
|
|
|
|
_Py_IDENTIFIER(strftime);
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Math utilities.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* k = i+j overflows iff k differs in sign from both inputs,
|
|
|
|
* iff k^i has sign bit set and k^j has sign bit set,
|
|
|
|
* iff (k^i)&(k^j) has sign bit set.
|
|
|
|
*/
|
|
|
|
#define SIGNED_ADD_OVERFLOWED(RESULT, I, J) \
|
2010-05-09 12:52:27 -03:00
|
|
|
((((RESULT) ^ (I)) & ((RESULT) ^ (J))) < 0)
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* Compute Python divmod(x, y), returning the quotient and storing the
|
|
|
|
* remainder into *r. The quotient is the floor of x/y, and that's
|
|
|
|
* the real point of this. C will probably truncate instead (C99
|
|
|
|
* requires truncation; C89 left it implementation-defined).
|
|
|
|
* Simplification: we *require* that y > 0 here. That's appropriate
|
|
|
|
* for all the uses made of it. This simplifies the code and makes
|
|
|
|
* the overflow case impossible (divmod(LONG_MIN, -1) is the only
|
|
|
|
* overflow case).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
divmod(int x, int y, int *r)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int quo;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(y > 0);
|
|
|
|
quo = x / y;
|
|
|
|
*r = x - quo * y;
|
|
|
|
if (*r < 0) {
|
|
|
|
--quo;
|
|
|
|
*r += y;
|
|
|
|
}
|
|
|
|
assert(0 <= *r && *r < y);
|
|
|
|
return quo;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-05-31 14:33:47 -03:00
|
|
|
/* Nearest integer to m / n for integers m and n. Half-integer results
|
|
|
|
* are rounded to even.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
divide_nearest(PyObject *m, PyObject *n)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
PyObject *temp;
|
|
|
|
|
2010-06-07 15:47:09 -03:00
|
|
|
temp = _PyLong_DivmodNear(m, n);
|
2010-05-31 14:33:47 -03:00
|
|
|
if (temp == NULL)
|
|
|
|
return NULL;
|
|
|
|
result = PyTuple_GET_ITEM(temp, 0);
|
|
|
|
Py_INCREF(result);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* General calendrical helper functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* For each month ordinal in 1..12, the number of days in that month,
|
|
|
|
* and the number of days before that month in the same year. These
|
|
|
|
* are correct for non-leap years only.
|
|
|
|
*/
|
2015-12-25 13:53:18 -04:00
|
|
|
static const int _days_in_month[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* unused; this vector uses 1-based indexing */
|
|
|
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const int _days_before_month[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* unused; this vector uses 1-based indexing */
|
|
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* year -> 1 if leap year, else 0. */
|
|
|
|
static int
|
|
|
|
is_leap(int year)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Cast year to unsigned. The result is the same either way, but
|
|
|
|
* C can generate faster code for unsigned mod than for signed
|
|
|
|
* mod (especially for % 4 -- a good compiler should just grab
|
|
|
|
* the last 2 bits when the LHS is unsigned).
|
|
|
|
*/
|
|
|
|
const unsigned int ayear = (unsigned int)year;
|
|
|
|
return ayear % 4 == 0 && (ayear % 100 != 0 || ayear % 400 == 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* year, month -> number of days in that month in that year */
|
|
|
|
static int
|
|
|
|
days_in_month(int year, int month)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(month >= 1);
|
|
|
|
assert(month <= 12);
|
|
|
|
if (month == 2 && is_leap(year))
|
|
|
|
return 29;
|
|
|
|
else
|
|
|
|
return _days_in_month[month];
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-05-26 02:35:26 -03:00
|
|
|
/* year, month -> number of days in year preceding first day of month */
|
2002-12-16 16:18:38 -04:00
|
|
|
static int
|
|
|
|
days_before_month(int year, int month)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int days;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(month >= 1);
|
|
|
|
assert(month <= 12);
|
|
|
|
days = _days_before_month[month];
|
|
|
|
if (month > 2 && is_leap(year))
|
|
|
|
++days;
|
|
|
|
return days;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* year -> number of days before January 1st of year. Remember that we
|
|
|
|
* start with year 1, so days_before_year(1) == 0.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
days_before_year(int year)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int y = year - 1;
|
|
|
|
/* This is incorrect if year <= 0; we really want the floor
|
|
|
|
* here. But so long as MINYEAR is 1, the smallest year this
|
2010-10-13 19:54:34 -03:00
|
|
|
* can see is 1.
|
2010-05-09 12:52:27 -03:00
|
|
|
*/
|
2010-10-13 19:54:34 -03:00
|
|
|
assert (year >= 1);
|
|
|
|
return y*365 + y/4 - y/100 + y/400;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Number of days in 4, 100, and 400 year cycles. That these have
|
|
|
|
* the correct values is asserted in the module init function.
|
|
|
|
*/
|
2010-05-09 12:52:27 -03:00
|
|
|
#define DI4Y 1461 /* days_before_year(5); days in 4 years */
|
|
|
|
#define DI100Y 36524 /* days_before_year(101); days in 100 years */
|
|
|
|
#define DI400Y 146097 /* days_before_year(401); days in 400 years */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
/* ordinal -> year, month, day, considering 01-Jan-0001 as day 1. */
|
|
|
|
static void
|
|
|
|
ord_to_ymd(int ordinal, int *year, int *month, int *day)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int n, n1, n4, n100, n400, leapyear, preceding;
|
|
|
|
|
|
|
|
/* ordinal is a 1-based index, starting at 1-Jan-1. The pattern of
|
|
|
|
* leap years repeats exactly every 400 years. The basic strategy is
|
|
|
|
* to find the closest 400-year boundary at or before ordinal, then
|
|
|
|
* work with the offset from that boundary to ordinal. Life is much
|
|
|
|
* clearer if we subtract 1 from ordinal first -- then the values
|
|
|
|
* of ordinal at 400-year boundaries are exactly those divisible
|
|
|
|
* by DI400Y:
|
|
|
|
*
|
|
|
|
* D M Y n n-1
|
|
|
|
* -- --- ---- ---------- ----------------
|
|
|
|
* 31 Dec -400 -DI400Y -DI400Y -1
|
|
|
|
* 1 Jan -399 -DI400Y +1 -DI400Y 400-year boundary
|
|
|
|
* ...
|
|
|
|
* 30 Dec 000 -1 -2
|
|
|
|
* 31 Dec 000 0 -1
|
|
|
|
* 1 Jan 001 1 0 400-year boundary
|
|
|
|
* 2 Jan 001 2 1
|
|
|
|
* 3 Jan 001 3 2
|
|
|
|
* ...
|
|
|
|
* 31 Dec 400 DI400Y DI400Y -1
|
|
|
|
* 1 Jan 401 DI400Y +1 DI400Y 400-year boundary
|
|
|
|
*/
|
|
|
|
assert(ordinal >= 1);
|
|
|
|
--ordinal;
|
|
|
|
n400 = ordinal / DI400Y;
|
|
|
|
n = ordinal % DI400Y;
|
|
|
|
*year = n400 * 400 + 1;
|
|
|
|
|
|
|
|
/* Now n is the (non-negative) offset, in days, from January 1 of
|
|
|
|
* year, to the desired date. Now compute how many 100-year cycles
|
|
|
|
* precede n.
|
|
|
|
* Note that it's possible for n100 to equal 4! In that case 4 full
|
|
|
|
* 100-year cycles precede the desired day, which implies the
|
|
|
|
* desired day is December 31 at the end of a 400-year cycle.
|
|
|
|
*/
|
|
|
|
n100 = n / DI100Y;
|
|
|
|
n = n % DI100Y;
|
|
|
|
|
|
|
|
/* Now compute how many 4-year cycles precede it. */
|
|
|
|
n4 = n / DI4Y;
|
|
|
|
n = n % DI4Y;
|
|
|
|
|
|
|
|
/* And now how many single years. Again n1 can be 4, and again
|
|
|
|
* meaning that the desired day is December 31 at the end of the
|
|
|
|
* 4-year cycle.
|
|
|
|
*/
|
|
|
|
n1 = n / 365;
|
|
|
|
n = n % 365;
|
|
|
|
|
|
|
|
*year += n100 * 100 + n4 * 4 + n1;
|
|
|
|
if (n1 == 4 || n100 == 4) {
|
|
|
|
assert(n == 0);
|
|
|
|
*year -= 1;
|
|
|
|
*month = 12;
|
|
|
|
*day = 31;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now the year is correct, and n is the offset from January 1. We
|
|
|
|
* find the month via an estimate that's either exact or one too
|
|
|
|
* large.
|
|
|
|
*/
|
|
|
|
leapyear = n1 == 3 && (n4 != 24 || n100 == 3);
|
|
|
|
assert(leapyear == is_leap(*year));
|
|
|
|
*month = (n + 50) >> 5;
|
|
|
|
preceding = (_days_before_month[*month] + (*month > 2 && leapyear));
|
|
|
|
if (preceding > n) {
|
|
|
|
/* estimate is too large */
|
|
|
|
*month -= 1;
|
|
|
|
preceding -= days_in_month(*year, *month);
|
|
|
|
}
|
|
|
|
n -= preceding;
|
|
|
|
assert(0 <= n);
|
|
|
|
assert(n < days_in_month(*year, *month));
|
|
|
|
|
|
|
|
*day = n + 1;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* year, month, day -> ordinal, considering 01-Jan-0001 as day 1. */
|
|
|
|
static int
|
|
|
|
ymd_to_ord(int year, int month, int day)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return days_before_year(year) + days_before_month(year, month) + day;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Day of week, where Monday==0, ..., Sunday==6. 1/1/1 was a Monday. */
|
|
|
|
static int
|
|
|
|
weekday(int year, int month, int day)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return (ymd_to_ord(year, month, day) + 6) % 7;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ordinal of the Monday starting week 1 of the ISO year. Week 1 is the
|
|
|
|
* first calendar week containing a Thursday.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
iso_week1_monday(int year)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int first_day = ymd_to_ord(year, 1, 1); /* ord of 1/1 */
|
|
|
|
/* 0 if 1/1 is a Monday, 1 if a Tue, etc. */
|
|
|
|
int first_weekday = (first_day + 6) % 7;
|
|
|
|
/* ordinal of closest Monday at or before 1/1 */
|
|
|
|
int week1_monday = first_day - first_weekday;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (first_weekday > 3) /* if 1/1 was Fri, Sat, Sun */
|
|
|
|
week1_monday += 7;
|
|
|
|
return week1_monday;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
static int
|
|
|
|
iso_to_ymd(const int iso_year, const int iso_week, const int iso_day,
|
|
|
|
int *year, int *month, int *day) {
|
|
|
|
if (iso_week <= 0 || iso_week >= 53) {
|
|
|
|
int out_of_range = 1;
|
|
|
|
if (iso_week == 53) {
|
|
|
|
// ISO years have 53 weeks in it on years starting with a Thursday
|
|
|
|
// and on leap years starting on Wednesday
|
|
|
|
int first_weekday = weekday(iso_year, 1, 1);
|
|
|
|
if (first_weekday == 3 || (first_weekday == 2 && is_leap(iso_year))) {
|
|
|
|
out_of_range = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out_of_range) {
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iso_day <= 0 || iso_day >= 8) {
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert (Y, W, D) to (Y, M, D) in-place
|
|
|
|
int day_1 = iso_week1_monday(iso_year);
|
|
|
|
|
|
|
|
int day_offset = (iso_week - 1)*7 + iso_day - 1;
|
|
|
|
|
|
|
|
ord_to_ymd(day_1 + day_offset, year, month, day);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Range checkers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Check that -MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS. If so, return 0.
|
|
|
|
* If not, raise OverflowError and return -1.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
check_delta_day_range(int days)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (-MAX_DELTA_DAYS <= days && days <= MAX_DELTA_DAYS)
|
|
|
|
return 0;
|
|
|
|
PyErr_Format(PyExc_OverflowError,
|
|
|
|
"days=%d; must have magnitude <= %d",
|
|
|
|
days, MAX_DELTA_DAYS);
|
|
|
|
return -1;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that date arguments are in range. Return 0 if they are. If they
|
|
|
|
* aren't, raise ValueError and return -1.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
check_date_args(int year, int month, int day)
|
|
|
|
{
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (year < MINYEAR || year > MAXYEAR) {
|
2017-02-10 05:34:02 -04:00
|
|
|
PyErr_Format(PyExc_ValueError, "year %i is out of range", year);
|
2010-05-09 12:52:27 -03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (month < 1 || month > 12) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"month must be in 1..12");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (day < 1 || day > days_in_month(year, month)) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"day is out of range for month");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that time arguments are in range. Return 0 if they are. If they
|
|
|
|
* aren't, raise ValueError and return -1.
|
|
|
|
*/
|
|
|
|
static int
|
2016-08-08 18:05:40 -03:00
|
|
|
check_time_args(int h, int m, int s, int us, int fold)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (h < 0 || h > 23) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"hour must be in 0..23");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (m < 0 || m > 59) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"minute must be in 0..59");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (s < 0 || s > 59) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"second must be in 0..59");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (us < 0 || us > 999999) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"microsecond must be in 0..999999");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-08-08 18:05:40 -03:00
|
|
|
if (fold != 0 && fold != 1) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"fold must be either 0 or 1");
|
|
|
|
return -1;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
return 0;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Normalization utilities.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* One step of a mixed-radix conversion. A "hi" unit is equivalent to
|
|
|
|
* factor "lo" units. factor must be > 0. If *lo is less than 0, or
|
|
|
|
* at least factor, enough of *lo is converted into "hi" units so that
|
|
|
|
* 0 <= *lo < factor. The input values must be such that int overflow
|
|
|
|
* is impossible.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
normalize_pair(int *hi, int *lo, int factor)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(factor > 0);
|
|
|
|
assert(lo != hi);
|
|
|
|
if (*lo < 0 || *lo >= factor) {
|
|
|
|
const int num_hi = divmod(*lo, factor, lo);
|
|
|
|
const int new_hi = *hi + num_hi;
|
|
|
|
assert(! SIGNED_ADD_OVERFLOWED(new_hi, *hi, num_hi));
|
|
|
|
*hi = new_hi;
|
|
|
|
}
|
|
|
|
assert(0 <= *lo && *lo < factor);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fiddle days (d), seconds (s), and microseconds (us) so that
|
2010-05-09 12:52:27 -03:00
|
|
|
* 0 <= *s < 24*3600
|
|
|
|
* 0 <= *us < 1000000
|
2002-12-16 16:18:38 -04:00
|
|
|
* The input values must be such that the internals don't overflow.
|
|
|
|
* The way this routine is used, we don't get close.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
normalize_d_s_us(int *d, int *s, int *us)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (*us < 0 || *us >= 1000000) {
|
|
|
|
normalize_pair(s, us, 1000000);
|
|
|
|
/* |s| can't be bigger than about
|
|
|
|
* |original s| + |original us|/1000000 now.
|
|
|
|
*/
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
if (*s < 0 || *s >= 24*3600) {
|
|
|
|
normalize_pair(d, s, 24*3600);
|
|
|
|
/* |d| can't be bigger than about
|
|
|
|
* |original d| +
|
|
|
|
* (|original s| + |original us|/1000000) / (24*3600) now.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
assert(0 <= *s && *s < 24*3600);
|
|
|
|
assert(0 <= *us && *us < 1000000);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fiddle years (y), months (m), and days (d) so that
|
2010-05-09 12:52:27 -03:00
|
|
|
* 1 <= *m <= 12
|
|
|
|
* 1 <= *d <= days_in_month(*y, *m)
|
2002-12-16 16:18:38 -04:00
|
|
|
* The input values must be such that the internals don't overflow.
|
|
|
|
* The way this routine is used, we don't get close.
|
|
|
|
*/
|
2010-05-27 18:42:58 -03:00
|
|
|
static int
|
2002-12-16 16:18:38 -04:00
|
|
|
normalize_y_m_d(int *y, int *m, int *d)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int dim; /* # of days in month */
|
|
|
|
|
2010-10-13 19:54:34 -03:00
|
|
|
/* In actual use, m is always the month component extracted from a
|
|
|
|
* date/datetime object. Therefore it is always in [1, 12] range.
|
2010-05-09 12:52:27 -03:00
|
|
|
*/
|
2010-10-13 19:54:34 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(1 <= *m && *m <= 12);
|
|
|
|
|
|
|
|
/* Now only day can be out of bounds (year may also be out of bounds
|
|
|
|
* for a datetime object, but we don't care about that here).
|
|
|
|
* If day is out of bounds, what to do is arguable, but at least the
|
|
|
|
* method here is principled and explainable.
|
|
|
|
*/
|
|
|
|
dim = days_in_month(*y, *m);
|
|
|
|
if (*d < 1 || *d > dim) {
|
|
|
|
/* Move day-1 days from the first of the month. First try to
|
|
|
|
* get off cheap if we're only one day out of range
|
|
|
|
* (adjustments for timezone alone can't be worse than that).
|
|
|
|
*/
|
|
|
|
if (*d == 0) {
|
|
|
|
--*m;
|
|
|
|
if (*m > 0)
|
|
|
|
*d = days_in_month(*y, *m);
|
|
|
|
else {
|
|
|
|
--*y;
|
|
|
|
*m = 12;
|
|
|
|
*d = 31;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (*d == dim + 1) {
|
|
|
|
/* move forward a day */
|
|
|
|
++*m;
|
|
|
|
*d = 1;
|
|
|
|
if (*m > 12) {
|
|
|
|
*m = 1;
|
|
|
|
++*y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int ordinal = ymd_to_ord(*y, *m, 1) +
|
|
|
|
*d - 1;
|
2010-05-27 18:42:58 -03:00
|
|
|
if (ordinal < 1 || ordinal > MAXORDINAL) {
|
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
ord_to_ymd(ordinal, y, m, d);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(*m > 0);
|
|
|
|
assert(*d > 0);
|
2010-05-27 18:42:58 -03:00
|
|
|
if (MINYEAR <= *y && *y <= MAXYEAR)
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
|
|
"date value out of range");
|
|
|
|
return -1;
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fiddle out-of-bounds months and days so that the result makes some kind
|
|
|
|
* of sense. The parameters are both inputs and outputs. Returns < 0 on
|
|
|
|
* failure, where failure means the adjusted year is out of bounds.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
normalize_date(int *year, int *month, int *day)
|
|
|
|
{
|
2010-05-27 18:42:58 -03:00
|
|
|
return normalize_y_m_d(year, month, day);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Force all the datetime fields into range. The parameters are both
|
|
|
|
* inputs and outputs. Returns < 0 on error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
normalize_datetime(int *year, int *month, int *day,
|
|
|
|
int *hour, int *minute, int *second,
|
|
|
|
int *microsecond)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
normalize_pair(second, microsecond, 1000000);
|
|
|
|
normalize_pair(minute, second, 60);
|
|
|
|
normalize_pair(hour, minute, 60);
|
|
|
|
normalize_pair(day, hour, 24);
|
|
|
|
return normalize_date(year, month, day);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-05-17 12:57:00 -03:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Basic object allocation: tp_alloc implementations. These allocate
|
|
|
|
* Python objects of the right size and type, and do the Python object-
|
|
|
|
* initialization bit. If there's not enough memory, they return NULL after
|
|
|
|
* setting MemoryError. All data members remain uninitialized trash.
|
|
|
|
*
|
|
|
|
* We abuse the tp_alloc "nitems" argument to communicate whether a tzinfo
|
2003-05-17 23:24:46 -03:00
|
|
|
* member is needed. This is ugly, imprecise, and possibly insecure.
|
|
|
|
* tp_basicsize for the time and datetime types is set to the size of the
|
|
|
|
* struct that has room for the tzinfo member, so subclasses in Python will
|
|
|
|
* allocate enough space for a tzinfo member whether or not one is actually
|
|
|
|
* needed. That's the "ugly and imprecise" parts. The "possibly insecure"
|
|
|
|
* part is that PyType_GenericAlloc() (which subclasses in Python end up
|
|
|
|
* using) just happens today to effectively ignore the nitems argument
|
|
|
|
* when tp_itemsize is 0, which it is for these type objects. If that
|
|
|
|
* changes, perhaps the callers of tp_alloc slots in this file should
|
|
|
|
* be changed to force a 0 nitems argument unless the type being allocated
|
|
|
|
* is a base type implemented in this file (so that tp_alloc is time_alloc
|
|
|
|
* or datetime_alloc below, which know about the nitems abuse).
|
2003-05-17 12:57:00 -03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static PyObject *
|
2006-02-15 13:27:45 -04:00
|
|
|
time_alloc(PyTypeObject *type, Py_ssize_t aware)
|
2003-05-17 12:57:00 -03:00
|
|
|
{
|
2020-06-15 20:28:07 -03:00
|
|
|
size_t size = aware ? sizeof(PyDateTime_Time) : sizeof(_PyDateTime_BaseTime);
|
|
|
|
PyObject *self = (PyObject *)PyObject_Malloc(size);
|
|
|
|
if (self == NULL) {
|
|
|
|
return PyErr_NoMemory();
|
|
|
|
}
|
|
|
|
_PyObject_Init(self, type);
|
2010-05-09 12:52:27 -03:00
|
|
|
return self;
|
2003-05-17 12:57:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2006-02-15 13:27:45 -04:00
|
|
|
datetime_alloc(PyTypeObject *type, Py_ssize_t aware)
|
2003-05-17 12:57:00 -03:00
|
|
|
{
|
2020-06-15 20:28:07 -03:00
|
|
|
size_t size = aware ? sizeof(PyDateTime_DateTime) : sizeof(_PyDateTime_BaseDateTime);
|
|
|
|
PyObject *self = (PyObject *)PyObject_Malloc(size);
|
|
|
|
if (self == NULL) {
|
|
|
|
return PyErr_NoMemory();
|
|
|
|
}
|
|
|
|
_PyObject_Init(self, type);
|
2010-05-09 12:52:27 -03:00
|
|
|
return self;
|
2003-05-17 12:57:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Helpers for setting object fields. These work on pointers to the
|
|
|
|
* appropriate base class.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* For date and datetime. */
|
|
|
|
static void
|
|
|
|
set_date_fields(PyDateTime_Date *self, int y, int m, int d)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
self->hashcode = -1;
|
|
|
|
SET_YEAR(self, y);
|
|
|
|
SET_MONTH(self, m);
|
|
|
|
SET_DAY(self, d);
|
2003-05-17 12:57:00 -03:00
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* String parsing utilities and helper functions
|
|
|
|
*/
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
static unsigned char
|
|
|
|
is_digit(const char c) {
|
|
|
|
return ((unsigned int)(c - '0')) < 10;
|
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
static const char *
|
|
|
|
parse_digits(const char *ptr, int *var, size_t num_digits)
|
2017-12-21 01:33:49 -04:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < num_digits; ++i) {
|
|
|
|
unsigned int tmp = (unsigned int)(*(ptr++) - '0');
|
|
|
|
if (tmp > 9) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*var *= 10;
|
|
|
|
*var += (signed int)tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
static int
|
2022-05-05 21:31:24 -03:00
|
|
|
parse_isoformat_date(const char *dtstr, const size_t len, int *year, int *month, int *day)
|
2018-10-22 13:32:52 -03:00
|
|
|
{
|
2017-12-21 01:33:49 -04:00
|
|
|
/* Parse the date components of the result of date.isoformat()
|
2018-10-22 13:32:52 -03:00
|
|
|
*
|
|
|
|
* Return codes:
|
|
|
|
* 0: Success
|
|
|
|
* -1: Failed to parse date component
|
2022-05-05 21:31:24 -03:00
|
|
|
* -2: Inconsistent date separator usage
|
|
|
|
* -3: Failed to parse ISO week.
|
|
|
|
* -4: Failed to parse ISO day.
|
|
|
|
* -5, -6: Failure in iso_to_ymd
|
2018-10-22 13:32:52 -03:00
|
|
|
*/
|
2017-12-21 01:33:49 -04:00
|
|
|
const char *p = dtstr;
|
|
|
|
p = parse_digits(p, year, 4);
|
|
|
|
if (NULL == p) {
|
|
|
|
return -1;
|
|
|
|
}
|
2018-01-15 05:45:49 -04:00
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
const unsigned char uses_separator = (*p == '-');
|
|
|
|
if (uses_separator) {
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*p == 'W') {
|
|
|
|
// This is an isocalendar-style date string
|
|
|
|
p++;
|
|
|
|
int iso_week = 0;
|
|
|
|
int iso_day = 0;
|
|
|
|
|
|
|
|
p = parse_digits(p, &iso_week, 2);
|
|
|
|
if (NULL == p) {
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(p > dtstr);
|
|
|
|
if ((size_t)(p - dtstr) < len) {
|
|
|
|
if (uses_separator && *(p++) != '-') {
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = parse_digits(p, &iso_day, 1);
|
|
|
|
if (NULL == p) {
|
|
|
|
return -4;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
iso_day = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rv = iso_to_ymd(*year, iso_week, iso_day, year, month, day);
|
|
|
|
if (rv) {
|
|
|
|
return -3 + rv;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
p = parse_digits(p, month, 2);
|
|
|
|
if (NULL == p) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
if (uses_separator && *(p++) != '-') {
|
2017-12-21 01:33:49 -04:00
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
p = parse_digits(p, day, 2);
|
|
|
|
if (p == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-10-22 13:32:52 -03:00
|
|
|
parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
|
|
|
|
int *minute, int *second, int *microsecond)
|
|
|
|
{
|
2022-05-05 21:31:24 -03:00
|
|
|
*hour = *minute = *second = *microsecond = 0;
|
2017-12-21 01:33:49 -04:00
|
|
|
const char *p = tstr;
|
|
|
|
const char *p_end = tstr_end;
|
|
|
|
int *vals[3] = {hour, minute, second};
|
2022-05-05 21:31:24 -03:00
|
|
|
// This is initialized to satisfy an erroneous compiler warning.
|
|
|
|
unsigned char has_separator = 1;
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// Parse [HH[:?MM[:?SS]]]
|
2017-12-21 01:33:49 -04:00
|
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
|
|
p = parse_digits(p, vals[i], 2);
|
|
|
|
if (NULL == p) {
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
char c = *(p++);
|
2022-05-05 21:31:24 -03:00
|
|
|
if (i == 0) {
|
|
|
|
has_separator = (c == ':');
|
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
if (p >= p_end) {
|
|
|
|
return c != '\0';
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
2022-05-05 21:31:24 -03:00
|
|
|
else if (has_separator && (c == ':')) {
|
2017-12-21 01:33:49 -04:00
|
|
|
continue;
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
2022-05-05 21:31:24 -03:00
|
|
|
else if (c == '.' || c == ',') {
|
2017-12-21 01:33:49 -04:00
|
|
|
break;
|
2022-05-05 21:31:24 -03:00
|
|
|
} else if (!has_separator) {
|
|
|
|
--p;
|
|
|
|
} else {
|
2018-10-22 13:32:52 -03:00
|
|
|
return -4; // Malformed time separator
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// Parse fractional components
|
2017-12-21 01:33:49 -04:00
|
|
|
size_t len_remains = p_end - p;
|
2022-05-05 21:31:24 -03:00
|
|
|
size_t to_parse = len_remains;
|
|
|
|
if (len_remains >= 6) {
|
|
|
|
to_parse = 6;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
p = parse_digits(p, microsecond, to_parse);
|
2017-12-21 01:33:49 -04:00
|
|
|
if (NULL == p) {
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
static int correction[] = {
|
|
|
|
100000, 10000, 1000, 100, 10
|
|
|
|
};
|
|
|
|
|
|
|
|
if (to_parse < 6) {
|
|
|
|
*microsecond *= correction[to_parse-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
while (is_digit(*p)){
|
|
|
|
++p; // skip truncated digits
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return 1 if it's not the end of the string
|
|
|
|
return *p != '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-10-22 13:32:52 -03:00
|
|
|
parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
|
|
|
|
int *second, int *microsecond, int *tzoffset,
|
|
|
|
int *tzmicrosecond)
|
|
|
|
{
|
2017-12-21 01:33:49 -04:00
|
|
|
// Parse the time portion of a datetime.isoformat() string
|
|
|
|
//
|
|
|
|
// Return codes:
|
|
|
|
// 0: Success (no tzoffset)
|
|
|
|
// 1: Success (with tzoffset)
|
|
|
|
// -3: Failed to parse time component
|
|
|
|
// -4: Failed to parse time separator
|
|
|
|
// -5: Malformed timezone string
|
|
|
|
|
|
|
|
const char *p = dtstr;
|
|
|
|
const char *p_end = dtstr + dtlen;
|
|
|
|
|
|
|
|
const char *tzinfo_pos = p;
|
|
|
|
do {
|
2022-05-05 21:31:24 -03:00
|
|
|
if (*tzinfo_pos == 'Z' || *tzinfo_pos == '+' || *tzinfo_pos == '-') {
|
2017-12-21 01:33:49 -04:00
|
|
|
break;
|
|
|
|
}
|
2018-10-22 13:32:52 -03:00
|
|
|
} while (++tzinfo_pos < p_end);
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
int rv = parse_hh_mm_ss_ff(dtstr, tzinfo_pos, hour, minute, second,
|
|
|
|
microsecond);
|
2017-12-21 01:33:49 -04:00
|
|
|
|
|
|
|
if (rv < 0) {
|
|
|
|
return rv;
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else if (tzinfo_pos == p_end) {
|
2017-12-21 01:33:49 -04:00
|
|
|
// We know that there's no time zone, so if there's stuff at the
|
|
|
|
// end of the string it's an error.
|
|
|
|
if (rv == 1) {
|
|
|
|
return -5;
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else {
|
2017-12-21 01:33:49 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// Special case UTC / Zulu time.
|
|
|
|
if (*tzinfo_pos == 'Z') {
|
|
|
|
*tzoffset = 0;
|
|
|
|
*tzmicrosecond = 0;
|
|
|
|
|
|
|
|
if (*(tzinfo_pos + 1) != '\0') {
|
|
|
|
return -5;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
int tzsign = (*tzinfo_pos == '-') ? -1 : 1;
|
2017-12-21 01:33:49 -04:00
|
|
|
tzinfo_pos++;
|
|
|
|
int tzhour = 0, tzminute = 0, tzsecond = 0;
|
2018-10-22 13:32:52 -03:00
|
|
|
rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,
|
|
|
|
tzmicrosecond);
|
2017-12-21 01:33:49 -04:00
|
|
|
|
|
|
|
*tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond);
|
|
|
|
*tzmicrosecond *= tzsign;
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
return rv ? -5 : 1;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2003-05-17 12:57:00 -03:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Create various objects, mostly without range checking.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Create a date instance with no range checking. */
|
|
|
|
static PyObject *
|
|
|
|
new_date_ex(int year, int month, int day, PyTypeObject *type)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDateTime_Date *self;
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2017-02-10 05:34:02 -04:00
|
|
|
if (check_date_args(year, month, day) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
self = (PyDateTime_Date *)(type->tp_alloc(type, 0));
|
2010-05-09 12:52:27 -03:00
|
|
|
if (self != NULL)
|
|
|
|
set_date_fields(self, year, month, day);
|
2018-10-22 13:32:52 -03:00
|
|
|
return (PyObject *)self;
|
2003-05-17 12:57:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
#define new_date(year, month, day) \
|
2010-05-09 12:52:27 -03:00
|
|
|
new_date_ex(year, month, day, &PyDateTime_DateType)
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2018-01-16 14:06:31 -04:00
|
|
|
// Forward declaration
|
2018-10-22 13:32:52 -03:00
|
|
|
static PyObject *
|
|
|
|
new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *);
|
2018-01-16 14:06:31 -04:00
|
|
|
|
|
|
|
/* Create date instance with no range checking, or call subclass constructor */
|
|
|
|
static PyObject *
|
2018-10-22 13:32:52 -03:00
|
|
|
new_date_subclass_ex(int year, int month, int day, PyObject *cls)
|
|
|
|
{
|
2018-01-16 14:06:31 -04:00
|
|
|
PyObject *result;
|
|
|
|
// We have "fast path" constructors for two subclasses: date and datetime
|
|
|
|
if ((PyTypeObject *)cls == &PyDateTime_DateType) {
|
|
|
|
result = new_date_ex(year, month, day, (PyTypeObject *)cls);
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) {
|
2018-01-16 14:06:31 -04:00
|
|
|
result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None,
|
|
|
|
(PyTypeObject *)cls);
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else {
|
2018-01-16 14:06:31 -04:00
|
|
|
result = PyObject_CallFunction(cls, "iii", year, month, day);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-05-17 12:57:00 -03:00
|
|
|
/* Create a datetime instance with no range checking. */
|
|
|
|
static PyObject *
|
2016-07-22 19:47:04 -03:00
|
|
|
new_datetime_ex2(int year, int month, int day, int hour, int minute,
|
|
|
|
int second, int usecond, PyObject *tzinfo, int fold, PyTypeObject *type)
|
2010-05-09 12:52:27 -03:00
|
|
|
{
|
|
|
|
PyDateTime_DateTime *self;
|
|
|
|
char aware = tzinfo != Py_None;
|
|
|
|
|
2017-02-10 05:34:02 -04:00
|
|
|
if (check_date_args(year, month, day) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (check_time_args(hour, minute, second, usecond, fold) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (check_tzinfo_subclass(tzinfo) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
self = (PyDateTime_DateTime *) (type->tp_alloc(type, aware));
|
|
|
|
if (self != NULL) {
|
|
|
|
self->hastzinfo = aware;
|
|
|
|
set_date_fields((PyDateTime_Date *)self, year, month, day);
|
|
|
|
DATE_SET_HOUR(self, hour);
|
|
|
|
DATE_SET_MINUTE(self, minute);
|
|
|
|
DATE_SET_SECOND(self, second);
|
|
|
|
DATE_SET_MICROSECOND(self, usecond);
|
|
|
|
if (aware) {
|
|
|
|
Py_INCREF(tzinfo);
|
|
|
|
self->tzinfo = tzinfo;
|
|
|
|
}
|
2016-07-22 19:47:04 -03:00
|
|
|
DATE_SET_FOLD(self, fold);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return (PyObject *)self;
|
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
new_datetime_ex(int year, int month, int day, int hour, int minute,
|
|
|
|
int second, int usecond, PyObject *tzinfo, PyTypeObject *type)
|
|
|
|
{
|
|
|
|
return new_datetime_ex2(year, month, day, hour, minute, second, usecond,
|
|
|
|
tzinfo, 0, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define new_datetime(y, m, d, hh, mm, ss, us, tzinfo, fold) \
|
|
|
|
new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, \
|
2010-05-09 12:52:27 -03:00
|
|
|
&PyDateTime_DateTimeType)
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2018-01-16 14:06:31 -04:00
|
|
|
static PyObject *
|
|
|
|
new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute,
|
|
|
|
int second, int usecond, PyObject *tzinfo,
|
|
|
|
int fold, PyObject *cls) {
|
|
|
|
PyObject* dt;
|
|
|
|
if ((PyTypeObject*)cls == &PyDateTime_DateTimeType) {
|
|
|
|
// Use the fast path constructor
|
|
|
|
dt = new_datetime(year, month, day, hour, minute, second, usecond,
|
|
|
|
tzinfo, fold);
|
|
|
|
} else {
|
|
|
|
// Subclass
|
|
|
|
dt = PyObject_CallFunction(cls, "iiiiiiiO",
|
|
|
|
year,
|
|
|
|
month,
|
|
|
|
day,
|
|
|
|
hour,
|
|
|
|
minute,
|
|
|
|
second,
|
|
|
|
usecond,
|
|
|
|
tzinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
new_datetime_subclass_ex(int year, int month, int day, int hour, int minute,
|
|
|
|
int second, int usecond, PyObject *tzinfo,
|
|
|
|
PyObject *cls) {
|
|
|
|
return new_datetime_subclass_fold_ex(year, month, day, hour, minute,
|
|
|
|
second, usecond, tzinfo, 0,
|
|
|
|
cls);
|
|
|
|
}
|
|
|
|
|
2003-05-17 12:57:00 -03:00
|
|
|
/* Create a time instance with no range checking. */
|
|
|
|
static PyObject *
|
2016-07-22 19:47:04 -03:00
|
|
|
new_time_ex2(int hour, int minute, int second, int usecond,
|
|
|
|
PyObject *tzinfo, int fold, PyTypeObject *type)
|
2010-05-09 12:52:27 -03:00
|
|
|
{
|
|
|
|
PyDateTime_Time *self;
|
|
|
|
char aware = tzinfo != Py_None;
|
|
|
|
|
2017-02-10 05:34:02 -04:00
|
|
|
if (check_time_args(hour, minute, second, usecond, fold) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (check_tzinfo_subclass(tzinfo) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
self = (PyDateTime_Time *) (type->tp_alloc(type, aware));
|
|
|
|
if (self != NULL) {
|
|
|
|
self->hastzinfo = aware;
|
|
|
|
self->hashcode = -1;
|
|
|
|
TIME_SET_HOUR(self, hour);
|
|
|
|
TIME_SET_MINUTE(self, minute);
|
|
|
|
TIME_SET_SECOND(self, second);
|
|
|
|
TIME_SET_MICROSECOND(self, usecond);
|
|
|
|
if (aware) {
|
|
|
|
Py_INCREF(tzinfo);
|
|
|
|
self->tzinfo = tzinfo;
|
|
|
|
}
|
2016-07-22 19:47:04 -03:00
|
|
|
TIME_SET_FOLD(self, fold);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return (PyObject *)self;
|
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
new_time_ex(int hour, int minute, int second, int usecond,
|
|
|
|
PyObject *tzinfo, PyTypeObject *type)
|
|
|
|
{
|
|
|
|
return new_time_ex2(hour, minute, second, usecond, tzinfo, 0, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define new_time(hh, mm, ss, us, tzinfo, fold) \
|
|
|
|
new_time_ex2(hh, mm, ss, us, tzinfo, fold, &PyDateTime_TimeType)
|
2003-05-17 12:57:00 -03:00
|
|
|
|
|
|
|
/* Create a timedelta instance. Normalize the members iff normalize is
|
|
|
|
* true. Passing false is a speed optimization, if you know for sure
|
|
|
|
* that seconds and microseconds are already in their proper ranges. In any
|
|
|
|
* case, raises OverflowError and returns NULL if the normalized days is out
|
2021-09-21 20:09:00 -03:00
|
|
|
* of range.
|
2003-05-17 12:57:00 -03:00
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
new_delta_ex(int days, int seconds, int microseconds, int normalize,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyTypeObject *type)
|
2003-05-17 12:57:00 -03:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDateTime_Delta *self;
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (normalize)
|
|
|
|
normalize_d_s_us(&days, &seconds, µseconds);
|
|
|
|
assert(0 <= seconds && seconds < 24*3600);
|
|
|
|
assert(0 <= microseconds && microseconds < 1000000);
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (check_delta_day_range(days) < 0)
|
|
|
|
return NULL;
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
self = (PyDateTime_Delta *) (type->tp_alloc(type, 0));
|
|
|
|
if (self != NULL) {
|
|
|
|
self->hashcode = -1;
|
|
|
|
SET_TD_DAYS(self, days);
|
|
|
|
SET_TD_SECONDS(self, seconds);
|
|
|
|
SET_TD_MICROSECONDS(self, microseconds);
|
|
|
|
}
|
|
|
|
return (PyObject *) self;
|
2003-05-17 12:57:00 -03:00
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
#define new_delta(d, s, us, normalize) \
|
|
|
|
new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType)
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
PyObject_HEAD
|
|
|
|
PyObject *offset;
|
|
|
|
PyObject *name;
|
|
|
|
} PyDateTime_TimeZone;
|
|
|
|
|
2011-03-21 14:15:42 -03:00
|
|
|
/* The interned UTC timezone instance */
|
2010-10-14 14:03:51 -03:00
|
|
|
static PyObject *PyDateTime_TimeZone_UTC;
|
2012-06-08 13:33:09 -03:00
|
|
|
/* The interned Epoch datetime instance */
|
|
|
|
static PyObject *PyDateTime_Epoch;
|
2010-07-06 20:19:45 -03:00
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
/* Create new timezone instance checking offset range. This
|
|
|
|
function does not check the name argument. Caller must assure
|
|
|
|
that offset is a timedelta instance and name is either NULL
|
|
|
|
or a unicode object. */
|
|
|
|
static PyObject *
|
2010-10-14 14:03:51 -03:00
|
|
|
create_timezone(PyObject *offset, PyObject *name)
|
2010-06-14 11:15:50 -03:00
|
|
|
{
|
|
|
|
PyDateTime_TimeZone *self;
|
|
|
|
PyTypeObject *type = &PyDateTime_TimeZoneType;
|
|
|
|
|
|
|
|
assert(offset != NULL);
|
|
|
|
assert(PyDelta_Check(offset));
|
|
|
|
assert(name == NULL || PyUnicode_Check(name));
|
|
|
|
|
2010-10-14 14:03:51 -03:00
|
|
|
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
|
|
|
|
if (self == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_INCREF(offset);
|
|
|
|
self->offset = offset;
|
|
|
|
Py_XINCREF(name);
|
|
|
|
self->name = name;
|
|
|
|
return (PyObject *)self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int delta_bool(PyDateTime_Delta *self);
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
new_timezone(PyObject *offset, PyObject *name)
|
|
|
|
{
|
|
|
|
assert(offset != NULL);
|
|
|
|
assert(PyDelta_Check(offset));
|
|
|
|
assert(name == NULL || PyUnicode_Check(name));
|
|
|
|
|
|
|
|
if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) {
|
|
|
|
Py_INCREF(PyDateTime_TimeZone_UTC);
|
|
|
|
return PyDateTime_TimeZone_UTC;
|
|
|
|
}
|
2019-08-09 11:22:16 -03:00
|
|
|
if ((GET_TD_DAYS(offset) == -1 &&
|
|
|
|
GET_TD_SECONDS(offset) == 0 &&
|
|
|
|
GET_TD_MICROSECONDS(offset) < 1) ||
|
2010-06-14 11:15:50 -03:00
|
|
|
GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
|
|
|
|
" strictly between -timedelta(hours=24) and"
|
2012-06-22 14:23:21 -03:00
|
|
|
" timedelta(hours=24),"
|
|
|
|
" not %R.", offset);
|
2010-06-14 11:15:50 -03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-10-14 14:03:51 -03:00
|
|
|
return create_timezone(offset, name);
|
2010-06-14 11:15:50 -03:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* tzinfo helpers.
|
|
|
|
*/
|
|
|
|
|
2002-12-21 23:43:39 -04:00
|
|
|
/* Ensure that p is None or of a tzinfo subclass. Return 0 if OK; if not
|
|
|
|
* raise TypeError and return -1.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
check_tzinfo_subclass(PyObject *p)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (p == Py_None || PyTZInfo_Check(p))
|
|
|
|
return 0;
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"tzinfo argument must be None or of a tzinfo subclass, "
|
|
|
|
"not type '%s'",
|
|
|
|
Py_TYPE(p)->tp_name);
|
|
|
|
return -1;
|
2002-12-21 23:43:39 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* If self has a tzinfo member, return a BORROWED reference to it. Else
|
|
|
|
* return NULL, which is NOT AN ERROR. There are no error returns here,
|
|
|
|
* and the caller must not decref the result.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
get_tzinfo_member(PyObject *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo = NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDateTime_Check(self) && HASTZINFO(self))
|
|
|
|
tzinfo = ((PyDateTime_DateTime *)self)->tzinfo;
|
|
|
|
else if (PyTime_Check(self) && HASTZINFO(self))
|
|
|
|
tzinfo = ((PyDateTime_Time *)self)->tzinfo;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return tzinfo;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
/* Call getattr(tzinfo, name)(tzinfoarg), and check the result. tzinfo must
|
|
|
|
* be an instance of the tzinfo class. If the method returns None, this
|
|
|
|
* returns None. If the method doesn't return None or timedelta, TypeError is
|
|
|
|
* raised and this returns NULL. If it returns a timedelta and the value is
|
|
|
|
* out of range or isn't a whole number of minutes, ValueError is raised and
|
|
|
|
* this returns NULL. Else result is returned.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
2010-07-07 20:56:38 -03:00
|
|
|
static PyObject *
|
2015-12-25 14:01:53 -04:00
|
|
|
call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg)
|
2010-05-09 12:52:27 -03:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *offset;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
assert(tzinfo != NULL);
|
2010-07-07 20:56:38 -03:00
|
|
|
assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None);
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(tzinfoarg != NULL);
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (tzinfo == Py_None)
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg);
|
|
|
|
if (offset == Py_None || offset == NULL)
|
|
|
|
return offset;
|
|
|
|
if (PyDelta_Check(offset)) {
|
2019-08-09 11:22:16 -03:00
|
|
|
if ((GET_TD_DAYS(offset) == -1 &&
|
|
|
|
GET_TD_SECONDS(offset) == 0 &&
|
|
|
|
GET_TD_MICROSECONDS(offset) < 1) ||
|
2010-07-07 20:56:38 -03:00
|
|
|
GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
|
|
|
|
" strictly between -timedelta(hours=24) and"
|
|
|
|
" timedelta(hours=24).");
|
|
|
|
return NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"tzinfo.%s() must return None or "
|
2010-07-07 20:56:38 -03:00
|
|
|
"timedelta, not '%.200s'",
|
|
|
|
name, Py_TYPE(offset)->tp_name);
|
2014-07-25 18:59:48 -03:00
|
|
|
Py_DECREF(offset);
|
2010-07-07 20:56:38 -03:00
|
|
|
return NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
return offset;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call tzinfo.utcoffset(tzinfoarg), and extract an integer from the
|
|
|
|
* result. tzinfo must be an instance of the tzinfo class. If utcoffset()
|
|
|
|
* returns None, call_utcoffset returns 0 and sets *none to 1. If uctoffset()
|
2003-01-02 17:28:08 -04:00
|
|
|
* doesn't return None or timedelta, TypeError is raised and this returns -1.
|
2017-07-31 11:26:50 -03:00
|
|
|
* If utcoffset() returns an out of range timedelta,
|
|
|
|
* ValueError is raised and this returns -1. Else *none is
|
|
|
|
* set to 0 and the offset is returned (as timedelta, positive east of UTC).
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
2002-12-21 23:43:39 -04:00
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg)
|
|
|
|
{
|
|
|
|
return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg);
|
2002-12-21 23:43:39 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* Call tzinfo.dst(tzinfoarg), and extract an integer from the
|
|
|
|
* result. tzinfo must be an instance of the tzinfo class. If dst()
|
|
|
|
* returns None, call_dst returns 0 and sets *none to 1. If dst()
|
2017-07-31 11:26:50 -03:00
|
|
|
* doesn't return None or timedelta, TypeError is raised and this
|
2003-10-20 11:01:56 -03:00
|
|
|
* returns -1. If dst() returns an invalid timedelta for a UTC offset,
|
2003-01-02 17:28:08 -04:00
|
|
|
* ValueError is raised and this returns -1. Else *none is set to 0 and
|
2017-07-31 11:26:50 -03:00
|
|
|
* the offset is returned (as timedelta, positive east of UTC).
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
2010-07-07 20:56:38 -03:00
|
|
|
static PyObject *
|
|
|
|
call_dst(PyObject *tzinfo, PyObject *tzinfoarg)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
return call_tzinfo_method(tzinfo, "dst", tzinfoarg);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2002-12-30 16:52:32 -04:00
|
|
|
/* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be
|
2002-12-21 23:43:39 -04:00
|
|
|
* an instance of the tzinfo class or None. If tzinfo isn't None, and
|
2002-12-30 16:52:32 -04:00
|
|
|
* tzname() doesn't return None or a string, TypeError is raised and this
|
2007-05-23 18:24:35 -03:00
|
|
|
* returns NULL. If the result is a string, we ensure it is a Unicode
|
|
|
|
* string.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2002-12-30 16:52:32 -04:00
|
|
|
call_tzname(PyObject *tzinfo, PyObject *tzinfoarg)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result;
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(tzname);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
assert(tzinfo != NULL);
|
|
|
|
assert(check_tzinfo_subclass(tzinfo) >= 0);
|
|
|
|
assert(tzinfoarg != NULL);
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (tzinfo == Py_None)
|
|
|
|
Py_RETURN_NONE;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2019-07-11 05:59:05 -03:00
|
|
|
result = _PyObject_CallMethodIdOneArg(tzinfo, &PyId_tzname, tzinfoarg);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (result == NULL || result == Py_None)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!PyUnicode_Check(result)) {
|
|
|
|
PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must "
|
|
|
|
"return None or a string, not '%s'",
|
|
|
|
Py_TYPE(result)->tp_name);
|
|
|
|
Py_DECREF(result);
|
|
|
|
result = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
|
|
|
|
return result;
|
2002-12-26 22:21:51 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* repr is like "someclass(arg1, arg2)". If tzinfo isn't None,
|
|
|
|
* stuff
|
|
|
|
* ", tzinfo=" + repr(tzinfo)
|
|
|
|
* before the closing ")".
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
append_keyword_tzinfo(PyObject *repr, PyObject *tzinfo)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *temp;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(PyUnicode_Check(repr));
|
|
|
|
assert(tzinfo);
|
|
|
|
if (tzinfo == Py_None)
|
|
|
|
return repr;
|
|
|
|
/* Get rid of the trailing ')'. */
|
2011-09-28 02:41:54 -03:00
|
|
|
assert(PyUnicode_READ_CHAR(repr, PyUnicode_GET_LENGTH(repr)-1) == ')');
|
|
|
|
temp = PyUnicode_Substring(repr, 0, PyUnicode_GET_LENGTH(repr) - 1);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(repr);
|
|
|
|
if (temp == NULL)
|
|
|
|
return NULL;
|
|
|
|
repr = PyUnicode_FromFormat("%U, tzinfo=%R)", temp, tzinfo);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
return repr;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
/* repr is like "someclass(arg1, arg2)". If fold isn't 0,
|
|
|
|
* stuff
|
|
|
|
* ", fold=" + repr(tzinfo)
|
|
|
|
* before the closing ")".
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
append_keyword_fold(PyObject *repr, int fold)
|
|
|
|
{
|
|
|
|
PyObject *temp;
|
|
|
|
|
|
|
|
assert(PyUnicode_Check(repr));
|
|
|
|
if (fold == 0)
|
|
|
|
return repr;
|
|
|
|
/* Get rid of the trailing ')'. */
|
|
|
|
assert(PyUnicode_READ_CHAR(repr, PyUnicode_GET_LENGTH(repr)-1) == ')');
|
|
|
|
temp = PyUnicode_Substring(repr, 0, PyUnicode_GET_LENGTH(repr) - 1);
|
|
|
|
Py_DECREF(repr);
|
|
|
|
if (temp == NULL)
|
|
|
|
return NULL;
|
|
|
|
repr = PyUnicode_FromFormat("%U, fold=%d)", temp, fold);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
return repr;
|
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
static inline PyObject *
|
2018-10-22 13:32:52 -03:00
|
|
|
tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
|
|
|
|
{
|
2017-12-21 01:33:49 -04:00
|
|
|
PyObject *tzinfo;
|
|
|
|
if (rv == 1) {
|
|
|
|
// Create a timezone from offset in seconds (0 returns UTC)
|
|
|
|
if (tzoffset == 0) {
|
|
|
|
Py_INCREF(PyDateTime_TimeZone_UTC);
|
|
|
|
return PyDateTime_TimeZone_UTC;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1);
|
2018-08-24 12:53:16 -03:00
|
|
|
if (delta == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-12-21 01:33:49 -04:00
|
|
|
tzinfo = new_timezone(delta, NULL);
|
2018-08-24 12:53:16 -03:00
|
|
|
Py_DECREF(delta);
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else {
|
2017-12-21 01:33:49 -04:00
|
|
|
tzinfo = Py_None;
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tzinfo;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* String format helpers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char * const DayNames[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
|
|
|
|
};
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char * const MonthNames[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
|
|
};
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
int wday = weekday(GET_YEAR(date), GET_MONTH(date), GET_DAY(date));
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyUnicode_FromFormat("%s %s %2d %02d:%02d:%02d %04d",
|
|
|
|
DayNames[wday], MonthNames[GET_MONTH(date)-1],
|
|
|
|
GET_DAY(date), hours, minutes, seconds,
|
|
|
|
GET_YEAR(date));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
static PyObject *delta_negative(PyDateTime_Delta *self);
|
|
|
|
|
2017-07-31 11:26:50 -03:00
|
|
|
/* Add formatted UTC offset string to buf. buf has no more than
|
2002-12-16 16:18:38 -04:00
|
|
|
* buflen bytes remaining. The UTC offset is gotten by calling
|
|
|
|
* tzinfo.uctoffset(tzinfoarg). If that returns None, \0 is stored into
|
|
|
|
* *buf, and that's all. Else the returned value is checked for sanity (an
|
|
|
|
* integer in range), and if that's OK it's converted to an hours & minutes
|
|
|
|
* string of the form
|
2017-07-31 11:26:50 -03:00
|
|
|
* sign HH sep MM [sep SS [. UUUUUU]]
|
2002-12-16 16:18:38 -04:00
|
|
|
* Returns 0 if everything is OK. If the return value from utcoffset() is
|
|
|
|
* bogus, an appropriate exception is set and -1 is returned.
|
|
|
|
*/
|
|
|
|
static int
|
2002-12-19 21:31:27 -04:00
|
|
|
format_utcoffset(char *buf, size_t buflen, const char *sep,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo, PyObject *tzinfoarg)
|
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *offset;
|
2017-07-31 11:26:50 -03:00
|
|
|
int hours, minutes, seconds, microseconds;
|
2010-05-09 12:52:27 -03:00
|
|
|
char sign;
|
|
|
|
|
|
|
|
assert(buflen >= 1);
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
offset = call_utcoffset(tzinfo, tzinfoarg);
|
|
|
|
if (offset == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return -1;
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == Py_None) {
|
|
|
|
Py_DECREF(offset);
|
2010-05-09 12:52:27 -03:00
|
|
|
*buf = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
/* Offset is normalized, so it is negative if days < 0 */
|
|
|
|
if (GET_TD_DAYS(offset) < 0) {
|
2010-05-09 12:52:27 -03:00
|
|
|
sign = '-';
|
2016-04-10 12:12:01 -03:00
|
|
|
Py_SETREF(offset, delta_negative((PyDateTime_Delta *)offset));
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sign = '+';
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
/* Offset is not negative here. */
|
2017-07-31 11:26:50 -03:00
|
|
|
microseconds = GET_TD_MICROSECONDS(offset);
|
2010-07-07 20:56:38 -03:00
|
|
|
seconds = GET_TD_SECONDS(offset);
|
|
|
|
Py_DECREF(offset);
|
|
|
|
minutes = divmod(seconds, 60, &seconds);
|
|
|
|
hours = divmod(minutes, 60, &minutes);
|
2017-07-31 11:26:50 -03:00
|
|
|
if (microseconds) {
|
|
|
|
PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d.%06d", sign,
|
|
|
|
hours, sep, minutes, sep, seconds, microseconds);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (seconds) {
|
2016-07-22 19:47:04 -03:00
|
|
|
PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d", sign, hours,
|
|
|
|
sep, minutes, sep, seconds);
|
2017-07-31 11:26:50 -03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes);
|
2010-05-09 12:52:27 -03:00
|
|
|
return 0;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2007-05-23 18:36:49 -03:00
|
|
|
static PyObject *
|
|
|
|
make_Zreplacement(PyObject *object, PyObject *tzinfoarg)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *temp;
|
|
|
|
PyObject *tzinfo = get_tzinfo_member(object);
|
|
|
|
PyObject *Zreplacement = PyUnicode_FromStringAndSize(NULL, 0);
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(replace);
|
2011-11-20 21:49:52 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (Zreplacement == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (tzinfo == Py_None || tzinfo == NULL)
|
|
|
|
return Zreplacement;
|
|
|
|
|
|
|
|
assert(tzinfoarg != NULL);
|
|
|
|
temp = call_tzname(tzinfo, tzinfoarg);
|
|
|
|
if (temp == NULL)
|
|
|
|
goto Error;
|
|
|
|
if (temp == Py_None) {
|
|
|
|
Py_DECREF(temp);
|
|
|
|
return Zreplacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(PyUnicode_Check(temp));
|
|
|
|
/* Since the tzname is getting stuffed into the
|
|
|
|
* format, we have to double any % signs so that
|
|
|
|
* strftime doesn't treat them as format codes.
|
|
|
|
*/
|
|
|
|
Py_DECREF(Zreplacement);
|
2011-10-09 05:38:36 -03:00
|
|
|
Zreplacement = _PyObject_CallMethodId(temp, &PyId_replace, "ss", "%", "%%");
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(temp);
|
|
|
|
if (Zreplacement == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (!PyUnicode_Check(Zreplacement)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"tzname.replace() did not return a string");
|
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
return Zreplacement;
|
2007-05-23 18:36:49 -03:00
|
|
|
|
|
|
|
Error:
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(Zreplacement);
|
|
|
|
return NULL;
|
2007-05-23 18:36:49 -03:00
|
|
|
}
|
|
|
|
|
Merged revisions 61239-61249,61252-61257,61260-61264,61269-61275,61278-61279,61285-61286,61288-61290,61298,61303-61305,61312-61314,61317,61329,61332,61344,61350-61351,61363-61376,61378-61379,61382-61383,61387-61388,61392,61395-61396,61402-61403 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r61239 | andrew.kuchling | 2008-03-05 01:44:41 +0100 (Wed, 05 Mar 2008) | 1 line
Add more items; add fragmentary notes
........
r61240 | amaury.forgeotdarc | 2008-03-05 02:50:33 +0100 (Wed, 05 Mar 2008) | 13 lines
Issue#2238: some syntax errors from *args or **kwargs expressions
would give bogus error messages, because of untested exceptions::
>>> f(**g(1=2))
XXX undetected error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
instead of the expected SyntaxError: keyword can't be an expression
Will backport.
........
r61241 | neal.norwitz | 2008-03-05 06:10:48 +0100 (Wed, 05 Mar 2008) | 3 lines
Remove the files/dirs after closing the DB so the tests work on Windows.
Patch from Trent Nelson. Also simplified removing a file by using test_support.
........
r61242 | neal.norwitz | 2008-03-05 06:14:18 +0100 (Wed, 05 Mar 2008) | 3 lines
Get this test to pass even when there is no sound card in the system.
Patch from Trent Nelson. (I can't test this.)
........
r61243 | neal.norwitz | 2008-03-05 06:20:44 +0100 (Wed, 05 Mar 2008) | 3 lines
Catch OSError when trying to remove a file in case removal fails. This
should prevent a failure in tearDown masking any real test failure.
........
r61244 | neal.norwitz | 2008-03-05 06:38:06 +0100 (Wed, 05 Mar 2008) | 5 lines
Make the timeout longer to give slow machines a chance to pass the test
before timing out. This doesn't change the duration of the test under
normal circumstances. This is targetted at fixing the spurious failures
on the FreeBSD buildbot primarily.
........
r61245 | neal.norwitz | 2008-03-05 06:49:03 +0100 (Wed, 05 Mar 2008) | 1 line
Tabs -> spaces
........
r61246 | neal.norwitz | 2008-03-05 06:50:20 +0100 (Wed, 05 Mar 2008) | 1 line
Use -u urlfetch to run more tests
........
r61247 | neal.norwitz | 2008-03-05 06:51:20 +0100 (Wed, 05 Mar 2008) | 1 line
test_smtplib sometimes reports leaks too, suppress it
........
r61248 | jeffrey.yasskin | 2008-03-05 07:19:56 +0100 (Wed, 05 Mar 2008) | 5 lines
Fix test_socketserver on Windows after r61099 added several signal.alarm()
calls (which don't exist on non-Unix platforms).
Thanks to Trent Nelson for the report and patch.
........
r61249 | georg.brandl | 2008-03-05 08:10:35 +0100 (Wed, 05 Mar 2008) | 2 lines
Fix some rst.
........
r61252 | thomas.heller | 2008-03-05 15:53:39 +0100 (Wed, 05 Mar 2008) | 2 lines
News entry for yesterdays commit.
........
r61253 | thomas.heller | 2008-03-05 16:34:29 +0100 (Wed, 05 Mar 2008) | 3 lines
Issue 1872: Changed the struct module typecode from 't' to '?', for
compatibility with PEP3118.
........
r61254 | skip.montanaro | 2008-03-05 17:41:09 +0100 (Wed, 05 Mar 2008) | 4 lines
Elaborate on the role of the altinstall target when installing multiple
versions.
........
r61255 | georg.brandl | 2008-03-05 20:31:44 +0100 (Wed, 05 Mar 2008) | 2 lines
#2239: PYTHONPATH delimiter is os.pathsep.
........
r61256 | raymond.hettinger | 2008-03-05 21:59:58 +0100 (Wed, 05 Mar 2008) | 1 line
C implementation of itertools.permutations().
........
r61257 | raymond.hettinger | 2008-03-05 22:04:32 +0100 (Wed, 05 Mar 2008) | 1 line
Small code cleanup.
........
r61260 | martin.v.loewis | 2008-03-05 23:24:31 +0100 (Wed, 05 Mar 2008) | 2 lines
cd PCbuild only after deleting all pyc files.
........
r61261 | raymond.hettinger | 2008-03-06 02:15:52 +0100 (Thu, 06 Mar 2008) | 1 line
Add examples.
........
r61262 | andrew.kuchling | 2008-03-06 02:36:27 +0100 (Thu, 06 Mar 2008) | 1 line
Add two items
........
r61263 | georg.brandl | 2008-03-06 07:47:18 +0100 (Thu, 06 Mar 2008) | 2 lines
#1725737: ignore other VC directories other than CVS and SVN's too.
........
r61264 | martin.v.loewis | 2008-03-06 07:55:22 +0100 (Thu, 06 Mar 2008) | 4 lines
Patch #2232: os.tmpfile might fail on Windows if the user has no
permission to create files in the root directory.
Will backport to 2.5.
........
r61269 | georg.brandl | 2008-03-06 08:19:15 +0100 (Thu, 06 Mar 2008) | 2 lines
Expand on re.split behavior with captured expressions.
........
r61270 | georg.brandl | 2008-03-06 08:22:09 +0100 (Thu, 06 Mar 2008) | 2 lines
Little clarification of assignments.
........
r61271 | georg.brandl | 2008-03-06 08:31:34 +0100 (Thu, 06 Mar 2008) | 2 lines
Add isinstance/issubclass to tutorial.
........
r61272 | georg.brandl | 2008-03-06 08:34:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Add missing NEWS entry for r61263.
........
r61273 | georg.brandl | 2008-03-06 08:41:16 +0100 (Thu, 06 Mar 2008) | 2 lines
#2225: return nonzero status code from py_compile if not all files could be compiled.
........
r61274 | georg.brandl | 2008-03-06 08:43:02 +0100 (Thu, 06 Mar 2008) | 2 lines
#2220: handle matching failure more gracefully.
........
r61275 | georg.brandl | 2008-03-06 08:45:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Bug #2220: handle rlcompleter attribute match failure more gracefully.
........
r61278 | martin.v.loewis | 2008-03-06 14:49:47 +0100 (Thu, 06 Mar 2008) | 1 line
Rely on x64 platform configuration when building _bsddb on AMD64.
........
r61279 | martin.v.loewis | 2008-03-06 14:50:28 +0100 (Thu, 06 Mar 2008) | 1 line
Update db-4.4.20 build procedure.
........
r61285 | raymond.hettinger | 2008-03-06 21:52:01 +0100 (Thu, 06 Mar 2008) | 1 line
More tests.
........
r61286 | raymond.hettinger | 2008-03-06 23:51:36 +0100 (Thu, 06 Mar 2008) | 1 line
Issue 2246: itertools grouper object did not participate in GC (should be backported).
........
r61288 | raymond.hettinger | 2008-03-07 02:33:20 +0100 (Fri, 07 Mar 2008) | 1 line
Tweak recipes and tests
........
r61289 | jeffrey.yasskin | 2008-03-07 07:22:15 +0100 (Fri, 07 Mar 2008) | 5 lines
Progress on issue #1193577 by adding a polling .shutdown() method to
SocketServers. The core of the patch was written by Pedro Werneck, but any bugs
are mine. I've also rearranged the code for timeouts in order to avoid
interfering with the shutdown poll.
........
r61290 | nick.coghlan | 2008-03-07 15:13:28 +0100 (Fri, 07 Mar 2008) | 1 line
Speed up with statements by storing the __exit__ method on the stack instead of in a temp variable (bumps the magic number for pyc files)
........
r61298 | andrew.kuchling | 2008-03-07 22:09:23 +0100 (Fri, 07 Mar 2008) | 1 line
Grammar fix
........
r61303 | georg.brandl | 2008-03-08 10:54:06 +0100 (Sat, 08 Mar 2008) | 2 lines
#2253: fix continue vs. finally docs.
........
r61304 | marc-andre.lemburg | 2008-03-08 11:01:43 +0100 (Sat, 08 Mar 2008) | 3 lines
Add new name for Mandrake: Mandriva.
........
r61305 | georg.brandl | 2008-03-08 11:05:24 +0100 (Sat, 08 Mar 2008) | 2 lines
#1533486: fix types in refcount intro.
........
r61312 | facundo.batista | 2008-03-08 17:50:27 +0100 (Sat, 08 Mar 2008) | 5 lines
Issue 1106316. post_mortem()'s parameter, traceback, is now
optional: it defaults to the traceback of the exception that is currently
being handled.
........
r61313 | jeffrey.yasskin | 2008-03-08 19:26:54 +0100 (Sat, 08 Mar 2008) | 2 lines
Add tests for with and finally performance to pybench.
........
r61314 | jeffrey.yasskin | 2008-03-08 21:08:21 +0100 (Sat, 08 Mar 2008) | 2 lines
Fix pybench for pythons < 2.6, tested back to 2.3.
........
r61317 | jeffrey.yasskin | 2008-03-08 22:35:15 +0100 (Sat, 08 Mar 2008) | 3 lines
Well that was dumb. platform.python_implementation returns a function, not a
string.
........
r61329 | georg.brandl | 2008-03-09 16:11:39 +0100 (Sun, 09 Mar 2008) | 2 lines
#2249: document assertTrue and assertFalse.
........
r61332 | neal.norwitz | 2008-03-09 20:03:42 +0100 (Sun, 09 Mar 2008) | 4 lines
Introduce a lock to fix a race condition which caused an exception in the test.
Some buildbots were consistently failing (e.g., amd64).
Also remove a couple of semi-colons.
........
r61344 | raymond.hettinger | 2008-03-11 01:19:07 +0100 (Tue, 11 Mar 2008) | 1 line
Add recipe to docs.
........
r61350 | guido.van.rossum | 2008-03-11 22:18:06 +0100 (Tue, 11 Mar 2008) | 3 lines
Fix the overflows in expandtabs(). "This time for sure!"
(Exploit at request.)
........
r61351 | raymond.hettinger | 2008-03-11 22:37:46 +0100 (Tue, 11 Mar 2008) | 1 line
Improve docs for itemgetter(). Show that it works with slices.
........
r61363 | georg.brandl | 2008-03-13 08:15:56 +0100 (Thu, 13 Mar 2008) | 2 lines
#2265: fix example.
........
r61364 | georg.brandl | 2008-03-13 08:17:14 +0100 (Thu, 13 Mar 2008) | 2 lines
#2270: fix typo.
........
r61365 | georg.brandl | 2008-03-13 08:21:41 +0100 (Thu, 13 Mar 2008) | 2 lines
#1720705: add docs about import/threading interaction, wording by Nick.
........
r61366 | andrew.kuchling | 2008-03-13 12:07:35 +0100 (Thu, 13 Mar 2008) | 1 line
Add class decorators
........
r61367 | raymond.hettinger | 2008-03-13 17:43:17 +0100 (Thu, 13 Mar 2008) | 1 line
Add 2-to-3 support for the itertools moved to builtins or renamed.
........
r61368 | raymond.hettinger | 2008-03-13 17:43:59 +0100 (Thu, 13 Mar 2008) | 1 line
Consistent tense.
........
r61369 | raymond.hettinger | 2008-03-13 20:03:51 +0100 (Thu, 13 Mar 2008) | 1 line
Issue 2274: Add heapq.heappushpop().
........
r61370 | raymond.hettinger | 2008-03-13 20:33:34 +0100 (Thu, 13 Mar 2008) | 1 line
Simplify the nlargest() code using heappushpop().
........
r61371 | brett.cannon | 2008-03-13 21:27:00 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_thread over to unittest. Commits GHOP 237.
Thanks Benjamin Peterson for the patch.
........
r61372 | brett.cannon | 2008-03-13 21:33:10 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_tokenize to doctest.
Done as GHOP 238 by Josip Dzolonga.
........
r61373 | brett.cannon | 2008-03-13 21:47:41 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_contains, test_crypt, and test_select to unittest.
Patch from GHOP 294 by David Marek.
........
r61374 | brett.cannon | 2008-03-13 22:02:16 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_gdbm to use unittest.
Closes issue #1960. Thanks Giampaolo Rodola.
........
r61375 | brett.cannon | 2008-03-13 22:09:28 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_fcntl to unittest.
Closes issue #2055. Thanks Giampaolo Rodola.
........
r61376 | raymond.hettinger | 2008-03-14 06:03:44 +0100 (Fri, 14 Mar 2008) | 1 line
Leave heapreplace() unchanged.
........
r61378 | martin.v.loewis | 2008-03-14 14:56:09 +0100 (Fri, 14 Mar 2008) | 2 lines
Patch #2284: add -x64 option to rt.bat.
........
r61379 | martin.v.loewis | 2008-03-14 14:57:59 +0100 (Fri, 14 Mar 2008) | 2 lines
Use -x64 flag.
........
r61382 | brett.cannon | 2008-03-14 15:03:10 +0100 (Fri, 14 Mar 2008) | 2 lines
Remove a bad test.
........
r61383 | mark.dickinson | 2008-03-14 15:23:37 +0100 (Fri, 14 Mar 2008) | 9 lines
Issue 705836: Fix struct.pack(">f", 1e40) to behave consistently
across platforms: it should now raise OverflowError on all
platforms. (Previously it raised OverflowError only on
non IEEE 754 platforms.)
Also fix the (already existing) test for this behaviour
so that it actually raises TestFailed instead of just
referencing it.
........
r61387 | thomas.heller | 2008-03-14 22:06:21 +0100 (Fri, 14 Mar 2008) | 1 line
Remove unneeded initializer.
........
r61388 | martin.v.loewis | 2008-03-14 22:19:28 +0100 (Fri, 14 Mar 2008) | 2 lines
Run debug version, cd to PCbuild.
........
r61392 | georg.brandl | 2008-03-15 00:10:34 +0100 (Sat, 15 Mar 2008) | 2 lines
Remove obsolete paragraph. #2288.
........
r61395 | georg.brandl | 2008-03-15 01:20:19 +0100 (Sat, 15 Mar 2008) | 2 lines
Fix lots of broken links in the docs, found by Sphinx' external link checker.
........
r61396 | skip.montanaro | 2008-03-15 03:32:49 +0100 (Sat, 15 Mar 2008) | 1 line
note that fork and forkpty raise OSError on failure
........
r61402 | skip.montanaro | 2008-03-15 17:04:45 +0100 (Sat, 15 Mar 2008) | 1 line
add %f format to datetime - issue 1158
........
r61403 | skip.montanaro | 2008-03-15 17:07:11 +0100 (Sat, 15 Mar 2008) | 2 lines
.
........
2008-03-15 21:07:10 -03:00
|
|
|
static PyObject *
|
|
|
|
make_freplacement(PyObject *object)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
char freplacement[64];
|
|
|
|
if (PyTime_Check(object))
|
|
|
|
sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object));
|
|
|
|
else if (PyDateTime_Check(object))
|
|
|
|
sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object));
|
|
|
|
else
|
|
|
|
sprintf(freplacement, "%06d", 0);
|
Merged revisions 61239-61249,61252-61257,61260-61264,61269-61275,61278-61279,61285-61286,61288-61290,61298,61303-61305,61312-61314,61317,61329,61332,61344,61350-61351,61363-61376,61378-61379,61382-61383,61387-61388,61392,61395-61396,61402-61403 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r61239 | andrew.kuchling | 2008-03-05 01:44:41 +0100 (Wed, 05 Mar 2008) | 1 line
Add more items; add fragmentary notes
........
r61240 | amaury.forgeotdarc | 2008-03-05 02:50:33 +0100 (Wed, 05 Mar 2008) | 13 lines
Issue#2238: some syntax errors from *args or **kwargs expressions
would give bogus error messages, because of untested exceptions::
>>> f(**g(1=2))
XXX undetected error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
instead of the expected SyntaxError: keyword can't be an expression
Will backport.
........
r61241 | neal.norwitz | 2008-03-05 06:10:48 +0100 (Wed, 05 Mar 2008) | 3 lines
Remove the files/dirs after closing the DB so the tests work on Windows.
Patch from Trent Nelson. Also simplified removing a file by using test_support.
........
r61242 | neal.norwitz | 2008-03-05 06:14:18 +0100 (Wed, 05 Mar 2008) | 3 lines
Get this test to pass even when there is no sound card in the system.
Patch from Trent Nelson. (I can't test this.)
........
r61243 | neal.norwitz | 2008-03-05 06:20:44 +0100 (Wed, 05 Mar 2008) | 3 lines
Catch OSError when trying to remove a file in case removal fails. This
should prevent a failure in tearDown masking any real test failure.
........
r61244 | neal.norwitz | 2008-03-05 06:38:06 +0100 (Wed, 05 Mar 2008) | 5 lines
Make the timeout longer to give slow machines a chance to pass the test
before timing out. This doesn't change the duration of the test under
normal circumstances. This is targetted at fixing the spurious failures
on the FreeBSD buildbot primarily.
........
r61245 | neal.norwitz | 2008-03-05 06:49:03 +0100 (Wed, 05 Mar 2008) | 1 line
Tabs -> spaces
........
r61246 | neal.norwitz | 2008-03-05 06:50:20 +0100 (Wed, 05 Mar 2008) | 1 line
Use -u urlfetch to run more tests
........
r61247 | neal.norwitz | 2008-03-05 06:51:20 +0100 (Wed, 05 Mar 2008) | 1 line
test_smtplib sometimes reports leaks too, suppress it
........
r61248 | jeffrey.yasskin | 2008-03-05 07:19:56 +0100 (Wed, 05 Mar 2008) | 5 lines
Fix test_socketserver on Windows after r61099 added several signal.alarm()
calls (which don't exist on non-Unix platforms).
Thanks to Trent Nelson for the report and patch.
........
r61249 | georg.brandl | 2008-03-05 08:10:35 +0100 (Wed, 05 Mar 2008) | 2 lines
Fix some rst.
........
r61252 | thomas.heller | 2008-03-05 15:53:39 +0100 (Wed, 05 Mar 2008) | 2 lines
News entry for yesterdays commit.
........
r61253 | thomas.heller | 2008-03-05 16:34:29 +0100 (Wed, 05 Mar 2008) | 3 lines
Issue 1872: Changed the struct module typecode from 't' to '?', for
compatibility with PEP3118.
........
r61254 | skip.montanaro | 2008-03-05 17:41:09 +0100 (Wed, 05 Mar 2008) | 4 lines
Elaborate on the role of the altinstall target when installing multiple
versions.
........
r61255 | georg.brandl | 2008-03-05 20:31:44 +0100 (Wed, 05 Mar 2008) | 2 lines
#2239: PYTHONPATH delimiter is os.pathsep.
........
r61256 | raymond.hettinger | 2008-03-05 21:59:58 +0100 (Wed, 05 Mar 2008) | 1 line
C implementation of itertools.permutations().
........
r61257 | raymond.hettinger | 2008-03-05 22:04:32 +0100 (Wed, 05 Mar 2008) | 1 line
Small code cleanup.
........
r61260 | martin.v.loewis | 2008-03-05 23:24:31 +0100 (Wed, 05 Mar 2008) | 2 lines
cd PCbuild only after deleting all pyc files.
........
r61261 | raymond.hettinger | 2008-03-06 02:15:52 +0100 (Thu, 06 Mar 2008) | 1 line
Add examples.
........
r61262 | andrew.kuchling | 2008-03-06 02:36:27 +0100 (Thu, 06 Mar 2008) | 1 line
Add two items
........
r61263 | georg.brandl | 2008-03-06 07:47:18 +0100 (Thu, 06 Mar 2008) | 2 lines
#1725737: ignore other VC directories other than CVS and SVN's too.
........
r61264 | martin.v.loewis | 2008-03-06 07:55:22 +0100 (Thu, 06 Mar 2008) | 4 lines
Patch #2232: os.tmpfile might fail on Windows if the user has no
permission to create files in the root directory.
Will backport to 2.5.
........
r61269 | georg.brandl | 2008-03-06 08:19:15 +0100 (Thu, 06 Mar 2008) | 2 lines
Expand on re.split behavior with captured expressions.
........
r61270 | georg.brandl | 2008-03-06 08:22:09 +0100 (Thu, 06 Mar 2008) | 2 lines
Little clarification of assignments.
........
r61271 | georg.brandl | 2008-03-06 08:31:34 +0100 (Thu, 06 Mar 2008) | 2 lines
Add isinstance/issubclass to tutorial.
........
r61272 | georg.brandl | 2008-03-06 08:34:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Add missing NEWS entry for r61263.
........
r61273 | georg.brandl | 2008-03-06 08:41:16 +0100 (Thu, 06 Mar 2008) | 2 lines
#2225: return nonzero status code from py_compile if not all files could be compiled.
........
r61274 | georg.brandl | 2008-03-06 08:43:02 +0100 (Thu, 06 Mar 2008) | 2 lines
#2220: handle matching failure more gracefully.
........
r61275 | georg.brandl | 2008-03-06 08:45:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Bug #2220: handle rlcompleter attribute match failure more gracefully.
........
r61278 | martin.v.loewis | 2008-03-06 14:49:47 +0100 (Thu, 06 Mar 2008) | 1 line
Rely on x64 platform configuration when building _bsddb on AMD64.
........
r61279 | martin.v.loewis | 2008-03-06 14:50:28 +0100 (Thu, 06 Mar 2008) | 1 line
Update db-4.4.20 build procedure.
........
r61285 | raymond.hettinger | 2008-03-06 21:52:01 +0100 (Thu, 06 Mar 2008) | 1 line
More tests.
........
r61286 | raymond.hettinger | 2008-03-06 23:51:36 +0100 (Thu, 06 Mar 2008) | 1 line
Issue 2246: itertools grouper object did not participate in GC (should be backported).
........
r61288 | raymond.hettinger | 2008-03-07 02:33:20 +0100 (Fri, 07 Mar 2008) | 1 line
Tweak recipes and tests
........
r61289 | jeffrey.yasskin | 2008-03-07 07:22:15 +0100 (Fri, 07 Mar 2008) | 5 lines
Progress on issue #1193577 by adding a polling .shutdown() method to
SocketServers. The core of the patch was written by Pedro Werneck, but any bugs
are mine. I've also rearranged the code for timeouts in order to avoid
interfering with the shutdown poll.
........
r61290 | nick.coghlan | 2008-03-07 15:13:28 +0100 (Fri, 07 Mar 2008) | 1 line
Speed up with statements by storing the __exit__ method on the stack instead of in a temp variable (bumps the magic number for pyc files)
........
r61298 | andrew.kuchling | 2008-03-07 22:09:23 +0100 (Fri, 07 Mar 2008) | 1 line
Grammar fix
........
r61303 | georg.brandl | 2008-03-08 10:54:06 +0100 (Sat, 08 Mar 2008) | 2 lines
#2253: fix continue vs. finally docs.
........
r61304 | marc-andre.lemburg | 2008-03-08 11:01:43 +0100 (Sat, 08 Mar 2008) | 3 lines
Add new name for Mandrake: Mandriva.
........
r61305 | georg.brandl | 2008-03-08 11:05:24 +0100 (Sat, 08 Mar 2008) | 2 lines
#1533486: fix types in refcount intro.
........
r61312 | facundo.batista | 2008-03-08 17:50:27 +0100 (Sat, 08 Mar 2008) | 5 lines
Issue 1106316. post_mortem()'s parameter, traceback, is now
optional: it defaults to the traceback of the exception that is currently
being handled.
........
r61313 | jeffrey.yasskin | 2008-03-08 19:26:54 +0100 (Sat, 08 Mar 2008) | 2 lines
Add tests for with and finally performance to pybench.
........
r61314 | jeffrey.yasskin | 2008-03-08 21:08:21 +0100 (Sat, 08 Mar 2008) | 2 lines
Fix pybench for pythons < 2.6, tested back to 2.3.
........
r61317 | jeffrey.yasskin | 2008-03-08 22:35:15 +0100 (Sat, 08 Mar 2008) | 3 lines
Well that was dumb. platform.python_implementation returns a function, not a
string.
........
r61329 | georg.brandl | 2008-03-09 16:11:39 +0100 (Sun, 09 Mar 2008) | 2 lines
#2249: document assertTrue and assertFalse.
........
r61332 | neal.norwitz | 2008-03-09 20:03:42 +0100 (Sun, 09 Mar 2008) | 4 lines
Introduce a lock to fix a race condition which caused an exception in the test.
Some buildbots were consistently failing (e.g., amd64).
Also remove a couple of semi-colons.
........
r61344 | raymond.hettinger | 2008-03-11 01:19:07 +0100 (Tue, 11 Mar 2008) | 1 line
Add recipe to docs.
........
r61350 | guido.van.rossum | 2008-03-11 22:18:06 +0100 (Tue, 11 Mar 2008) | 3 lines
Fix the overflows in expandtabs(). "This time for sure!"
(Exploit at request.)
........
r61351 | raymond.hettinger | 2008-03-11 22:37:46 +0100 (Tue, 11 Mar 2008) | 1 line
Improve docs for itemgetter(). Show that it works with slices.
........
r61363 | georg.brandl | 2008-03-13 08:15:56 +0100 (Thu, 13 Mar 2008) | 2 lines
#2265: fix example.
........
r61364 | georg.brandl | 2008-03-13 08:17:14 +0100 (Thu, 13 Mar 2008) | 2 lines
#2270: fix typo.
........
r61365 | georg.brandl | 2008-03-13 08:21:41 +0100 (Thu, 13 Mar 2008) | 2 lines
#1720705: add docs about import/threading interaction, wording by Nick.
........
r61366 | andrew.kuchling | 2008-03-13 12:07:35 +0100 (Thu, 13 Mar 2008) | 1 line
Add class decorators
........
r61367 | raymond.hettinger | 2008-03-13 17:43:17 +0100 (Thu, 13 Mar 2008) | 1 line
Add 2-to-3 support for the itertools moved to builtins or renamed.
........
r61368 | raymond.hettinger | 2008-03-13 17:43:59 +0100 (Thu, 13 Mar 2008) | 1 line
Consistent tense.
........
r61369 | raymond.hettinger | 2008-03-13 20:03:51 +0100 (Thu, 13 Mar 2008) | 1 line
Issue 2274: Add heapq.heappushpop().
........
r61370 | raymond.hettinger | 2008-03-13 20:33:34 +0100 (Thu, 13 Mar 2008) | 1 line
Simplify the nlargest() code using heappushpop().
........
r61371 | brett.cannon | 2008-03-13 21:27:00 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_thread over to unittest. Commits GHOP 237.
Thanks Benjamin Peterson for the patch.
........
r61372 | brett.cannon | 2008-03-13 21:33:10 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_tokenize to doctest.
Done as GHOP 238 by Josip Dzolonga.
........
r61373 | brett.cannon | 2008-03-13 21:47:41 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_contains, test_crypt, and test_select to unittest.
Patch from GHOP 294 by David Marek.
........
r61374 | brett.cannon | 2008-03-13 22:02:16 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_gdbm to use unittest.
Closes issue #1960. Thanks Giampaolo Rodola.
........
r61375 | brett.cannon | 2008-03-13 22:09:28 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_fcntl to unittest.
Closes issue #2055. Thanks Giampaolo Rodola.
........
r61376 | raymond.hettinger | 2008-03-14 06:03:44 +0100 (Fri, 14 Mar 2008) | 1 line
Leave heapreplace() unchanged.
........
r61378 | martin.v.loewis | 2008-03-14 14:56:09 +0100 (Fri, 14 Mar 2008) | 2 lines
Patch #2284: add -x64 option to rt.bat.
........
r61379 | martin.v.loewis | 2008-03-14 14:57:59 +0100 (Fri, 14 Mar 2008) | 2 lines
Use -x64 flag.
........
r61382 | brett.cannon | 2008-03-14 15:03:10 +0100 (Fri, 14 Mar 2008) | 2 lines
Remove a bad test.
........
r61383 | mark.dickinson | 2008-03-14 15:23:37 +0100 (Fri, 14 Mar 2008) | 9 lines
Issue 705836: Fix struct.pack(">f", 1e40) to behave consistently
across platforms: it should now raise OverflowError on all
platforms. (Previously it raised OverflowError only on
non IEEE 754 platforms.)
Also fix the (already existing) test for this behaviour
so that it actually raises TestFailed instead of just
referencing it.
........
r61387 | thomas.heller | 2008-03-14 22:06:21 +0100 (Fri, 14 Mar 2008) | 1 line
Remove unneeded initializer.
........
r61388 | martin.v.loewis | 2008-03-14 22:19:28 +0100 (Fri, 14 Mar 2008) | 2 lines
Run debug version, cd to PCbuild.
........
r61392 | georg.brandl | 2008-03-15 00:10:34 +0100 (Sat, 15 Mar 2008) | 2 lines
Remove obsolete paragraph. #2288.
........
r61395 | georg.brandl | 2008-03-15 01:20:19 +0100 (Sat, 15 Mar 2008) | 2 lines
Fix lots of broken links in the docs, found by Sphinx' external link checker.
........
r61396 | skip.montanaro | 2008-03-15 03:32:49 +0100 (Sat, 15 Mar 2008) | 1 line
note that fork and forkpty raise OSError on failure
........
r61402 | skip.montanaro | 2008-03-15 17:04:45 +0100 (Sat, 15 Mar 2008) | 1 line
add %f format to datetime - issue 1158
........
r61403 | skip.montanaro | 2008-03-15 17:07:11 +0100 (Sat, 15 Mar 2008) | 2 lines
.
........
2008-03-15 21:07:10 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyBytes_FromStringAndSize(freplacement, strlen(freplacement));
|
Merged revisions 61239-61249,61252-61257,61260-61264,61269-61275,61278-61279,61285-61286,61288-61290,61298,61303-61305,61312-61314,61317,61329,61332,61344,61350-61351,61363-61376,61378-61379,61382-61383,61387-61388,61392,61395-61396,61402-61403 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r61239 | andrew.kuchling | 2008-03-05 01:44:41 +0100 (Wed, 05 Mar 2008) | 1 line
Add more items; add fragmentary notes
........
r61240 | amaury.forgeotdarc | 2008-03-05 02:50:33 +0100 (Wed, 05 Mar 2008) | 13 lines
Issue#2238: some syntax errors from *args or **kwargs expressions
would give bogus error messages, because of untested exceptions::
>>> f(**g(1=2))
XXX undetected error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
instead of the expected SyntaxError: keyword can't be an expression
Will backport.
........
r61241 | neal.norwitz | 2008-03-05 06:10:48 +0100 (Wed, 05 Mar 2008) | 3 lines
Remove the files/dirs after closing the DB so the tests work on Windows.
Patch from Trent Nelson. Also simplified removing a file by using test_support.
........
r61242 | neal.norwitz | 2008-03-05 06:14:18 +0100 (Wed, 05 Mar 2008) | 3 lines
Get this test to pass even when there is no sound card in the system.
Patch from Trent Nelson. (I can't test this.)
........
r61243 | neal.norwitz | 2008-03-05 06:20:44 +0100 (Wed, 05 Mar 2008) | 3 lines
Catch OSError when trying to remove a file in case removal fails. This
should prevent a failure in tearDown masking any real test failure.
........
r61244 | neal.norwitz | 2008-03-05 06:38:06 +0100 (Wed, 05 Mar 2008) | 5 lines
Make the timeout longer to give slow machines a chance to pass the test
before timing out. This doesn't change the duration of the test under
normal circumstances. This is targetted at fixing the spurious failures
on the FreeBSD buildbot primarily.
........
r61245 | neal.norwitz | 2008-03-05 06:49:03 +0100 (Wed, 05 Mar 2008) | 1 line
Tabs -> spaces
........
r61246 | neal.norwitz | 2008-03-05 06:50:20 +0100 (Wed, 05 Mar 2008) | 1 line
Use -u urlfetch to run more tests
........
r61247 | neal.norwitz | 2008-03-05 06:51:20 +0100 (Wed, 05 Mar 2008) | 1 line
test_smtplib sometimes reports leaks too, suppress it
........
r61248 | jeffrey.yasskin | 2008-03-05 07:19:56 +0100 (Wed, 05 Mar 2008) | 5 lines
Fix test_socketserver on Windows after r61099 added several signal.alarm()
calls (which don't exist on non-Unix platforms).
Thanks to Trent Nelson for the report and patch.
........
r61249 | georg.brandl | 2008-03-05 08:10:35 +0100 (Wed, 05 Mar 2008) | 2 lines
Fix some rst.
........
r61252 | thomas.heller | 2008-03-05 15:53:39 +0100 (Wed, 05 Mar 2008) | 2 lines
News entry for yesterdays commit.
........
r61253 | thomas.heller | 2008-03-05 16:34:29 +0100 (Wed, 05 Mar 2008) | 3 lines
Issue 1872: Changed the struct module typecode from 't' to '?', for
compatibility with PEP3118.
........
r61254 | skip.montanaro | 2008-03-05 17:41:09 +0100 (Wed, 05 Mar 2008) | 4 lines
Elaborate on the role of the altinstall target when installing multiple
versions.
........
r61255 | georg.brandl | 2008-03-05 20:31:44 +0100 (Wed, 05 Mar 2008) | 2 lines
#2239: PYTHONPATH delimiter is os.pathsep.
........
r61256 | raymond.hettinger | 2008-03-05 21:59:58 +0100 (Wed, 05 Mar 2008) | 1 line
C implementation of itertools.permutations().
........
r61257 | raymond.hettinger | 2008-03-05 22:04:32 +0100 (Wed, 05 Mar 2008) | 1 line
Small code cleanup.
........
r61260 | martin.v.loewis | 2008-03-05 23:24:31 +0100 (Wed, 05 Mar 2008) | 2 lines
cd PCbuild only after deleting all pyc files.
........
r61261 | raymond.hettinger | 2008-03-06 02:15:52 +0100 (Thu, 06 Mar 2008) | 1 line
Add examples.
........
r61262 | andrew.kuchling | 2008-03-06 02:36:27 +0100 (Thu, 06 Mar 2008) | 1 line
Add two items
........
r61263 | georg.brandl | 2008-03-06 07:47:18 +0100 (Thu, 06 Mar 2008) | 2 lines
#1725737: ignore other VC directories other than CVS and SVN's too.
........
r61264 | martin.v.loewis | 2008-03-06 07:55:22 +0100 (Thu, 06 Mar 2008) | 4 lines
Patch #2232: os.tmpfile might fail on Windows if the user has no
permission to create files in the root directory.
Will backport to 2.5.
........
r61269 | georg.brandl | 2008-03-06 08:19:15 +0100 (Thu, 06 Mar 2008) | 2 lines
Expand on re.split behavior with captured expressions.
........
r61270 | georg.brandl | 2008-03-06 08:22:09 +0100 (Thu, 06 Mar 2008) | 2 lines
Little clarification of assignments.
........
r61271 | georg.brandl | 2008-03-06 08:31:34 +0100 (Thu, 06 Mar 2008) | 2 lines
Add isinstance/issubclass to tutorial.
........
r61272 | georg.brandl | 2008-03-06 08:34:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Add missing NEWS entry for r61263.
........
r61273 | georg.brandl | 2008-03-06 08:41:16 +0100 (Thu, 06 Mar 2008) | 2 lines
#2225: return nonzero status code from py_compile if not all files could be compiled.
........
r61274 | georg.brandl | 2008-03-06 08:43:02 +0100 (Thu, 06 Mar 2008) | 2 lines
#2220: handle matching failure more gracefully.
........
r61275 | georg.brandl | 2008-03-06 08:45:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Bug #2220: handle rlcompleter attribute match failure more gracefully.
........
r61278 | martin.v.loewis | 2008-03-06 14:49:47 +0100 (Thu, 06 Mar 2008) | 1 line
Rely on x64 platform configuration when building _bsddb on AMD64.
........
r61279 | martin.v.loewis | 2008-03-06 14:50:28 +0100 (Thu, 06 Mar 2008) | 1 line
Update db-4.4.20 build procedure.
........
r61285 | raymond.hettinger | 2008-03-06 21:52:01 +0100 (Thu, 06 Mar 2008) | 1 line
More tests.
........
r61286 | raymond.hettinger | 2008-03-06 23:51:36 +0100 (Thu, 06 Mar 2008) | 1 line
Issue 2246: itertools grouper object did not participate in GC (should be backported).
........
r61288 | raymond.hettinger | 2008-03-07 02:33:20 +0100 (Fri, 07 Mar 2008) | 1 line
Tweak recipes and tests
........
r61289 | jeffrey.yasskin | 2008-03-07 07:22:15 +0100 (Fri, 07 Mar 2008) | 5 lines
Progress on issue #1193577 by adding a polling .shutdown() method to
SocketServers. The core of the patch was written by Pedro Werneck, but any bugs
are mine. I've also rearranged the code for timeouts in order to avoid
interfering with the shutdown poll.
........
r61290 | nick.coghlan | 2008-03-07 15:13:28 +0100 (Fri, 07 Mar 2008) | 1 line
Speed up with statements by storing the __exit__ method on the stack instead of in a temp variable (bumps the magic number for pyc files)
........
r61298 | andrew.kuchling | 2008-03-07 22:09:23 +0100 (Fri, 07 Mar 2008) | 1 line
Grammar fix
........
r61303 | georg.brandl | 2008-03-08 10:54:06 +0100 (Sat, 08 Mar 2008) | 2 lines
#2253: fix continue vs. finally docs.
........
r61304 | marc-andre.lemburg | 2008-03-08 11:01:43 +0100 (Sat, 08 Mar 2008) | 3 lines
Add new name for Mandrake: Mandriva.
........
r61305 | georg.brandl | 2008-03-08 11:05:24 +0100 (Sat, 08 Mar 2008) | 2 lines
#1533486: fix types in refcount intro.
........
r61312 | facundo.batista | 2008-03-08 17:50:27 +0100 (Sat, 08 Mar 2008) | 5 lines
Issue 1106316. post_mortem()'s parameter, traceback, is now
optional: it defaults to the traceback of the exception that is currently
being handled.
........
r61313 | jeffrey.yasskin | 2008-03-08 19:26:54 +0100 (Sat, 08 Mar 2008) | 2 lines
Add tests for with and finally performance to pybench.
........
r61314 | jeffrey.yasskin | 2008-03-08 21:08:21 +0100 (Sat, 08 Mar 2008) | 2 lines
Fix pybench for pythons < 2.6, tested back to 2.3.
........
r61317 | jeffrey.yasskin | 2008-03-08 22:35:15 +0100 (Sat, 08 Mar 2008) | 3 lines
Well that was dumb. platform.python_implementation returns a function, not a
string.
........
r61329 | georg.brandl | 2008-03-09 16:11:39 +0100 (Sun, 09 Mar 2008) | 2 lines
#2249: document assertTrue and assertFalse.
........
r61332 | neal.norwitz | 2008-03-09 20:03:42 +0100 (Sun, 09 Mar 2008) | 4 lines
Introduce a lock to fix a race condition which caused an exception in the test.
Some buildbots were consistently failing (e.g., amd64).
Also remove a couple of semi-colons.
........
r61344 | raymond.hettinger | 2008-03-11 01:19:07 +0100 (Tue, 11 Mar 2008) | 1 line
Add recipe to docs.
........
r61350 | guido.van.rossum | 2008-03-11 22:18:06 +0100 (Tue, 11 Mar 2008) | 3 lines
Fix the overflows in expandtabs(). "This time for sure!"
(Exploit at request.)
........
r61351 | raymond.hettinger | 2008-03-11 22:37:46 +0100 (Tue, 11 Mar 2008) | 1 line
Improve docs for itemgetter(). Show that it works with slices.
........
r61363 | georg.brandl | 2008-03-13 08:15:56 +0100 (Thu, 13 Mar 2008) | 2 lines
#2265: fix example.
........
r61364 | georg.brandl | 2008-03-13 08:17:14 +0100 (Thu, 13 Mar 2008) | 2 lines
#2270: fix typo.
........
r61365 | georg.brandl | 2008-03-13 08:21:41 +0100 (Thu, 13 Mar 2008) | 2 lines
#1720705: add docs about import/threading interaction, wording by Nick.
........
r61366 | andrew.kuchling | 2008-03-13 12:07:35 +0100 (Thu, 13 Mar 2008) | 1 line
Add class decorators
........
r61367 | raymond.hettinger | 2008-03-13 17:43:17 +0100 (Thu, 13 Mar 2008) | 1 line
Add 2-to-3 support for the itertools moved to builtins or renamed.
........
r61368 | raymond.hettinger | 2008-03-13 17:43:59 +0100 (Thu, 13 Mar 2008) | 1 line
Consistent tense.
........
r61369 | raymond.hettinger | 2008-03-13 20:03:51 +0100 (Thu, 13 Mar 2008) | 1 line
Issue 2274: Add heapq.heappushpop().
........
r61370 | raymond.hettinger | 2008-03-13 20:33:34 +0100 (Thu, 13 Mar 2008) | 1 line
Simplify the nlargest() code using heappushpop().
........
r61371 | brett.cannon | 2008-03-13 21:27:00 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_thread over to unittest. Commits GHOP 237.
Thanks Benjamin Peterson for the patch.
........
r61372 | brett.cannon | 2008-03-13 21:33:10 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_tokenize to doctest.
Done as GHOP 238 by Josip Dzolonga.
........
r61373 | brett.cannon | 2008-03-13 21:47:41 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_contains, test_crypt, and test_select to unittest.
Patch from GHOP 294 by David Marek.
........
r61374 | brett.cannon | 2008-03-13 22:02:16 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_gdbm to use unittest.
Closes issue #1960. Thanks Giampaolo Rodola.
........
r61375 | brett.cannon | 2008-03-13 22:09:28 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_fcntl to unittest.
Closes issue #2055. Thanks Giampaolo Rodola.
........
r61376 | raymond.hettinger | 2008-03-14 06:03:44 +0100 (Fri, 14 Mar 2008) | 1 line
Leave heapreplace() unchanged.
........
r61378 | martin.v.loewis | 2008-03-14 14:56:09 +0100 (Fri, 14 Mar 2008) | 2 lines
Patch #2284: add -x64 option to rt.bat.
........
r61379 | martin.v.loewis | 2008-03-14 14:57:59 +0100 (Fri, 14 Mar 2008) | 2 lines
Use -x64 flag.
........
r61382 | brett.cannon | 2008-03-14 15:03:10 +0100 (Fri, 14 Mar 2008) | 2 lines
Remove a bad test.
........
r61383 | mark.dickinson | 2008-03-14 15:23:37 +0100 (Fri, 14 Mar 2008) | 9 lines
Issue 705836: Fix struct.pack(">f", 1e40) to behave consistently
across platforms: it should now raise OverflowError on all
platforms. (Previously it raised OverflowError only on
non IEEE 754 platforms.)
Also fix the (already existing) test for this behaviour
so that it actually raises TestFailed instead of just
referencing it.
........
r61387 | thomas.heller | 2008-03-14 22:06:21 +0100 (Fri, 14 Mar 2008) | 1 line
Remove unneeded initializer.
........
r61388 | martin.v.loewis | 2008-03-14 22:19:28 +0100 (Fri, 14 Mar 2008) | 2 lines
Run debug version, cd to PCbuild.
........
r61392 | georg.brandl | 2008-03-15 00:10:34 +0100 (Sat, 15 Mar 2008) | 2 lines
Remove obsolete paragraph. #2288.
........
r61395 | georg.brandl | 2008-03-15 01:20:19 +0100 (Sat, 15 Mar 2008) | 2 lines
Fix lots of broken links in the docs, found by Sphinx' external link checker.
........
r61396 | skip.montanaro | 2008-03-15 03:32:49 +0100 (Sat, 15 Mar 2008) | 1 line
note that fork and forkpty raise OSError on failure
........
r61402 | skip.montanaro | 2008-03-15 17:04:45 +0100 (Sat, 15 Mar 2008) | 1 line
add %f format to datetime - issue 1158
........
r61403 | skip.montanaro | 2008-03-15 17:07:11 +0100 (Sat, 15 Mar 2008) | 2 lines
.
........
2008-03-15 21:07:10 -03:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* I sure don't want to reproduce the strftime code from the time module,
|
|
|
|
* so this imports the module and calls it. All the hair is due to
|
Merged revisions 61239-61249,61252-61257,61260-61264,61269-61275,61278-61279,61285-61286,61288-61290,61298,61303-61305,61312-61314,61317,61329,61332,61344,61350-61351,61363-61376,61378-61379,61382-61383,61387-61388,61392,61395-61396,61402-61403 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r61239 | andrew.kuchling | 2008-03-05 01:44:41 +0100 (Wed, 05 Mar 2008) | 1 line
Add more items; add fragmentary notes
........
r61240 | amaury.forgeotdarc | 2008-03-05 02:50:33 +0100 (Wed, 05 Mar 2008) | 13 lines
Issue#2238: some syntax errors from *args or **kwargs expressions
would give bogus error messages, because of untested exceptions::
>>> f(**g(1=2))
XXX undetected error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
instead of the expected SyntaxError: keyword can't be an expression
Will backport.
........
r61241 | neal.norwitz | 2008-03-05 06:10:48 +0100 (Wed, 05 Mar 2008) | 3 lines
Remove the files/dirs after closing the DB so the tests work on Windows.
Patch from Trent Nelson. Also simplified removing a file by using test_support.
........
r61242 | neal.norwitz | 2008-03-05 06:14:18 +0100 (Wed, 05 Mar 2008) | 3 lines
Get this test to pass even when there is no sound card in the system.
Patch from Trent Nelson. (I can't test this.)
........
r61243 | neal.norwitz | 2008-03-05 06:20:44 +0100 (Wed, 05 Mar 2008) | 3 lines
Catch OSError when trying to remove a file in case removal fails. This
should prevent a failure in tearDown masking any real test failure.
........
r61244 | neal.norwitz | 2008-03-05 06:38:06 +0100 (Wed, 05 Mar 2008) | 5 lines
Make the timeout longer to give slow machines a chance to pass the test
before timing out. This doesn't change the duration of the test under
normal circumstances. This is targetted at fixing the spurious failures
on the FreeBSD buildbot primarily.
........
r61245 | neal.norwitz | 2008-03-05 06:49:03 +0100 (Wed, 05 Mar 2008) | 1 line
Tabs -> spaces
........
r61246 | neal.norwitz | 2008-03-05 06:50:20 +0100 (Wed, 05 Mar 2008) | 1 line
Use -u urlfetch to run more tests
........
r61247 | neal.norwitz | 2008-03-05 06:51:20 +0100 (Wed, 05 Mar 2008) | 1 line
test_smtplib sometimes reports leaks too, suppress it
........
r61248 | jeffrey.yasskin | 2008-03-05 07:19:56 +0100 (Wed, 05 Mar 2008) | 5 lines
Fix test_socketserver on Windows after r61099 added several signal.alarm()
calls (which don't exist on non-Unix platforms).
Thanks to Trent Nelson for the report and patch.
........
r61249 | georg.brandl | 2008-03-05 08:10:35 +0100 (Wed, 05 Mar 2008) | 2 lines
Fix some rst.
........
r61252 | thomas.heller | 2008-03-05 15:53:39 +0100 (Wed, 05 Mar 2008) | 2 lines
News entry for yesterdays commit.
........
r61253 | thomas.heller | 2008-03-05 16:34:29 +0100 (Wed, 05 Mar 2008) | 3 lines
Issue 1872: Changed the struct module typecode from 't' to '?', for
compatibility with PEP3118.
........
r61254 | skip.montanaro | 2008-03-05 17:41:09 +0100 (Wed, 05 Mar 2008) | 4 lines
Elaborate on the role of the altinstall target when installing multiple
versions.
........
r61255 | georg.brandl | 2008-03-05 20:31:44 +0100 (Wed, 05 Mar 2008) | 2 lines
#2239: PYTHONPATH delimiter is os.pathsep.
........
r61256 | raymond.hettinger | 2008-03-05 21:59:58 +0100 (Wed, 05 Mar 2008) | 1 line
C implementation of itertools.permutations().
........
r61257 | raymond.hettinger | 2008-03-05 22:04:32 +0100 (Wed, 05 Mar 2008) | 1 line
Small code cleanup.
........
r61260 | martin.v.loewis | 2008-03-05 23:24:31 +0100 (Wed, 05 Mar 2008) | 2 lines
cd PCbuild only after deleting all pyc files.
........
r61261 | raymond.hettinger | 2008-03-06 02:15:52 +0100 (Thu, 06 Mar 2008) | 1 line
Add examples.
........
r61262 | andrew.kuchling | 2008-03-06 02:36:27 +0100 (Thu, 06 Mar 2008) | 1 line
Add two items
........
r61263 | georg.brandl | 2008-03-06 07:47:18 +0100 (Thu, 06 Mar 2008) | 2 lines
#1725737: ignore other VC directories other than CVS and SVN's too.
........
r61264 | martin.v.loewis | 2008-03-06 07:55:22 +0100 (Thu, 06 Mar 2008) | 4 lines
Patch #2232: os.tmpfile might fail on Windows if the user has no
permission to create files in the root directory.
Will backport to 2.5.
........
r61269 | georg.brandl | 2008-03-06 08:19:15 +0100 (Thu, 06 Mar 2008) | 2 lines
Expand on re.split behavior with captured expressions.
........
r61270 | georg.brandl | 2008-03-06 08:22:09 +0100 (Thu, 06 Mar 2008) | 2 lines
Little clarification of assignments.
........
r61271 | georg.brandl | 2008-03-06 08:31:34 +0100 (Thu, 06 Mar 2008) | 2 lines
Add isinstance/issubclass to tutorial.
........
r61272 | georg.brandl | 2008-03-06 08:34:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Add missing NEWS entry for r61263.
........
r61273 | georg.brandl | 2008-03-06 08:41:16 +0100 (Thu, 06 Mar 2008) | 2 lines
#2225: return nonzero status code from py_compile if not all files could be compiled.
........
r61274 | georg.brandl | 2008-03-06 08:43:02 +0100 (Thu, 06 Mar 2008) | 2 lines
#2220: handle matching failure more gracefully.
........
r61275 | georg.brandl | 2008-03-06 08:45:52 +0100 (Thu, 06 Mar 2008) | 2 lines
Bug #2220: handle rlcompleter attribute match failure more gracefully.
........
r61278 | martin.v.loewis | 2008-03-06 14:49:47 +0100 (Thu, 06 Mar 2008) | 1 line
Rely on x64 platform configuration when building _bsddb on AMD64.
........
r61279 | martin.v.loewis | 2008-03-06 14:50:28 +0100 (Thu, 06 Mar 2008) | 1 line
Update db-4.4.20 build procedure.
........
r61285 | raymond.hettinger | 2008-03-06 21:52:01 +0100 (Thu, 06 Mar 2008) | 1 line
More tests.
........
r61286 | raymond.hettinger | 2008-03-06 23:51:36 +0100 (Thu, 06 Mar 2008) | 1 line
Issue 2246: itertools grouper object did not participate in GC (should be backported).
........
r61288 | raymond.hettinger | 2008-03-07 02:33:20 +0100 (Fri, 07 Mar 2008) | 1 line
Tweak recipes and tests
........
r61289 | jeffrey.yasskin | 2008-03-07 07:22:15 +0100 (Fri, 07 Mar 2008) | 5 lines
Progress on issue #1193577 by adding a polling .shutdown() method to
SocketServers. The core of the patch was written by Pedro Werneck, but any bugs
are mine. I've also rearranged the code for timeouts in order to avoid
interfering with the shutdown poll.
........
r61290 | nick.coghlan | 2008-03-07 15:13:28 +0100 (Fri, 07 Mar 2008) | 1 line
Speed up with statements by storing the __exit__ method on the stack instead of in a temp variable (bumps the magic number for pyc files)
........
r61298 | andrew.kuchling | 2008-03-07 22:09:23 +0100 (Fri, 07 Mar 2008) | 1 line
Grammar fix
........
r61303 | georg.brandl | 2008-03-08 10:54:06 +0100 (Sat, 08 Mar 2008) | 2 lines
#2253: fix continue vs. finally docs.
........
r61304 | marc-andre.lemburg | 2008-03-08 11:01:43 +0100 (Sat, 08 Mar 2008) | 3 lines
Add new name for Mandrake: Mandriva.
........
r61305 | georg.brandl | 2008-03-08 11:05:24 +0100 (Sat, 08 Mar 2008) | 2 lines
#1533486: fix types in refcount intro.
........
r61312 | facundo.batista | 2008-03-08 17:50:27 +0100 (Sat, 08 Mar 2008) | 5 lines
Issue 1106316. post_mortem()'s parameter, traceback, is now
optional: it defaults to the traceback of the exception that is currently
being handled.
........
r61313 | jeffrey.yasskin | 2008-03-08 19:26:54 +0100 (Sat, 08 Mar 2008) | 2 lines
Add tests for with and finally performance to pybench.
........
r61314 | jeffrey.yasskin | 2008-03-08 21:08:21 +0100 (Sat, 08 Mar 2008) | 2 lines
Fix pybench for pythons < 2.6, tested back to 2.3.
........
r61317 | jeffrey.yasskin | 2008-03-08 22:35:15 +0100 (Sat, 08 Mar 2008) | 3 lines
Well that was dumb. platform.python_implementation returns a function, not a
string.
........
r61329 | georg.brandl | 2008-03-09 16:11:39 +0100 (Sun, 09 Mar 2008) | 2 lines
#2249: document assertTrue and assertFalse.
........
r61332 | neal.norwitz | 2008-03-09 20:03:42 +0100 (Sun, 09 Mar 2008) | 4 lines
Introduce a lock to fix a race condition which caused an exception in the test.
Some buildbots were consistently failing (e.g., amd64).
Also remove a couple of semi-colons.
........
r61344 | raymond.hettinger | 2008-03-11 01:19:07 +0100 (Tue, 11 Mar 2008) | 1 line
Add recipe to docs.
........
r61350 | guido.van.rossum | 2008-03-11 22:18:06 +0100 (Tue, 11 Mar 2008) | 3 lines
Fix the overflows in expandtabs(). "This time for sure!"
(Exploit at request.)
........
r61351 | raymond.hettinger | 2008-03-11 22:37:46 +0100 (Tue, 11 Mar 2008) | 1 line
Improve docs for itemgetter(). Show that it works with slices.
........
r61363 | georg.brandl | 2008-03-13 08:15:56 +0100 (Thu, 13 Mar 2008) | 2 lines
#2265: fix example.
........
r61364 | georg.brandl | 2008-03-13 08:17:14 +0100 (Thu, 13 Mar 2008) | 2 lines
#2270: fix typo.
........
r61365 | georg.brandl | 2008-03-13 08:21:41 +0100 (Thu, 13 Mar 2008) | 2 lines
#1720705: add docs about import/threading interaction, wording by Nick.
........
r61366 | andrew.kuchling | 2008-03-13 12:07:35 +0100 (Thu, 13 Mar 2008) | 1 line
Add class decorators
........
r61367 | raymond.hettinger | 2008-03-13 17:43:17 +0100 (Thu, 13 Mar 2008) | 1 line
Add 2-to-3 support for the itertools moved to builtins or renamed.
........
r61368 | raymond.hettinger | 2008-03-13 17:43:59 +0100 (Thu, 13 Mar 2008) | 1 line
Consistent tense.
........
r61369 | raymond.hettinger | 2008-03-13 20:03:51 +0100 (Thu, 13 Mar 2008) | 1 line
Issue 2274: Add heapq.heappushpop().
........
r61370 | raymond.hettinger | 2008-03-13 20:33:34 +0100 (Thu, 13 Mar 2008) | 1 line
Simplify the nlargest() code using heappushpop().
........
r61371 | brett.cannon | 2008-03-13 21:27:00 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_thread over to unittest. Commits GHOP 237.
Thanks Benjamin Peterson for the patch.
........
r61372 | brett.cannon | 2008-03-13 21:33:10 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_tokenize to doctest.
Done as GHOP 238 by Josip Dzolonga.
........
r61373 | brett.cannon | 2008-03-13 21:47:41 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_contains, test_crypt, and test_select to unittest.
Patch from GHOP 294 by David Marek.
........
r61374 | brett.cannon | 2008-03-13 22:02:16 +0100 (Thu, 13 Mar 2008) | 4 lines
Move test_gdbm to use unittest.
Closes issue #1960. Thanks Giampaolo Rodola.
........
r61375 | brett.cannon | 2008-03-13 22:09:28 +0100 (Thu, 13 Mar 2008) | 4 lines
Convert test_fcntl to unittest.
Closes issue #2055. Thanks Giampaolo Rodola.
........
r61376 | raymond.hettinger | 2008-03-14 06:03:44 +0100 (Fri, 14 Mar 2008) | 1 line
Leave heapreplace() unchanged.
........
r61378 | martin.v.loewis | 2008-03-14 14:56:09 +0100 (Fri, 14 Mar 2008) | 2 lines
Patch #2284: add -x64 option to rt.bat.
........
r61379 | martin.v.loewis | 2008-03-14 14:57:59 +0100 (Fri, 14 Mar 2008) | 2 lines
Use -x64 flag.
........
r61382 | brett.cannon | 2008-03-14 15:03:10 +0100 (Fri, 14 Mar 2008) | 2 lines
Remove a bad test.
........
r61383 | mark.dickinson | 2008-03-14 15:23:37 +0100 (Fri, 14 Mar 2008) | 9 lines
Issue 705836: Fix struct.pack(">f", 1e40) to behave consistently
across platforms: it should now raise OverflowError on all
platforms. (Previously it raised OverflowError only on
non IEEE 754 platforms.)
Also fix the (already existing) test for this behaviour
so that it actually raises TestFailed instead of just
referencing it.
........
r61387 | thomas.heller | 2008-03-14 22:06:21 +0100 (Fri, 14 Mar 2008) | 1 line
Remove unneeded initializer.
........
r61388 | martin.v.loewis | 2008-03-14 22:19:28 +0100 (Fri, 14 Mar 2008) | 2 lines
Run debug version, cd to PCbuild.
........
r61392 | georg.brandl | 2008-03-15 00:10:34 +0100 (Sat, 15 Mar 2008) | 2 lines
Remove obsolete paragraph. #2288.
........
r61395 | georg.brandl | 2008-03-15 01:20:19 +0100 (Sat, 15 Mar 2008) | 2 lines
Fix lots of broken links in the docs, found by Sphinx' external link checker.
........
r61396 | skip.montanaro | 2008-03-15 03:32:49 +0100 (Sat, 15 Mar 2008) | 1 line
note that fork and forkpty raise OSError on failure
........
r61402 | skip.montanaro | 2008-03-15 17:04:45 +0100 (Sat, 15 Mar 2008) | 1 line
add %f format to datetime - issue 1158
........
r61403 | skip.montanaro | 2008-03-15 17:07:11 +0100 (Sat, 15 Mar 2008) | 2 lines
.
........
2008-03-15 21:07:10 -03:00
|
|
|
* giving special meanings to the %z, %Z and %f format codes via a
|
|
|
|
* preprocessing step on the format string.
|
2002-12-30 16:52:32 -04:00
|
|
|
* tzinfoarg is the argument to pass to the object's tzinfo method, if
|
|
|
|
* needed.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2002-12-30 16:52:32 -04:00
|
|
|
wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfoarg)
|
|
|
|
{
|
|
|
|
PyObject *result = NULL; /* guilty until proved innocent */
|
|
|
|
|
|
|
|
PyObject *zreplacement = NULL; /* py string, replacement for %z */
|
|
|
|
PyObject *Zreplacement = NULL; /* py string, replacement for %Z */
|
|
|
|
PyObject *freplacement = NULL; /* py string, replacement for %f */
|
|
|
|
|
|
|
|
const char *pin; /* pointer to next char in input format */
|
|
|
|
Py_ssize_t flen; /* length of input format */
|
|
|
|
char ch; /* next char in input format */
|
|
|
|
|
|
|
|
PyObject *newfmt = NULL; /* py string, the output format */
|
|
|
|
char *pnew; /* pointer to available byte in output format */
|
|
|
|
size_t totalnew; /* number bytes total in output format buffer,
|
|
|
|
exclusive of trailing \0 */
|
|
|
|
size_t usednew; /* number bytes used so far in output format buffer */
|
|
|
|
|
|
|
|
const char *ptoappend; /* ptr to string to append to output buffer */
|
|
|
|
Py_ssize_t ntoappend; /* # of bytes to append to output buffer */
|
|
|
|
|
|
|
|
assert(object && format && timetuple);
|
|
|
|
assert(PyUnicode_Check(format));
|
|
|
|
/* Convert the input format to a C string and size */
|
2016-11-20 03:13:07 -04:00
|
|
|
pin = PyUnicode_AsUTF8AndSize(format, &flen);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (!pin)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Scan the input format, looking for %z/%Z/%f escapes, building
|
|
|
|
* a new format. Since computing the replacements for those codes
|
|
|
|
* is expensive, don't unless they're actually used.
|
|
|
|
*/
|
|
|
|
if (flen > INT_MAX - 1) {
|
|
|
|
PyErr_NoMemory();
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
|
|
|
|
totalnew = flen + 1; /* realistic if no %z/%Z */
|
|
|
|
newfmt = PyBytes_FromStringAndSize(NULL, totalnew);
|
|
|
|
if (newfmt == NULL) goto Done;
|
|
|
|
pnew = PyBytes_AsString(newfmt);
|
|
|
|
usednew = 0;
|
|
|
|
|
|
|
|
while ((ch = *pin++) != '\0') {
|
|
|
|
if (ch != '%') {
|
|
|
|
ptoappend = pin - 1;
|
|
|
|
ntoappend = 1;
|
|
|
|
}
|
|
|
|
else if ((ch = *pin++) == '\0') {
|
2019-06-17 09:27:23 -03:00
|
|
|
/* Null byte follows %, copy only '%'.
|
|
|
|
*
|
2019-01-14 06:23:39 -04:00
|
|
|
* Back the pin up one char so that we catch the null check
|
|
|
|
* the next time through the loop.*/
|
|
|
|
pin--;
|
|
|
|
ptoappend = pin - 1;
|
|
|
|
ntoappend = 1;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
/* A % has been seen and ch is the character after it. */
|
|
|
|
else if (ch == 'z') {
|
|
|
|
if (zreplacement == NULL) {
|
|
|
|
/* format utcoffset */
|
|
|
|
char buf[100];
|
|
|
|
PyObject *tzinfo = get_tzinfo_member(object);
|
|
|
|
zreplacement = PyBytes_FromStringAndSize("", 0);
|
|
|
|
if (zreplacement == NULL) goto Done;
|
|
|
|
if (tzinfo != Py_None && tzinfo != NULL) {
|
|
|
|
assert(tzinfoarg != NULL);
|
|
|
|
if (format_utcoffset(buf,
|
|
|
|
sizeof(buf),
|
|
|
|
"",
|
|
|
|
tzinfo,
|
|
|
|
tzinfoarg) < 0)
|
|
|
|
goto Done;
|
|
|
|
Py_DECREF(zreplacement);
|
|
|
|
zreplacement =
|
|
|
|
PyBytes_FromStringAndSize(buf,
|
|
|
|
strlen(buf));
|
|
|
|
if (zreplacement == NULL)
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(zreplacement != NULL);
|
|
|
|
ptoappend = PyBytes_AS_STRING(zreplacement);
|
|
|
|
ntoappend = PyBytes_GET_SIZE(zreplacement);
|
|
|
|
}
|
|
|
|
else if (ch == 'Z') {
|
|
|
|
/* format tzname */
|
|
|
|
if (Zreplacement == NULL) {
|
|
|
|
Zreplacement = make_Zreplacement(object,
|
|
|
|
tzinfoarg);
|
|
|
|
if (Zreplacement == NULL)
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
assert(Zreplacement != NULL);
|
|
|
|
assert(PyUnicode_Check(Zreplacement));
|
2016-11-20 03:13:07 -04:00
|
|
|
ptoappend = PyUnicode_AsUTF8AndSize(Zreplacement,
|
2010-05-09 12:52:27 -03:00
|
|
|
&ntoappend);
|
2010-12-08 19:31:48 -04:00
|
|
|
if (ptoappend == NULL)
|
|
|
|
goto Done;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
else if (ch == 'f') {
|
|
|
|
/* format microseconds */
|
|
|
|
if (freplacement == NULL) {
|
|
|
|
freplacement = make_freplacement(object);
|
|
|
|
if (freplacement == NULL)
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
assert(freplacement != NULL);
|
|
|
|
assert(PyBytes_Check(freplacement));
|
|
|
|
ptoappend = PyBytes_AS_STRING(freplacement);
|
|
|
|
ntoappend = PyBytes_GET_SIZE(freplacement);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* percent followed by neither z nor Z */
|
|
|
|
ptoappend = pin - 2;
|
|
|
|
ntoappend = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append the ntoappend chars starting at ptoappend to
|
|
|
|
* the new format.
|
|
|
|
*/
|
|
|
|
if (ntoappend == 0)
|
|
|
|
continue;
|
|
|
|
assert(ptoappend != NULL);
|
|
|
|
assert(ntoappend > 0);
|
|
|
|
while (usednew + ntoappend > totalnew) {
|
2012-10-06 14:04:49 -03:00
|
|
|
if (totalnew > (PY_SSIZE_T_MAX >> 1)) { /* overflow */
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_NoMemory();
|
|
|
|
goto Done;
|
|
|
|
}
|
2012-10-06 14:04:49 -03:00
|
|
|
totalnew <<= 1;
|
|
|
|
if (_PyBytes_Resize(&newfmt, totalnew) < 0)
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
|
|
|
pnew = PyBytes_AsString(newfmt) + usednew;
|
|
|
|
}
|
|
|
|
memcpy(pnew, ptoappend, ntoappend);
|
|
|
|
pnew += ntoappend;
|
|
|
|
usednew += ntoappend;
|
|
|
|
assert(usednew <= totalnew);
|
|
|
|
} /* end while() */
|
2019-06-17 09:27:23 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (_PyBytes_Resize(&newfmt, usednew) < 0)
|
|
|
|
goto Done;
|
|
|
|
{
|
|
|
|
PyObject *format;
|
2022-06-14 01:15:26 -03:00
|
|
|
PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime");
|
2011-10-09 05:38:36 -03:00
|
|
|
|
2022-06-14 01:15:26 -03:00
|
|
|
if (strftime == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
|
|
|
format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
|
|
|
|
if (format != NULL) {
|
2022-06-14 01:15:26 -03:00
|
|
|
result = PyObject_CallFunctionObjArgs(strftime,
|
2016-12-09 10:24:31 -04:00
|
|
|
format, timetuple, NULL);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(format);
|
|
|
|
}
|
2022-06-14 01:15:26 -03:00
|
|
|
Py_DECREF(strftime);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
Done:
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_XDECREF(freplacement);
|
|
|
|
Py_XDECREF(zreplacement);
|
|
|
|
Py_XDECREF(Zreplacement);
|
|
|
|
Py_XDECREF(newfmt);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Wrap functions from the time module. These aren't directly available
|
|
|
|
* from C. Perhaps they should be.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Call time.time() and return its result (a Python float). */
|
|
|
|
static PyObject *
|
2002-12-16 16:34:55 -04:00
|
|
|
time_time(void)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
2022-06-14 01:15:26 -03:00
|
|
|
PyObject *time = _PyImport_GetModuleAttrString("time", "time");
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (time != NULL) {
|
2022-06-14 01:15:26 -03:00
|
|
|
result = PyObject_CallNoArgs(time);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(time);
|
|
|
|
}
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Build a time.struct_time. The weekday and day number are automatically
|
|
|
|
* computed from the y,m,d args.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag)
|
|
|
|
{
|
2022-06-14 01:15:26 -03:00
|
|
|
PyObject *struct_time;
|
2016-12-08 19:38:16 -04:00
|
|
|
PyObject *result;
|
|
|
|
|
2022-06-14 01:15:26 -03:00
|
|
|
struct_time = _PyImport_GetModuleAttrString("time", "struct_time");
|
|
|
|
if (struct_time == NULL) {
|
2016-12-08 19:38:16 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-06-14 01:15:26 -03:00
|
|
|
result = PyObject_CallFunction(struct_time, "((iiiiiiiii))",
|
2016-12-08 19:38:16 -04:00
|
|
|
y, m, d,
|
|
|
|
hh, mm, ss,
|
|
|
|
weekday(y, m, d),
|
|
|
|
days_before_month(y, m) + d,
|
|
|
|
dstflag);
|
2022-06-14 01:15:26 -03:00
|
|
|
Py_DECREF(struct_time);
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Miscellaneous helpers.
|
|
|
|
*/
|
|
|
|
|
2019-05-30 23:13:39 -03:00
|
|
|
/* The comparisons here all most naturally compute a cmp()-like result.
|
2002-12-16 16:18:38 -04:00
|
|
|
* This little helper turns that into a bool result for rich comparisons.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
diff_to_bool(int diff, int op)
|
|
|
|
{
|
2017-11-02 07:32:54 -03:00
|
|
|
Py_RETURN_RICHCOMPARE(diff, 0, op);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-02-07 18:50:28 -04:00
|
|
|
/* Raises a "can't compare" TypeError and returns NULL. */
|
|
|
|
static PyObject *
|
|
|
|
cmperror(PyObject *a, PyObject *b)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"can't compare %s to %s",
|
|
|
|
Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name);
|
|
|
|
return NULL;
|
2003-02-07 18:50:28 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Cached Python objects; these are set by the module init function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Conversion factors. */
|
2010-05-09 12:52:27 -03:00
|
|
|
static PyObject *us_per_ms = NULL; /* 1000 */
|
|
|
|
static PyObject *us_per_second = NULL; /* 1000000 */
|
|
|
|
static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */
|
2013-08-27 13:40:23 -03:00
|
|
|
static PyObject *us_per_hour = NULL; /* 1e6 * 3600 as Python int */
|
|
|
|
static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python int */
|
|
|
|
static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python int */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Class implementations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PyDateTime_Delta implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Convert a timedelta to a number of us,
|
2010-05-09 12:52:27 -03:00
|
|
|
* (24*3600*self.days + self.seconds)*1000000 + self.microseconds
|
2013-08-27 13:40:23 -03:00
|
|
|
* as a Python int.
|
2002-12-16 16:18:38 -04:00
|
|
|
* Doing mixed-radix arithmetic by hand instead is excruciating in C,
|
|
|
|
* due to ubiquitous overflow possibilities.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
delta_to_microseconds(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *x1 = NULL;
|
|
|
|
PyObject *x2 = NULL;
|
|
|
|
PyObject *x3 = NULL;
|
|
|
|
PyObject *result = NULL;
|
|
|
|
|
|
|
|
x1 = PyLong_FromLong(GET_TD_DAYS(self));
|
|
|
|
if (x1 == NULL)
|
|
|
|
goto Done;
|
|
|
|
x2 = PyNumber_Multiply(x1, seconds_per_day); /* days in seconds */
|
|
|
|
if (x2 == NULL)
|
|
|
|
goto Done;
|
|
|
|
Py_DECREF(x1);
|
|
|
|
x1 = NULL;
|
|
|
|
|
|
|
|
/* x2 has days in seconds */
|
|
|
|
x1 = PyLong_FromLong(GET_TD_SECONDS(self)); /* seconds */
|
|
|
|
if (x1 == NULL)
|
|
|
|
goto Done;
|
|
|
|
x3 = PyNumber_Add(x1, x2); /* days and seconds in seconds */
|
|
|
|
if (x3 == NULL)
|
|
|
|
goto Done;
|
|
|
|
Py_DECREF(x1);
|
|
|
|
Py_DECREF(x2);
|
2011-02-22 16:15:44 -04:00
|
|
|
/* x1 = */ x2 = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* x3 has days+seconds in seconds */
|
|
|
|
x1 = PyNumber_Multiply(x3, us_per_second); /* us */
|
|
|
|
if (x1 == NULL)
|
|
|
|
goto Done;
|
|
|
|
Py_DECREF(x3);
|
|
|
|
x3 = NULL;
|
|
|
|
|
|
|
|
/* x1 has days+seconds in us */
|
|
|
|
x2 = PyLong_FromLong(GET_TD_MICROSECONDS(self));
|
|
|
|
if (x2 == NULL)
|
|
|
|
goto Done;
|
|
|
|
result = PyNumber_Add(x1, x2);
|
2017-10-23 11:12:28 -03:00
|
|
|
assert(result == NULL || PyLong_CheckExact(result));
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
Done:
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_XDECREF(x1);
|
|
|
|
Py_XDECREF(x2);
|
|
|
|
Py_XDECREF(x3);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
static PyObject *
|
|
|
|
checked_divmod(PyObject *a, PyObject *b)
|
|
|
|
{
|
|
|
|
PyObject *result = PyNumber_Divmod(a, b);
|
|
|
|
if (result != NULL) {
|
|
|
|
if (!PyTuple_Check(result)) {
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"divmod() returned non-tuple (type %.200s)",
|
2020-02-06 22:37:06 -04:00
|
|
|
Py_TYPE(result)->tp_name);
|
2018-11-20 14:41:09 -04:00
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyTuple_GET_SIZE(result) != 2) {
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"divmod() returned a tuple of size %zd",
|
|
|
|
PyTuple_GET_SIZE(result));
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-08-27 13:40:23 -03:00
|
|
|
/* Convert a number of us (as a Python int) to a timedelta.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2003-05-17 12:57:00 -03:00
|
|
|
microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int us;
|
|
|
|
int s;
|
|
|
|
int d;
|
|
|
|
|
|
|
|
PyObject *tuple = NULL;
|
|
|
|
PyObject *num = NULL;
|
|
|
|
PyObject *result = NULL;
|
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
tuple = checked_divmod(pyus, us_per_second);
|
|
|
|
if (tuple == NULL) {
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
2018-11-20 14:41:09 -04:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
num = PyTuple_GET_ITEM(tuple, 1); /* us */
|
|
|
|
us = _PyLong_AsInt(num);
|
2010-05-09 12:52:27 -03:00
|
|
|
num = NULL;
|
2018-11-20 14:41:09 -04:00
|
|
|
if (us == -1 && PyErr_Occurred()) {
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
|
|
|
}
|
2018-11-20 14:41:09 -04:00
|
|
|
if (!(0 <= us && us < 1000000)) {
|
|
|
|
goto BadDivmod;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_INCREF(num);
|
|
|
|
Py_DECREF(tuple);
|
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
tuple = checked_divmod(num, seconds_per_day);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (tuple == NULL)
|
|
|
|
goto Done;
|
|
|
|
Py_DECREF(num);
|
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
|
|
|
|
s = _PyLong_AsInt(num);
|
2010-05-09 12:52:27 -03:00
|
|
|
num = NULL;
|
2018-11-20 14:41:09 -04:00
|
|
|
if (s == -1 && PyErr_Occurred()) {
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
|
|
|
}
|
2018-11-20 14:41:09 -04:00
|
|
|
if (!(0 <= s && s < 24*3600)) {
|
|
|
|
goto BadDivmod;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_INCREF(num);
|
2018-11-20 14:41:09 -04:00
|
|
|
d = _PyLong_AsInt(num);
|
|
|
|
if (d == -1 && PyErr_Occurred()) {
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
result = new_delta_ex(d, s, us, 0, type);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
Done:
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_XDECREF(tuple);
|
|
|
|
Py_XDECREF(num);
|
|
|
|
return result;
|
2018-11-20 14:41:09 -04:00
|
|
|
|
|
|
|
BadDivmod:
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"divmod() returned a value out of range");
|
|
|
|
goto Done;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
#define microseconds_to_delta(pymicros) \
|
|
|
|
microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType)
|
2003-05-17 12:57:00 -03:00
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_in;
|
|
|
|
PyObject *pyus_out;
|
|
|
|
PyObject *result;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_in = delta_to_microseconds(delta);
|
|
|
|
if (pyus_in == NULL)
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
pyus_out = PyNumber_Multiply(intobj, pyus_in);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(pyus_in);
|
|
|
|
if (pyus_out == NULL)
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
result = microseconds_to_delta(pyus_out);
|
|
|
|
Py_DECREF(pyus_out);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2017-09-19 09:58:11 -03:00
|
|
|
static PyObject *
|
|
|
|
get_float_as_integer_ratio(PyObject *floatobj)
|
|
|
|
{
|
|
|
|
PyObject *ratio;
|
|
|
|
|
|
|
|
assert(floatobj && PyFloat_Check(floatobj));
|
2019-07-08 05:19:25 -03:00
|
|
|
ratio = _PyObject_CallMethodIdNoArgs(floatobj, &PyId_as_integer_ratio);
|
2017-09-19 09:58:11 -03:00
|
|
|
if (ratio == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!PyTuple_Check(ratio)) {
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"unexpected return type from as_integer_ratio(): "
|
|
|
|
"expected tuple, got '%.200s'",
|
|
|
|
Py_TYPE(ratio)->tp_name);
|
|
|
|
Py_DECREF(ratio);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyTuple_Size(ratio) != 2) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"as_integer_ratio() must return a 2-tuple");
|
|
|
|
Py_DECREF(ratio);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ratio;
|
|
|
|
}
|
|
|
|
|
2017-10-04 14:30:09 -03:00
|
|
|
/* op is 0 for multiplication, 1 for division */
|
2010-05-31 14:33:47 -03:00
|
|
|
static PyObject *
|
2017-10-04 14:30:09 -03:00
|
|
|
multiply_truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *floatobj, int op)
|
2010-05-31 14:33:47 -03:00
|
|
|
{
|
|
|
|
PyObject *result = NULL;
|
|
|
|
PyObject *pyus_in = NULL, *temp, *pyus_out;
|
|
|
|
PyObject *ratio = NULL;
|
|
|
|
|
|
|
|
pyus_in = delta_to_microseconds(delta);
|
|
|
|
if (pyus_in == NULL)
|
|
|
|
return NULL;
|
2017-09-19 09:58:11 -03:00
|
|
|
ratio = get_float_as_integer_ratio(floatobj);
|
|
|
|
if (ratio == NULL) {
|
2010-05-31 14:33:47 -03:00
|
|
|
goto error;
|
2017-09-19 09:58:11 -03:00
|
|
|
}
|
2017-10-04 14:30:09 -03:00
|
|
|
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, op));
|
2010-05-31 14:33:47 -03:00
|
|
|
Py_DECREF(pyus_in);
|
|
|
|
pyus_in = NULL;
|
|
|
|
if (temp == NULL)
|
|
|
|
goto error;
|
2017-10-04 14:30:09 -03:00
|
|
|
pyus_out = divide_nearest(temp, PyTuple_GET_ITEM(ratio, !op));
|
2010-05-31 14:33:47 -03:00
|
|
|
Py_DECREF(temp);
|
|
|
|
if (pyus_out == NULL)
|
|
|
|
goto error;
|
|
|
|
result = microseconds_to_delta(pyus_out);
|
|
|
|
Py_DECREF(pyus_out);
|
|
|
|
error:
|
|
|
|
Py_XDECREF(pyus_in);
|
|
|
|
Py_XDECREF(ratio);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_in;
|
|
|
|
PyObject *pyus_out;
|
|
|
|
PyObject *result;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_in = delta_to_microseconds(delta);
|
|
|
|
if (pyus_in == NULL)
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_out = PyNumber_FloorDivide(pyus_in, intobj);
|
|
|
|
Py_DECREF(pyus_in);
|
|
|
|
if (pyus_out == NULL)
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
result = microseconds_to_delta(pyus_out);
|
|
|
|
Py_DECREF(pyus_out);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-04-20 19:32:49 -03:00
|
|
|
static PyObject *
|
|
|
|
divide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_left;
|
|
|
|
PyObject *pyus_right;
|
|
|
|
PyObject *result;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_left = delta_to_microseconds(left);
|
|
|
|
if (pyus_left == NULL)
|
|
|
|
return NULL;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_right = delta_to_microseconds(right);
|
|
|
|
if (pyus_right == NULL) {
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
result = PyNumber_FloorDivide(pyus_left, pyus_right);
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
Py_DECREF(pyus_right);
|
|
|
|
return result;
|
2010-04-20 19:32:49 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
truedivide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_left;
|
|
|
|
PyObject *pyus_right;
|
|
|
|
PyObject *result;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_left = delta_to_microseconds(left);
|
|
|
|
if (pyus_left == NULL)
|
|
|
|
return NULL;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_right = delta_to_microseconds(right);
|
|
|
|
if (pyus_right == NULL) {
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
result = PyNumber_TrueDivide(pyus_left, pyus_right);
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
Py_DECREF(pyus_right);
|
|
|
|
return result;
|
2010-04-20 19:32:49 -03:00
|
|
|
}
|
|
|
|
|
2010-05-31 14:33:47 -03:00
|
|
|
static PyObject *
|
|
|
|
truedivide_timedelta_int(PyDateTime_Delta *delta, PyObject *i)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
PyObject *pyus_in, *pyus_out;
|
|
|
|
pyus_in = delta_to_microseconds(delta);
|
|
|
|
if (pyus_in == NULL)
|
|
|
|
return NULL;
|
|
|
|
pyus_out = divide_nearest(pyus_in, i);
|
|
|
|
Py_DECREF(pyus_in);
|
|
|
|
if (pyus_out == NULL)
|
|
|
|
return NULL;
|
|
|
|
result = microseconds_to_delta(pyus_out);
|
|
|
|
Py_DECREF(pyus_out);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
delta_add(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(left) && PyDelta_Check(right)) {
|
|
|
|
/* delta + delta */
|
|
|
|
/* The C-level additions can't overflow because of the
|
|
|
|
* invariant bounds.
|
|
|
|
*/
|
|
|
|
int days = GET_TD_DAYS(left) + GET_TD_DAYS(right);
|
|
|
|
int seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right);
|
|
|
|
int microseconds = GET_TD_MICROSECONDS(left) +
|
|
|
|
GET_TD_MICROSECONDS(right);
|
|
|
|
result = new_delta(days, seconds, microseconds, 1);
|
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_negative(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return new_delta(-GET_TD_DAYS(self),
|
|
|
|
-GET_TD_SECONDS(self),
|
|
|
|
-GET_TD_MICROSECONDS(self),
|
|
|
|
1);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_positive(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Could optimize this (by returning self) if this isn't a
|
|
|
|
* subclass -- but who uses unary + ? Approximately nobody.
|
|
|
|
*/
|
|
|
|
return new_delta(GET_TD_DAYS(self),
|
|
|
|
GET_TD_SECONDS(self),
|
|
|
|
GET_TD_MICROSECONDS(self),
|
|
|
|
0);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_abs(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(GET_TD_MICROSECONDS(self) >= 0);
|
|
|
|
assert(GET_TD_SECONDS(self) >= 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (GET_TD_DAYS(self) < 0)
|
|
|
|
result = delta_negative(self);
|
|
|
|
else
|
|
|
|
result = delta_positive(self);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_subtract(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(left) && PyDelta_Check(right)) {
|
|
|
|
/* delta - delta */
|
2011-04-05 21:07:38 -03:00
|
|
|
/* The C-level additions can't overflow because of the
|
|
|
|
* invariant bounds.
|
|
|
|
*/
|
|
|
|
int days = GET_TD_DAYS(left) - GET_TD_DAYS(right);
|
|
|
|
int seconds = GET_TD_SECONDS(left) - GET_TD_SECONDS(right);
|
|
|
|
int microseconds = GET_TD_MICROSECONDS(left) -
|
|
|
|
GET_TD_MICROSECONDS(right);
|
|
|
|
result = new_delta(days, seconds, microseconds, 1);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
static int
|
|
|
|
delta_cmp(PyObject *self, PyObject *other)
|
|
|
|
{
|
|
|
|
int diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
|
|
|
|
if (diff == 0) {
|
|
|
|
diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
|
|
|
|
if (diff == 0)
|
|
|
|
diff = GET_TD_MICROSECONDS(self) -
|
|
|
|
GET_TD_MICROSECONDS(other);
|
|
|
|
}
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2006-08-24 14:29:38 -03:00
|
|
|
delta_richcompare(PyObject *self, PyObject *other, int op)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(other)) {
|
2010-07-07 20:56:38 -03:00
|
|
|
int diff = delta_cmp(self, other);
|
2010-05-09 12:52:27 -03:00
|
|
|
return diff_to_bool(diff, op);
|
|
|
|
}
|
|
|
|
else {
|
2011-08-10 22:28:54 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *delta_getstate(PyDateTime_Delta *self);
|
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2002-12-16 16:18:38 -04:00
|
|
|
delta_hash(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (self->hashcode == -1) {
|
|
|
|
PyObject *temp = delta_getstate(self);
|
|
|
|
if (temp != NULL) {
|
|
|
|
self->hashcode = PyObject_Hash(temp);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self->hashcode;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_multiply(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(left)) {
|
|
|
|
/* delta * ??? */
|
|
|
|
if (PyLong_Check(right))
|
|
|
|
result = multiply_int_timedelta(right,
|
|
|
|
(PyDateTime_Delta *) left);
|
2010-05-31 14:33:47 -03:00
|
|
|
else if (PyFloat_Check(right))
|
2017-10-04 14:30:09 -03:00
|
|
|
result = multiply_truedivide_timedelta_float(
|
|
|
|
(PyDateTime_Delta *) left, right, 0);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
else if (PyLong_Check(left))
|
|
|
|
result = multiply_int_timedelta(left,
|
2010-05-31 14:33:47 -03:00
|
|
|
(PyDateTime_Delta *) right);
|
|
|
|
else if (PyFloat_Check(left))
|
2017-10-04 14:30:09 -03:00
|
|
|
result = multiply_truedivide_timedelta_float(
|
|
|
|
(PyDateTime_Delta *) right, left, 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_divide(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(left)) {
|
|
|
|
/* delta * ??? */
|
|
|
|
if (PyLong_Check(right))
|
|
|
|
result = divide_timedelta_int(
|
|
|
|
(PyDateTime_Delta *)left,
|
|
|
|
right);
|
|
|
|
else if (PyDelta_Check(right))
|
|
|
|
result = divide_timedelta_timedelta(
|
|
|
|
(PyDateTime_Delta *)left,
|
|
|
|
(PyDateTime_Delta *)right);
|
|
|
|
}
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2010-04-20 19:32:49 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_truedivide(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDelta_Check(left)) {
|
|
|
|
if (PyDelta_Check(right))
|
|
|
|
result = truedivide_timedelta_timedelta(
|
|
|
|
(PyDateTime_Delta *)left,
|
|
|
|
(PyDateTime_Delta *)right);
|
2010-05-31 14:33:47 -03:00
|
|
|
else if (PyFloat_Check(right))
|
2017-10-04 14:30:09 -03:00
|
|
|
result = multiply_truedivide_timedelta_float(
|
|
|
|
(PyDateTime_Delta *)left, right, 1);
|
2010-05-31 14:33:47 -03:00
|
|
|
else if (PyLong_Check(right))
|
|
|
|
result = truedivide_timedelta_int(
|
|
|
|
(PyDateTime_Delta *)left, right);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-04-20 19:32:49 -03:00
|
|
|
static PyObject *
|
|
|
|
delta_remainder(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_left;
|
|
|
|
PyObject *pyus_right;
|
|
|
|
PyObject *pyus_remainder;
|
|
|
|
PyObject *remainder;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2011-08-10 22:28:54 -03:00
|
|
|
if (!PyDelta_Check(left) || !PyDelta_Check(right))
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_left = delta_to_microseconds((PyDateTime_Delta *)left);
|
|
|
|
if (pyus_left == NULL)
|
|
|
|
return NULL;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_right = delta_to_microseconds((PyDateTime_Delta *)right);
|
|
|
|
if (pyus_right == NULL) {
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
pyus_remainder = PyNumber_Remainder(pyus_left, pyus_right);
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
Py_DECREF(pyus_right);
|
|
|
|
if (pyus_remainder == NULL)
|
|
|
|
return NULL;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
remainder = microseconds_to_delta(pyus_remainder);
|
|
|
|
Py_DECREF(pyus_remainder);
|
|
|
|
if (remainder == NULL)
|
|
|
|
return NULL;
|
2010-04-20 19:32:49 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return remainder;
|
2010-04-20 19:32:49 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_divmod(PyObject *left, PyObject *right)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *pyus_left;
|
|
|
|
PyObject *pyus_right;
|
|
|
|
PyObject *divmod;
|
|
|
|
PyObject *delta;
|
|
|
|
PyObject *result;
|
|
|
|
|
2011-08-10 22:28:54 -03:00
|
|
|
if (!PyDelta_Check(left) || !PyDelta_Check(right))
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
pyus_left = delta_to_microseconds((PyDateTime_Delta *)left);
|
|
|
|
if (pyus_left == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pyus_right = delta_to_microseconds((PyDateTime_Delta *)right);
|
|
|
|
if (pyus_right == NULL) {
|
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-20 14:41:09 -04:00
|
|
|
divmod = checked_divmod(pyus_left, pyus_right);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(pyus_left);
|
|
|
|
Py_DECREF(pyus_right);
|
|
|
|
if (divmod == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
|
|
|
|
if (delta == NULL) {
|
|
|
|
Py_DECREF(divmod);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
result = PyTuple_Pack(2, PyTuple_GET_ITEM(divmod, 0), delta);
|
|
|
|
Py_DECREF(delta);
|
|
|
|
Py_DECREF(divmod);
|
|
|
|
return result;
|
2010-04-20 19:32:49 -03:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* Fold in the value of the tag ("seconds", "weeks", etc) component of a
|
|
|
|
* timedelta constructor. sofar is the # of microseconds accounted for
|
|
|
|
* so far, and there are factor microseconds per current unit, the number
|
|
|
|
* of which is given by num. num * factor is added to sofar in a
|
|
|
|
* numerically careful way, and that's the result. Any fractional
|
|
|
|
* microseconds left over (this can happen if num is a float type) are
|
|
|
|
* added into *leftover.
|
|
|
|
* Note that there are many ways this can give an error (NULL) return.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
|
|
|
|
double *leftover)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *prod;
|
|
|
|
PyObject *sum;
|
|
|
|
|
|
|
|
assert(num != NULL);
|
|
|
|
|
|
|
|
if (PyLong_Check(num)) {
|
2018-11-20 14:41:09 -04:00
|
|
|
prod = PyNumber_Multiply(num, factor);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (prod == NULL)
|
|
|
|
return NULL;
|
|
|
|
sum = PyNumber_Add(sofar, prod);
|
|
|
|
Py_DECREF(prod);
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyFloat_Check(num)) {
|
|
|
|
double dnum;
|
|
|
|
double fracpart;
|
|
|
|
double intpart;
|
|
|
|
PyObject *x;
|
|
|
|
PyObject *y;
|
|
|
|
|
|
|
|
/* The Plan: decompose num into an integer part and a
|
|
|
|
* fractional part, num = intpart + fracpart.
|
|
|
|
* Then num * factor ==
|
|
|
|
* intpart * factor + fracpart * factor
|
|
|
|
* and the LHS can be computed exactly in long arithmetic.
|
|
|
|
* The RHS is again broken into an int part and frac part.
|
|
|
|
* and the frac part is added into *leftover.
|
|
|
|
*/
|
|
|
|
dnum = PyFloat_AsDouble(num);
|
|
|
|
if (dnum == -1.0 && PyErr_Occurred())
|
|
|
|
return NULL;
|
|
|
|
fracpart = modf(dnum, &intpart);
|
|
|
|
x = PyLong_FromDouble(intpart);
|
|
|
|
if (x == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prod = PyNumber_Multiply(x, factor);
|
|
|
|
Py_DECREF(x);
|
|
|
|
if (prod == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sum = PyNumber_Add(sofar, prod);
|
|
|
|
Py_DECREF(prod);
|
|
|
|
if (sum == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (fracpart == 0.0)
|
|
|
|
return sum;
|
|
|
|
/* So far we've lost no information. Dealing with the
|
|
|
|
* fractional part requires float arithmetic, and may
|
|
|
|
* lose a little info.
|
|
|
|
*/
|
2017-10-23 11:12:28 -03:00
|
|
|
assert(PyLong_CheckExact(factor));
|
2010-05-09 12:52:27 -03:00
|
|
|
dnum = PyLong_AsDouble(factor);
|
|
|
|
|
|
|
|
dnum *= fracpart;
|
|
|
|
fracpart = modf(dnum, &intpart);
|
|
|
|
x = PyLong_FromDouble(intpart);
|
|
|
|
if (x == NULL) {
|
|
|
|
Py_DECREF(sum);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
y = PyNumber_Add(sum, x);
|
|
|
|
Py_DECREF(sum);
|
|
|
|
Py_DECREF(x);
|
|
|
|
*leftover += fracpart;
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
"unsupported type for timedelta %s component: %s",
|
|
|
|
tag, Py_TYPE(num)->tp_name);
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self = NULL;
|
|
|
|
|
|
|
|
/* Argument objects. */
|
|
|
|
PyObject *day = NULL;
|
|
|
|
PyObject *second = NULL;
|
|
|
|
PyObject *us = NULL;
|
|
|
|
PyObject *ms = NULL;
|
|
|
|
PyObject *minute = NULL;
|
|
|
|
PyObject *hour = NULL;
|
|
|
|
PyObject *week = NULL;
|
|
|
|
|
|
|
|
PyObject *x = NULL; /* running sum of microseconds */
|
|
|
|
PyObject *y = NULL; /* temp sum of microseconds */
|
|
|
|
double leftover_us = 0.0;
|
|
|
|
|
|
|
|
static char *keywords[] = {
|
|
|
|
"days", "seconds", "microseconds", "milliseconds",
|
|
|
|
"minutes", "hours", "weeks", NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOO:__new__",
|
|
|
|
keywords,
|
|
|
|
&day, &second, &us,
|
|
|
|
&ms, &minute, &hour, &week) == 0)
|
|
|
|
goto Done;
|
|
|
|
|
|
|
|
x = PyLong_FromLong(0);
|
|
|
|
if (x == NULL)
|
|
|
|
goto Done;
|
|
|
|
|
|
|
|
#define CLEANUP \
|
|
|
|
Py_DECREF(x); \
|
|
|
|
x = y; \
|
|
|
|
if (x == NULL) \
|
|
|
|
goto Done
|
|
|
|
|
|
|
|
if (us) {
|
2020-10-27 13:12:53 -03:00
|
|
|
y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us);
|
2010-05-09 12:52:27 -03:00
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (ms) {
|
|
|
|
y = accum("milliseconds", x, ms, us_per_ms, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (second) {
|
|
|
|
y = accum("seconds", x, second, us_per_second, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (minute) {
|
|
|
|
y = accum("minutes", x, minute, us_per_minute, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (hour) {
|
|
|
|
y = accum("hours", x, hour, us_per_hour, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (day) {
|
|
|
|
y = accum("days", x, day, us_per_day, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (week) {
|
|
|
|
y = accum("weeks", x, week, us_per_week, &leftover_us);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
if (leftover_us) {
|
|
|
|
/* Round to nearest whole # of us, and add into x. */
|
2013-08-04 15:51:35 -03:00
|
|
|
double whole_us = round(leftover_us);
|
2015-09-08 18:58:54 -03:00
|
|
|
int x_is_odd;
|
2013-08-04 15:51:35 -03:00
|
|
|
PyObject *temp;
|
|
|
|
|
2015-09-08 18:58:54 -03:00
|
|
|
if (fabs(whole_us - leftover_us) == 0.5) {
|
|
|
|
/* We're exactly halfway between two integers. In order
|
|
|
|
* to do round-half-to-even, we must determine whether x
|
|
|
|
* is odd. Note that x is odd when it's last bit is 1. The
|
|
|
|
* code below uses bitwise and operation to check the last
|
|
|
|
* bit. */
|
2020-10-27 13:12:53 -03:00
|
|
|
temp = PyNumber_And(x, _PyLong_GetOne()); /* temp <- x & 1 */
|
2015-09-08 18:58:54 -03:00
|
|
|
if (temp == NULL) {
|
|
|
|
Py_DECREF(x);
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
x_is_odd = PyObject_IsTrue(temp);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
if (x_is_odd == -1) {
|
|
|
|
Py_DECREF(x);
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
whole_us = 2.0 * round((leftover_us + x_is_odd) * 0.5) - x_is_odd;
|
|
|
|
}
|
2013-08-04 15:51:35 -03:00
|
|
|
|
2013-08-27 20:53:39 -03:00
|
|
|
temp = PyLong_FromLong((long)whole_us);
|
2013-08-04 15:51:35 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (temp == NULL) {
|
|
|
|
Py_DECREF(x);
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
y = PyNumber_Add(x, temp);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
CLEANUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
self = microseconds_to_delta_ex(x, type);
|
|
|
|
Py_DECREF(x);
|
2002-12-16 16:18:38 -04:00
|
|
|
Done:
|
2010-05-09 12:52:27 -03:00
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
#undef CLEANUP
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-11-28 15:15:13 -04:00
|
|
|
delta_bool(PyDateTime_Delta *self)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return (GET_TD_DAYS(self) != 0
|
|
|
|
|| GET_TD_SECONDS(self) != 0
|
|
|
|
|| GET_TD_MICROSECONDS(self) != 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_repr(PyDateTime_Delta *self)
|
|
|
|
{
|
2017-07-25 18:51:33 -03:00
|
|
|
PyObject *args = PyUnicode_FromString("");
|
|
|
|
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *sep = "";
|
|
|
|
|
|
|
|
if (GET_TD_DAYS(self) != 0) {
|
|
|
|
Py_SETREF(args, PyUnicode_FromFormat("days=%d", GET_TD_DAYS(self)));
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GET_TD_SECONDS(self) != 0) {
|
|
|
|
Py_SETREF(args, PyUnicode_FromFormat("%U%sseconds=%d", args, sep,
|
|
|
|
GET_TD_SECONDS(self)));
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GET_TD_MICROSECONDS(self) != 0) {
|
|
|
|
Py_SETREF(args, PyUnicode_FromFormat("%U%smicroseconds=%d", args, sep,
|
|
|
|
GET_TD_MICROSECONDS(self)));
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyUnicode_GET_LENGTH(args) == 0) {
|
|
|
|
Py_SETREF(args, PyUnicode_FromString("0"));
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *repr = PyUnicode_FromFormat("%s(%S)", Py_TYPE(self)->tp_name,
|
|
|
|
args);
|
|
|
|
Py_DECREF(args);
|
|
|
|
return repr;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
delta_str(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int us = GET_TD_MICROSECONDS(self);
|
|
|
|
int seconds = GET_TD_SECONDS(self);
|
|
|
|
int minutes = divmod(seconds, 60, &seconds);
|
|
|
|
int hours = divmod(minutes, 60, &minutes);
|
|
|
|
int days = GET_TD_DAYS(self);
|
|
|
|
|
|
|
|
if (days) {
|
|
|
|
if (us)
|
|
|
|
return PyUnicode_FromFormat("%d day%s, %d:%02d:%02d.%06d",
|
|
|
|
days, (days == 1 || days == -1) ? "" : "s",
|
|
|
|
hours, minutes, seconds, us);
|
|
|
|
else
|
|
|
|
return PyUnicode_FromFormat("%d day%s, %d:%02d:%02d",
|
|
|
|
days, (days == 1 || days == -1) ? "" : "s",
|
|
|
|
hours, minutes, seconds);
|
|
|
|
} else {
|
|
|
|
if (us)
|
|
|
|
return PyUnicode_FromFormat("%d:%02d:%02d.%06d",
|
|
|
|
hours, minutes, seconds, us);
|
|
|
|
else
|
|
|
|
return PyUnicode_FromFormat("%d:%02d:%02d",
|
|
|
|
hours, minutes, seconds);
|
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-01-31 21:52:50 -04:00
|
|
|
/* Pickle support, a simple use of __reduce__. */
|
|
|
|
|
2003-01-31 22:54:15 -04:00
|
|
|
/* __getstate__ isn't exposed */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
delta_getstate(PyDateTime_Delta *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return Py_BuildValue("iii", GET_TD_DAYS(self),
|
|
|
|
GET_TD_SECONDS(self),
|
|
|
|
GET_TD_MICROSECONDS(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2009-11-25 19:02:32 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored))
|
2009-11-25 19:02:32 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *total_seconds;
|
|
|
|
PyObject *total_microseconds;
|
2010-05-08 11:35:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
total_microseconds = delta_to_microseconds((PyDateTime_Delta *)self);
|
|
|
|
if (total_microseconds == NULL)
|
|
|
|
return NULL;
|
2010-05-08 11:35:02 -03:00
|
|
|
|
2013-08-04 16:18:58 -03:00
|
|
|
total_seconds = PyNumber_TrueDivide(total_microseconds, us_per_second);
|
2010-05-08 11:35:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(total_microseconds);
|
|
|
|
return total_seconds;
|
2009-11-25 19:02:32 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
delta_reduce(PyDateTime_Delta* self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return Py_BuildValue("ON", Py_TYPE(self), delta_getstate(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define OFFSET(field) offsetof(PyDateTime_Delta, field)
|
|
|
|
|
|
|
|
static PyMemberDef delta_members[] = {
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"days", T_INT, OFFSET(days), READONLY,
|
|
|
|
PyDoc_STR("Number of days.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"seconds", T_INT, OFFSET(seconds), READONLY,
|
|
|
|
PyDoc_STR("Number of seconds (>= 0 and less than 1 day).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"microseconds", T_INT, OFFSET(microseconds), READONLY,
|
|
|
|
PyDoc_STR("Number of microseconds (>= 0 and less than 1 second).")},
|
|
|
|
{NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyMethodDef delta_methods[] = {
|
2018-04-29 15:59:33 -03:00
|
|
|
{"total_seconds", delta_total_seconds, METH_NOARGS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("Total seconds in the duration.")},
|
2009-11-25 19:02:32 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"__reduce__", (PyCFunction)delta_reduce, METH_NOARGS,
|
|
|
|
PyDoc_STR("__reduce__() -> (cls, state)")},
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL},
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char delta_doc[] =
|
2018-10-19 19:43:24 -03:00
|
|
|
PyDoc_STR("Difference between two datetime values.\n\n"
|
|
|
|
"timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, "
|
|
|
|
"minutes=0, hours=0, weeks=0)\n\n"
|
|
|
|
"All arguments are optional and default to 0.\n"
|
|
|
|
"Arguments may be integers or floats, and may be positive or negative.");
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyNumberMethods delta_as_number = {
|
2010-05-09 12:52:27 -03:00
|
|
|
delta_add, /* nb_add */
|
|
|
|
delta_subtract, /* nb_subtract */
|
|
|
|
delta_multiply, /* nb_multiply */
|
|
|
|
delta_remainder, /* nb_remainder */
|
|
|
|
delta_divmod, /* nb_divmod */
|
|
|
|
0, /* nb_power */
|
|
|
|
(unaryfunc)delta_negative, /* nb_negative */
|
|
|
|
(unaryfunc)delta_positive, /* nb_positive */
|
|
|
|
(unaryfunc)delta_abs, /* nb_absolute */
|
|
|
|
(inquiry)delta_bool, /* nb_bool */
|
|
|
|
0, /*nb_invert*/
|
|
|
|
0, /*nb_lshift*/
|
|
|
|
0, /*nb_rshift*/
|
|
|
|
0, /*nb_and*/
|
|
|
|
0, /*nb_xor*/
|
|
|
|
0, /*nb_or*/
|
|
|
|
0, /*nb_int*/
|
|
|
|
0, /*nb_reserved*/
|
|
|
|
0, /*nb_float*/
|
|
|
|
0, /*nb_inplace_add*/
|
|
|
|
0, /*nb_inplace_subtract*/
|
|
|
|
0, /*nb_inplace_multiply*/
|
|
|
|
0, /*nb_inplace_remainder*/
|
|
|
|
0, /*nb_inplace_power*/
|
|
|
|
0, /*nb_inplace_lshift*/
|
|
|
|
0, /*nb_inplace_rshift*/
|
|
|
|
0, /*nb_inplace_and*/
|
|
|
|
0, /*nb_inplace_xor*/
|
|
|
|
0, /*nb_inplace_or*/
|
|
|
|
delta_divide, /* nb_floor_divide */
|
|
|
|
delta_truedivide, /* nb_true_divide */
|
|
|
|
0, /* nb_inplace_floor_divide */
|
|
|
|
0, /* nb_inplace_true_divide */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyTypeObject PyDateTime_DeltaType = {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.timedelta", /* tp_name */
|
|
|
|
sizeof(PyDateTime_Delta), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
0, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-05-09 12:52:27 -03:00
|
|
|
(reprfunc)delta_repr, /* tp_repr */
|
|
|
|
&delta_as_number, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
(hashfunc)delta_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
(reprfunc)delta_str, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
delta_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
delta_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
delta_methods, /* tp_methods */
|
|
|
|
delta_members, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
0, /* tp_alloc */
|
|
|
|
delta_new, /* tp_new */
|
|
|
|
0, /* tp_free */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PyDateTime_Date implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Accessor properties. */
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_year(PyDateTime_Date *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(GET_YEAR(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_month(PyDateTime_Date *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(GET_MONTH(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_day(PyDateTime_Date *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(GET_DAY(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyGetSetDef date_getset[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
{"year", (getter)date_year},
|
|
|
|
{"month", (getter)date_month},
|
|
|
|
{"day", (getter)date_day},
|
|
|
|
{NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Constructors. */
|
|
|
|
|
2006-02-27 13:20:04 -04:00
|
|
|
static char *date_kws[] = {"year", "month", "day", NULL};
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2018-12-07 07:42:10 -04:00
|
|
|
static PyObject *
|
|
|
|
date_from_pickle(PyTypeObject *type, PyObject *state)
|
|
|
|
{
|
|
|
|
PyDateTime_Date *me;
|
|
|
|
|
|
|
|
me = (PyDateTime_Date *) (type->tp_alloc(type, 0));
|
|
|
|
if (me != NULL) {
|
|
|
|
const char *pdata = PyBytes_AS_STRING(state);
|
|
|
|
memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE);
|
|
|
|
me->hashcode = -1;
|
|
|
|
}
|
|
|
|
return (PyObject *)me;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self = NULL;
|
|
|
|
int year;
|
|
|
|
int month;
|
|
|
|
int day;
|
|
|
|
|
|
|
|
/* Check for invocation from pickle with __getstate__ state */
|
2018-12-07 10:48:21 -04:00
|
|
|
if (PyTuple_GET_SIZE(args) == 1) {
|
2018-12-07 07:42:10 -04:00
|
|
|
PyObject *state = PyTuple_GET_ITEM(args, 0);
|
|
|
|
if (PyBytes_Check(state)) {
|
|
|
|
if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE &&
|
|
|
|
MONTH_IS_SANE(PyBytes_AS_STRING(state)[2]))
|
|
|
|
{
|
|
|
|
return date_from_pickle(type, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (PyUnicode_Check(state)) {
|
|
|
|
if (PyUnicode_READY(state)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATE_DATASIZE &&
|
|
|
|
MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2)))
|
|
|
|
{
|
|
|
|
state = PyUnicode_AsLatin1String(state);
|
|
|
|
if (state == NULL) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
|
|
|
|
/* More informative error message. */
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"Failed to encode latin1 string when unpickling "
|
|
|
|
"a date object. "
|
|
|
|
"pickle.load(data, encoding='latin1') is assumed.");
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
self = date_from_pickle(type, state);
|
|
|
|
Py_DECREF(state);
|
|
|
|
return self;
|
2018-11-21 22:37:50 -04:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws,
|
|
|
|
&year, &month, &day)) {
|
|
|
|
self = new_date_ex(year, month, day, type);
|
|
|
|
}
|
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-09-24 05:39:02 -03:00
|
|
|
date_fromtimestamp(PyObject *cls, PyObject *obj)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2016-09-10 16:58:31 -03:00
|
|
|
struct tm tm;
|
2010-05-09 12:52:27 -03:00
|
|
|
time_t t;
|
|
|
|
|
2015-03-29 20:10:14 -03:00
|
|
|
if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_FLOOR) == -1)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2012-03-13 09:35:55 -03:00
|
|
|
|
2016-09-28 18:31:35 -03:00
|
|
|
if (_PyTime_localtime(t, &tm) != 0)
|
2012-03-13 20:15:40 -03:00
|
|
|
return NULL;
|
|
|
|
|
2018-01-16 14:06:31 -04:00
|
|
|
return new_date_subclass_ex(tm.tm_year + 1900,
|
|
|
|
tm.tm_mon + 1,
|
|
|
|
tm.tm_mday,
|
|
|
|
cls);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return new date from current time.
|
|
|
|
* We say this is equivalent to fromtimestamp(time.time()), and the
|
|
|
|
* only way to be sure of that is to *call* time.time(). That's not
|
|
|
|
* generally the same as calling C's time.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
date_today(PyObject *cls, PyObject *dummy)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *time;
|
|
|
|
PyObject *result;
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(fromtimestamp);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
time = time_time();
|
|
|
|
if (time == NULL)
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Note well: today() is a class method, so this may not call
|
|
|
|
* date.fromtimestamp. For example, it may call
|
|
|
|
* datetime.fromtimestamp. That's why we need all the accuracy
|
|
|
|
* time.time() delivers; if someone were gonzo about optimization,
|
|
|
|
* date.today() could get away with plain C time().
|
|
|
|
*/
|
2019-07-11 05:59:05 -03:00
|
|
|
result = _PyObject_CallMethodIdOneArg(cls, &PyId_fromtimestamp, time);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(time);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2018-09-24 05:39:02 -03:00
|
|
|
/*[clinic input]
|
|
|
|
@classmethod
|
|
|
|
datetime.date.fromtimestamp
|
|
|
|
|
|
|
|
timestamp: object
|
|
|
|
/
|
|
|
|
|
|
|
|
Create a date from a POSIX timestamp.
|
|
|
|
|
|
|
|
The timestamp is a number, e.g. created via time.time(), that is interpreted
|
|
|
|
as local time.
|
|
|
|
[clinic start generated code]*/
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2018-09-24 05:39:02 -03:00
|
|
|
datetime_date_fromtimestamp(PyTypeObject *type, PyObject *timestamp)
|
|
|
|
/*[clinic end generated code: output=fd045fda58168869 input=eabb3fe7f40491fe]*/
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2018-09-24 05:39:02 -03:00
|
|
|
return date_fromtimestamp((PyObject *) type, timestamp);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2019-04-27 16:39:40 -03:00
|
|
|
/* bpo-36025: This is a wrapper for API compatibility with the public C API,
|
|
|
|
* which expects a function that takes an *args tuple, whereas the argument
|
|
|
|
* clinic generates code that takes METH_O.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args)
|
|
|
|
{
|
|
|
|
PyObject *timestamp;
|
|
|
|
PyObject *result = NULL;
|
|
|
|
|
|
|
|
if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, ×tamp)) {
|
|
|
|
result = date_fromtimestamp(cls, timestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* Return new date from proleptic Gregorian ordinal. Raises ValueError if
|
|
|
|
* the ordinal is out of range.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
date_fromordinal(PyObject *cls, PyObject *args)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
int ordinal;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyArg_ParseTuple(args, "i:fromordinal", &ordinal)) {
|
|
|
|
int year;
|
|
|
|
int month;
|
|
|
|
int day;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (ordinal < 1)
|
|
|
|
PyErr_SetString(PyExc_ValueError, "ordinal must be "
|
|
|
|
">= 1");
|
|
|
|
else {
|
|
|
|
ord_to_ymd(ordinal, &year, &month, &day);
|
2018-01-16 14:06:31 -04:00
|
|
|
result = new_date_subclass_ex(year, month, day, cls);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
/* Return the new date from a string as generated by date.isoformat() */
|
|
|
|
static PyObject *
|
2018-10-22 13:32:52 -03:00
|
|
|
date_fromisoformat(PyObject *cls, PyObject *dtstr)
|
|
|
|
{
|
2017-12-21 01:33:49 -04:00
|
|
|
assert(dtstr != NULL);
|
|
|
|
|
|
|
|
if (!PyUnicode_Check(dtstr)) {
|
2018-10-22 13:32:52 -03:00
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"fromisoformat: argument must be str");
|
2017-12-21 01:33:49 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_ssize_t len;
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
|
2018-08-23 12:06:20 -03:00
|
|
|
if (dt_ptr == NULL) {
|
|
|
|
goto invalid_string_error;
|
|
|
|
}
|
2017-12-21 01:33:49 -04:00
|
|
|
|
|
|
|
int year = 0, month = 0, day = 0;
|
|
|
|
|
|
|
|
int rv;
|
2022-05-05 21:31:24 -03:00
|
|
|
if (len == 7 || len == 8 || len == 10) {
|
|
|
|
rv = parse_isoformat_date(dt_ptr, len, &year, &month, &day);
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else {
|
2017-12-21 01:33:49 -04:00
|
|
|
rv = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rv < 0) {
|
2018-08-23 12:06:20 -03:00
|
|
|
goto invalid_string_error;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2018-01-16 14:06:31 -04:00
|
|
|
return new_date_subclass_ex(year, month, day, cls);
|
2018-08-23 12:06:20 -03:00
|
|
|
|
|
|
|
invalid_string_error:
|
2018-10-22 13:32:52 -03:00
|
|
|
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
|
2018-08-23 12:06:20 -03:00
|
|
|
return NULL;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:22:03 -03:00
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
|
|
|
|
{
|
|
|
|
static char *keywords[] = {
|
|
|
|
"year", "week", "day", NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
int year, week, day;
|
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "iii:fromisocalendar",
|
|
|
|
keywords,
|
|
|
|
&year, &week, &day) == 0) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
|
|
|
PyErr_Format(PyExc_ValueError,
|
|
|
|
"ISO calendar component out of range");
|
|
|
|
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
|
|
|
|
if (year < MINYEAR || year > MAXYEAR) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
int month;
|
|
|
|
int rv = iso_to_ymd(year, week, day, &year, &month, &day);
|
2019-04-29 10:22:03 -03:00
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
|
|
|
|
if (rv == -2) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Invalid week: %d", week);
|
|
|
|
return NULL;
|
2019-04-29 10:22:03 -03:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
if (rv == -3) {
|
2019-04-29 10:22:03 -03:00
|
|
|
PyErr_Format(PyExc_ValueError, "Invalid day: %d (range is [1, 7])",
|
|
|
|
day);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_date_subclass_ex(year, month, day, cls);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/*
|
|
|
|
* Date arithmetic.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* date + timedelta -> date. If arg negate is true, subtract the timedelta
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
int year = GET_YEAR(date);
|
|
|
|
int month = GET_MONTH(date);
|
|
|
|
int deltadays = GET_TD_DAYS(delta);
|
|
|
|
/* C-level overflow is impossible because |deltadays| < 1e9. */
|
|
|
|
int day = GET_DAY(date) + (negate ? -deltadays : deltadays);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (normalize_date(&year, &month, &day) >= 0)
|
2019-02-04 15:42:04 -04:00
|
|
|
result = new_date_subclass_ex(year, month, day,
|
|
|
|
(PyObject* )Py_TYPE(date));
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_add(PyObject *left, PyObject *right)
|
|
|
|
{
|
2011-08-10 22:28:54 -03:00
|
|
|
if (PyDateTime_Check(left) || PyDateTime_Check(right))
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDate_Check(left)) {
|
|
|
|
/* date + ??? */
|
|
|
|
if (PyDelta_Check(right))
|
|
|
|
/* date + delta */
|
|
|
|
return add_date_timedelta((PyDateTime_Date *) left,
|
|
|
|
(PyDateTime_Delta *) right,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* ??? + date
|
|
|
|
* 'right' must be one of us, or we wouldn't have been called
|
|
|
|
*/
|
|
|
|
if (PyDelta_Check(left))
|
|
|
|
/* delta + date */
|
|
|
|
return add_date_timedelta((PyDateTime_Date *) right,
|
|
|
|
(PyDateTime_Delta *) left,
|
|
|
|
0);
|
|
|
|
}
|
2011-08-10 22:28:54 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_subtract(PyObject *left, PyObject *right)
|
|
|
|
{
|
2011-08-10 22:28:54 -03:00
|
|
|
if (PyDateTime_Check(left) || PyDateTime_Check(right))
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDate_Check(left)) {
|
|
|
|
if (PyDate_Check(right)) {
|
|
|
|
/* date - date */
|
|
|
|
int left_ord = ymd_to_ord(GET_YEAR(left),
|
|
|
|
GET_MONTH(left),
|
|
|
|
GET_DAY(left));
|
|
|
|
int right_ord = ymd_to_ord(GET_YEAR(right),
|
|
|
|
GET_MONTH(right),
|
|
|
|
GET_DAY(right));
|
|
|
|
return new_delta(left_ord - right_ord, 0, 0, 0);
|
|
|
|
}
|
|
|
|
if (PyDelta_Check(right)) {
|
|
|
|
/* date - delta */
|
|
|
|
return add_date_timedelta((PyDateTime_Date *) left,
|
|
|
|
(PyDateTime_Delta *) right,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
}
|
2011-08-10 22:28:54 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Various ways to turn a date into a string. */
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_repr(PyDateTime_Date *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyUnicode_FromFormat("%s(%d, %d, %d)",
|
|
|
|
Py_TYPE(self)->tp_name,
|
|
|
|
GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_isoformat(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyUnicode_FromFormat("%04d-%02d-%02d",
|
|
|
|
GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-05-02 15:39:55 -03:00
|
|
|
/* str() calls the appropriate isoformat() method. */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
|
|
|
date_str(PyDateTime_Date *self)
|
|
|
|
{
|
2019-07-08 05:19:25 -03:00
|
|
|
return _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_isoformat);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_ctime(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return format_ctime(self, 0, 0, 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
/* This method can be inherited, and needs to call the
|
|
|
|
* timetuple() method appropriate to self's class.
|
|
|
|
*/
|
|
|
|
PyObject *result;
|
|
|
|
PyObject *tuple;
|
|
|
|
PyObject *format;
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(timetuple);
|
2010-05-09 12:52:27 -03:00
|
|
|
static char *keywords[] = {"format", NULL};
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "U:strftime", keywords,
|
|
|
|
&format))
|
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2019-07-08 05:19:25 -03:00
|
|
|
tuple = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_timetuple);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (tuple == NULL)
|
|
|
|
return NULL;
|
|
|
|
result = wrap_strftime((PyObject *)self, format, tuple,
|
|
|
|
(PyObject *)self);
|
|
|
|
Py_DECREF(tuple);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2007-09-11 15:06:02 -03:00
|
|
|
static PyObject *
|
|
|
|
date_format(PyDateTime_Date *self, PyObject *args)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *format;
|
2007-09-11 15:06:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (!PyArg_ParseTuple(args, "U:__format__", &format))
|
|
|
|
return NULL;
|
2007-09-11 15:06:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* if the format is zero length, return str(self) */
|
2011-11-20 21:49:52 -04:00
|
|
|
if (PyUnicode_GetLength(format) == 0)
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyObject_Str((PyObject *)self);
|
2007-09-11 15:06:02 -03:00
|
|
|
|
2019-07-11 05:59:05 -03:00
|
|
|
return _PyObject_CallMethodIdOneArg((PyObject *)self, &PyId_strftime,
|
|
|
|
format);
|
2007-09-11 15:06:02 -03:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* ISO methods. */
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_isoweekday(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(dow + 1);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2020-05-16 11:02:59 -03:00
|
|
|
PyDoc_STRVAR(iso_calendar_date__doc__,
|
|
|
|
"The result of date.isocalendar() or datetime.isocalendar()\n\n\
|
|
|
|
This object may be accessed either as a tuple of\n\
|
|
|
|
((year, week, weekday)\n\
|
|
|
|
or via the object attributes as named in the above tuple.");
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PyTupleObject tuple;
|
|
|
|
} PyDateTime_IsoCalendarDate;
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_repr(PyDateTime_IsoCalendarDate *self)
|
|
|
|
{
|
|
|
|
PyObject* year = PyTuple_GetItem((PyObject *)self, 0);
|
|
|
|
if (year == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyObject* week = PyTuple_GetItem((PyObject *)self, 1);
|
|
|
|
if (week == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyObject* weekday = PyTuple_GetItem((PyObject *)self, 2);
|
|
|
|
if (weekday == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PyUnicode_FromFormat("%.200s(year=%S, week=%S, weekday=%S)",
|
|
|
|
Py_TYPE(self)->tp_name, year, week, weekday);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
|
|
{
|
|
|
|
// Construct the tuple that this reduces to
|
|
|
|
PyObject * reduce_tuple = Py_BuildValue(
|
|
|
|
"O((OOO))", &PyTuple_Type,
|
|
|
|
PyTuple_GET_ITEM(self, 0),
|
|
|
|
PyTuple_GET_ITEM(self, 1),
|
|
|
|
PyTuple_GET_ITEM(self, 2)
|
|
|
|
);
|
|
|
|
|
|
|
|
return reduce_tuple;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_year(PyDateTime_IsoCalendarDate *self, void *unused)
|
|
|
|
{
|
|
|
|
PyObject *year = PyTuple_GetItem((PyObject *)self, 0);
|
|
|
|
if (year == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_INCREF(year);
|
|
|
|
return year;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_week(PyDateTime_IsoCalendarDate *self, void *unused)
|
|
|
|
{
|
|
|
|
PyObject *week = PyTuple_GetItem((PyObject *)self, 1);
|
|
|
|
if (week == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_INCREF(week);
|
|
|
|
return week;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_weekday(PyDateTime_IsoCalendarDate *self, void *unused)
|
|
|
|
{
|
|
|
|
PyObject *weekday = PyTuple_GetItem((PyObject *)self, 2);
|
|
|
|
if (weekday == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_INCREF(weekday);
|
|
|
|
return weekday;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyGetSetDef iso_calendar_date_getset[] = {
|
|
|
|
{"year", (getter)iso_calendar_date_year},
|
|
|
|
{"week", (getter)iso_calendar_date_week},
|
|
|
|
{"weekday", (getter)iso_calendar_date_weekday},
|
|
|
|
{NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyMethodDef iso_calendar_date_methods[] = {
|
|
|
|
{"__reduce__", (PyCFunction)iso_calendar_date_reduce, METH_NOARGS,
|
|
|
|
PyDoc_STR("__reduce__() -> (cls, state)")},
|
|
|
|
{NULL, NULL},
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyTypeObject PyDateTime_IsoCalendarDateType = {
|
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
.tp_name = "datetime.IsoCalendarDate",
|
|
|
|
.tp_basicsize = sizeof(PyDateTime_IsoCalendarDate),
|
|
|
|
.tp_repr = (reprfunc) iso_calendar_date_repr,
|
|
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
|
|
.tp_doc = iso_calendar_date__doc__,
|
|
|
|
.tp_methods = iso_calendar_date_methods,
|
|
|
|
.tp_getset = iso_calendar_date_getset,
|
2020-05-28 13:14:46 -03:00
|
|
|
// .tp_base = &PyTuple_Type, // filled in PyInit__datetime
|
2020-05-16 11:02:59 -03:00
|
|
|
.tp_new = iso_calendar_date_new,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*[clinic input]
|
|
|
|
@classmethod
|
|
|
|
datetime.IsoCalendarDate.__new__ as iso_calendar_date_new
|
|
|
|
year: int
|
|
|
|
week: int
|
|
|
|
weekday: int
|
|
|
|
[clinic start generated code]*/
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
iso_calendar_date_new_impl(PyTypeObject *type, int year, int week,
|
|
|
|
int weekday)
|
|
|
|
/*[clinic end generated code: output=383d33d8dc7183a2 input=4f2c663c9d19c4ee]*/
|
|
|
|
|
|
|
|
{
|
|
|
|
PyDateTime_IsoCalendarDate *self;
|
|
|
|
self = (PyDateTime_IsoCalendarDate *) type->tp_alloc(type, 3);
|
|
|
|
if (self == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyTuple_SET_ITEM(self, 0, PyLong_FromLong(year));
|
|
|
|
PyTuple_SET_ITEM(self, 1, PyLong_FromLong(week));
|
|
|
|
PyTuple_SET_ITEM(self, 2, PyLong_FromLong(weekday));
|
|
|
|
|
|
|
|
return (PyObject *)self;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int year = GET_YEAR(self);
|
|
|
|
int week1_monday = iso_week1_monday(year);
|
|
|
|
int today = ymd_to_ord(year, GET_MONTH(self), GET_DAY(self));
|
|
|
|
int week;
|
|
|
|
int day;
|
|
|
|
|
|
|
|
week = divmod(today - week1_monday, 7, &day);
|
|
|
|
if (week < 0) {
|
|
|
|
--year;
|
|
|
|
week1_monday = iso_week1_monday(year);
|
|
|
|
week = divmod(today - week1_monday, 7, &day);
|
|
|
|
}
|
|
|
|
else if (week >= 52 && today >= iso_week1_monday(year + 1)) {
|
|
|
|
++year;
|
|
|
|
week = 0;
|
|
|
|
}
|
2020-05-16 11:02:59 -03:00
|
|
|
|
|
|
|
PyObject* v = iso_calendar_date_new_impl(&PyDateTime_IsoCalendarDateType,
|
|
|
|
year, week + 1, day + 1);
|
|
|
|
if (v == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return v;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Miscellaneous methods. */
|
|
|
|
|
|
|
|
static PyObject *
|
2006-08-24 14:29:38 -03:00
|
|
|
date_richcompare(PyObject *self, PyObject *other, int op)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDate_Check(other)) {
|
|
|
|
int diff = memcmp(((PyDateTime_Date *)self)->data,
|
|
|
|
((PyDateTime_Date *)other)->data,
|
|
|
|
_PyDateTime_DATE_DATASIZE);
|
|
|
|
return diff_to_bool(diff, op);
|
|
|
|
}
|
2011-08-10 22:28:54 -03:00
|
|
|
else
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_timetuple(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return build_struct_time(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self),
|
|
|
|
0, 0, 0, -1);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2002-12-24 01:41:27 -04:00
|
|
|
static PyObject *
|
|
|
|
date_replace(PyDateTime_Date *self, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *clone;
|
|
|
|
PyObject *tuple;
|
|
|
|
int year = GET_YEAR(self);
|
|
|
|
int month = GET_MONTH(self);
|
|
|
|
int day = GET_DAY(self);
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "|iii:replace", date_kws,
|
|
|
|
&year, &month, &day))
|
|
|
|
return NULL;
|
|
|
|
tuple = Py_BuildValue("iii", year, month, day);
|
|
|
|
if (tuple == NULL)
|
|
|
|
return NULL;
|
|
|
|
clone = date_new(Py_TYPE(self), tuple, NULL);
|
|
|
|
Py_DECREF(tuple);
|
|
|
|
return clone;
|
2002-12-24 01:41:27 -04:00
|
|
|
}
|
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2007-08-24 01:05:13 -03:00
|
|
|
generic_hash(unsigned char *data, int len)
|
|
|
|
{
|
2012-01-14 18:31:13 -04:00
|
|
|
return _Py_HashBytes(data, len);
|
2007-08-24 01:05:13 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PyObject *date_getstate(PyDateTime_Date *self);
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2002-12-16 16:18:38 -04:00
|
|
|
date_hash(PyDateTime_Date *self)
|
|
|
|
{
|
2016-09-09 21:46:24 -03:00
|
|
|
if (self->hashcode == -1) {
|
2010-05-09 12:52:27 -03:00
|
|
|
self->hashcode = generic_hash(
|
|
|
|
(unsigned char *)self->data, _PyDateTime_DATE_DATASIZE);
|
2016-09-09 21:46:24 -03:00
|
|
|
}
|
2007-11-21 15:29:53 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return self->hashcode;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_toordinal(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(ymd_to_ord(GET_YEAR(self), GET_MONTH(self),
|
|
|
|
GET_DAY(self)));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
date_weekday(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(dow);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-31 21:52:50 -04:00
|
|
|
/* Pickle support, a simple use of __reduce__. */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-31 22:54:15 -04:00
|
|
|
/* __getstate__ isn't exposed */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2007-08-24 01:05:13 -03:00
|
|
|
date_getstate(PyDateTime_Date *self)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject* field;
|
|
|
|
field = PyBytes_FromStringAndSize((char*)self->data,
|
|
|
|
_PyDateTime_DATE_DATASIZE);
|
|
|
|
return Py_BuildValue("(N)", field);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-30 18:06:23 -04:00
|
|
|
date_reduce(PyDateTime_Date *self, PyObject *arg)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return Py_BuildValue("(ON)", Py_TYPE(self), date_getstate(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef date_methods[] = {
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Class methods: */
|
2018-09-24 05:39:02 -03:00
|
|
|
DATETIME_DATE_FROMTIMESTAMP_METHODDEF
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"fromordinal", (PyCFunction)date_fromordinal, METH_VARARGS |
|
|
|
|
METH_CLASS,
|
|
|
|
PyDoc_STR("int -> date corresponding to a proleptic Gregorian "
|
|
|
|
"ordinal.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
{"fromisoformat", (PyCFunction)date_fromisoformat, METH_O |
|
|
|
|
METH_CLASS,
|
2022-05-05 21:31:24 -03:00
|
|
|
PyDoc_STR("str -> Construct a date from a string in ISO 8601 format.")},
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"fromisocalendar", _PyCFunction_CAST(date_fromisocalendar),
|
2019-04-29 10:22:03 -03:00
|
|
|
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
|
|
|
|
PyDoc_STR("int, int, int -> Construct a date from the ISO year, week "
|
|
|
|
"number and weekday.\n\n"
|
|
|
|
"This is the inverse of the date.isocalendar() function")},
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"today", (PyCFunction)date_today, METH_NOARGS | METH_CLASS,
|
|
|
|
PyDoc_STR("Current date or datetime: same as "
|
|
|
|
"self.__class__.fromtimestamp(time.time()).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Instance methods: */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"ctime", (PyCFunction)date_ctime, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return ctime() style string.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"strftime", _PyCFunction_CAST(date_strftime), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("format -> strftime() style string.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"__format__", (PyCFunction)date_format, METH_VARARGS,
|
|
|
|
PyDoc_STR("Formats self with strftime.")},
|
2007-09-11 15:06:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"timetuple", (PyCFunction)date_timetuple, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return time tuple, compatible with time.localtime().")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"isocalendar", (PyCFunction)date_isocalendar, METH_NOARGS,
|
2020-05-16 11:02:59 -03:00
|
|
|
PyDoc_STR("Return a named tuple containing ISO year, week number, and "
|
2010-05-09 12:52:27 -03:00
|
|
|
"weekday.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"isoformat", (PyCFunction)date_isoformat, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return string in ISO 8601 format, YYYY-MM-DD.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"isoweekday", (PyCFunction)date_isoweekday, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return the day of the week represented by the date.\n"
|
|
|
|
"Monday == 1 ... Sunday == 7")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"toordinal", (PyCFunction)date_toordinal, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return proleptic Gregorian ordinal. January 1 of year "
|
|
|
|
"1 is day 1.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"weekday", (PyCFunction)date_weekday, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return the day of the week represented by the date.\n"
|
|
|
|
"Monday == 0 ... Sunday == 6")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"replace", _PyCFunction_CAST(date_replace), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("Return date with new specified fields.")},
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"__reduce__", (PyCFunction)date_reduce, METH_NOARGS,
|
|
|
|
PyDoc_STR("__reduce__() -> (cls, state)")},
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char date_doc[] =
|
2004-12-19 16:13:24 -04:00
|
|
|
PyDoc_STR("date(year, month, day) --> date object");
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyNumberMethods date_as_number = {
|
2010-05-09 12:52:27 -03:00
|
|
|
date_add, /* nb_add */
|
|
|
|
date_subtract, /* nb_subtract */
|
|
|
|
0, /* nb_multiply */
|
|
|
|
0, /* nb_remainder */
|
|
|
|
0, /* nb_divmod */
|
|
|
|
0, /* nb_power */
|
|
|
|
0, /* nb_negative */
|
|
|
|
0, /* nb_positive */
|
|
|
|
0, /* nb_absolute */
|
|
|
|
0, /* nb_bool */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyTypeObject PyDateTime_DateType = {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.date", /* tp_name */
|
|
|
|
sizeof(PyDateTime_Date), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
0, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-05-09 12:52:27 -03:00
|
|
|
(reprfunc)date_repr, /* tp_repr */
|
|
|
|
&date_as_number, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
(hashfunc)date_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
(reprfunc)date_str, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
date_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
date_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
date_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
date_getset, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
0, /* tp_alloc */
|
|
|
|
date_new, /* tp_new */
|
|
|
|
0, /* tp_free */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2003-01-10 23:39:11 -04:00
|
|
|
* PyDateTime_TZInfo implementation.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* This is a pure abstract base class, so doesn't do anything beyond
|
|
|
|
* raising NotImplemented exceptions. Real tzinfo classes need
|
|
|
|
* to derive from this. This is mostly for clarity, and for efficiency in
|
|
|
|
* datetime and time constructors (their tzinfo arguments need to
|
|
|
|
* be subclasses of this tzinfo class, which is easy and quick to check).
|
|
|
|
*
|
|
|
|
* Note: For reasons having to do with pickling of subclasses, we have
|
|
|
|
* to allow tzinfo objects to be instantiated. This wasn't an issue
|
|
|
|
* in the Python implementation (__init__() could raise NotImplementedError
|
|
|
|
* there without ill effect), but doing so in the C implementation hit a
|
|
|
|
* brick wall.
|
|
|
|
*/
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
tzinfo_nogo(const char* methodname)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_Format(PyExc_NotImplementedError,
|
|
|
|
"a tzinfo subclass must implement %s()",
|
|
|
|
methodname);
|
|
|
|
return NULL;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Methods. A subclass must implement these. */
|
|
|
|
|
2003-01-23 12:36:11 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
tzinfo_tzname(PyDateTime_TZInfo *self, PyObject *dt)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return tzinfo_nogo("tzname");
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2003-01-23 12:36:11 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
tzinfo_utcoffset(PyDateTime_TZInfo *self, PyObject *dt)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return tzinfo_nogo("utcoffset");
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2003-01-23 12:36:11 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
tzinfo_dst(PyDateTime_TZInfo *self, PyObject *dt)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return tzinfo_nogo("dst");
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
|
|
|
|
static PyObject *add_datetime_timedelta(PyDateTime_DateTime *date,
|
|
|
|
PyDateTime_Delta *delta,
|
|
|
|
int factor);
|
|
|
|
static PyObject *datetime_utcoffset(PyObject *self, PyObject *);
|
|
|
|
static PyObject *datetime_dst(PyObject *self, PyObject *);
|
|
|
|
|
2003-01-23 12:36:11 -04:00
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt)
|
2003-01-23 12:36:11 -04:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
PyObject *off = NULL, *dst = NULL;
|
|
|
|
PyDateTime_Delta *delta = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (!PyDateTime_Check(dt)) {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"fromutc: argument must be a datetime");
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
if (GET_DT_TZINFO(dt) != (PyObject *)self) {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
|
|
|
|
"is not self");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
off = datetime_utcoffset(dt, NULL);
|
|
|
|
if (off == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2010-07-07 20:56:38 -03:00
|
|
|
if (off == Py_None) {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
|
|
|
|
"utcoffset() result required");
|
2010-07-07 20:56:38 -03:00
|
|
|
goto Fail;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
dst = datetime_dst(dt, NULL);
|
|
|
|
if (dst == NULL)
|
|
|
|
goto Fail;
|
|
|
|
if (dst == Py_None) {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
|
|
|
|
"dst() result required");
|
2010-07-07 20:56:38 -03:00
|
|
|
goto Fail;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
delta = (PyDateTime_Delta *)delta_subtract(off, dst);
|
|
|
|
if (delta == NULL)
|
|
|
|
goto Fail;
|
|
|
|
result = add_datetime_timedelta((PyDateTime_DateTime *)dt, delta, 1);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (result == NULL)
|
|
|
|
goto Fail;
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(dst);
|
|
|
|
dst = call_dst(GET_DT_TZINFO(dt), result);
|
|
|
|
if (dst == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
goto Fail;
|
2010-07-07 20:56:38 -03:00
|
|
|
if (dst == Py_None)
|
|
|
|
goto Inconsistent;
|
2015-09-27 22:41:55 -03:00
|
|
|
if (delta_bool((PyDateTime_Delta *)dst) != 0) {
|
2016-04-10 12:12:01 -03:00
|
|
|
Py_SETREF(result, add_datetime_timedelta((PyDateTime_DateTime *)result,
|
2016-01-05 15:27:54 -04:00
|
|
|
(PyDateTime_Delta *)dst, 1));
|
2010-07-07 20:56:38 -03:00
|
|
|
if (result == NULL)
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
Py_DECREF(delta);
|
|
|
|
Py_DECREF(dst);
|
|
|
|
Py_DECREF(off);
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
2003-01-23 12:36:11 -04:00
|
|
|
|
|
|
|
Inconsistent:
|
2018-11-05 10:20:25 -04:00
|
|
|
PyErr_SetString(PyExc_ValueError, "fromutc: tz.dst() gave "
|
2010-05-09 12:52:27 -03:00
|
|
|
"inconsistent results; cannot convert");
|
2003-01-23 12:36:11 -04:00
|
|
|
|
2018-02-03 20:36:10 -04:00
|
|
|
/* fall through to failure */
|
2003-01-23 12:36:11 -04:00
|
|
|
Fail:
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_XDECREF(off);
|
|
|
|
Py_XDECREF(dst);
|
|
|
|
Py_XDECREF(delta);
|
|
|
|
Py_XDECREF(result);
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2003-01-23 12:36:11 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* Pickle support. This is solely so that tzinfo subclasses can use
|
2003-01-30 18:06:23 -04:00
|
|
|
* pickling -- tzinfo itself is supposed to be uninstantiable.
|
2003-01-10 23:39:11 -04:00
|
|
|
*/
|
|
|
|
|
2003-01-30 18:06:23 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
|
2003-01-30 18:06:23 -04:00
|
|
|
{
|
2016-08-22 19:11:04 -03:00
|
|
|
PyObject *args, *state;
|
2022-04-06 14:00:14 -03:00
|
|
|
PyObject *getinitargs;
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(__getinitargs__);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2019-09-01 06:03:39 -03:00
|
|
|
if (_PyObject_LookupAttrId(self, &PyId___getinitargs__, &getinitargs) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
if (getinitargs != NULL) {
|
2019-06-17 09:27:23 -03:00
|
|
|
args = PyObject_CallNoArgs(getinitargs);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(getinitargs);
|
|
|
|
}
|
|
|
|
else {
|
2016-08-22 19:11:04 -03:00
|
|
|
args = PyTuple_New(0);
|
2019-09-01 06:03:39 -03:00
|
|
|
}
|
|
|
|
if (args == NULL) {
|
|
|
|
return NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2022-04-06 14:00:14 -03:00
|
|
|
state = _PyObject_GetState(self);
|
|
|
|
if (state == NULL) {
|
2019-09-01 06:03:39 -03:00
|
|
|
Py_DECREF(args);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2022-04-06 14:00:14 -03:00
|
|
|
return Py_BuildValue("(ONN)", Py_TYPE(self), args, state);
|
2003-01-30 18:06:23 -04:00
|
|
|
}
|
2003-01-10 23:39:11 -04:00
|
|
|
|
|
|
|
static PyMethodDef tzinfo_methods[] = {
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"tzname", (PyCFunction)tzinfo_tzname, METH_O,
|
|
|
|
PyDoc_STR("datetime -> string name of time zone.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utcoffset", (PyCFunction)tzinfo_utcoffset, METH_O,
|
2010-06-03 22:51:38 -03:00
|
|
|
PyDoc_STR("datetime -> timedelta showing offset from UTC, negative "
|
|
|
|
"values indicating West of UTC")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"dst", (PyCFunction)tzinfo_dst, METH_O,
|
2017-07-31 11:26:50 -03:00
|
|
|
PyDoc_STR("datetime -> DST offset as timedelta positive east of UTC.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"fromutc", (PyCFunction)tzinfo_fromutc, METH_O,
|
2010-07-03 00:35:27 -03:00
|
|
|
PyDoc_STR("datetime in UTC -> datetime in local time.")},
|
2003-01-23 12:36:11 -04:00
|
|
|
|
2018-04-29 15:59:33 -03:00
|
|
|
{"__reduce__", tzinfo_reduce, METH_NOARGS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("-> (cls, state)")},
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL}
|
2003-01-10 23:39:11 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char tzinfo_doc[] =
|
2003-01-10 23:39:11 -04:00
|
|
|
PyDoc_STR("Abstract base class for time zone info objects.");
|
|
|
|
|
2006-03-22 05:28:35 -04:00
|
|
|
static PyTypeObject PyDateTime_TZInfoType = {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.tzinfo", /* tp_name */
|
|
|
|
sizeof(PyDateTime_TZInfo), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
0, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_repr */
|
|
|
|
0, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
0, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
2010-06-14 11:15:50 -03:00
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
2010-05-09 12:52:27 -03:00
|
|
|
tzinfo_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
0, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
tzinfo_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
0, /* tp_alloc */
|
|
|
|
PyType_GenericNew, /* tp_new */
|
|
|
|
0, /* tp_free */
|
2003-01-10 23:39:11 -04:00
|
|
|
};
|
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
static char *timezone_kws[] = {"offset", "name", NULL};
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|
|
|
{
|
|
|
|
PyObject *offset;
|
|
|
|
PyObject *name = NULL;
|
2016-10-23 09:12:25 -03:00
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws,
|
|
|
|
&PyDateTime_DeltaType, &offset, &name))
|
2010-06-14 11:15:50 -03:00
|
|
|
return new_timezone(offset, name);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
timezone_dealloc(PyDateTime_TimeZone *self)
|
|
|
|
{
|
|
|
|
Py_CLEAR(self->offset);
|
|
|
|
Py_CLEAR(self->name);
|
|
|
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_richcompare(PyDateTime_TimeZone *self,
|
|
|
|
PyDateTime_TimeZone *other, int op)
|
|
|
|
{
|
2011-08-10 22:28:54 -03:00
|
|
|
if (op != Py_EQ && op != Py_NE)
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2019-08-22 16:24:25 -03:00
|
|
|
if (!PyTimezone_Check(other)) {
|
2019-08-04 06:38:46 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2012-09-22 04:23:12 -03:00
|
|
|
}
|
2010-06-14 11:15:50 -03:00
|
|
|
return delta_richcompare(self->offset, other->offset, op);
|
|
|
|
}
|
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2010-06-14 11:15:50 -03:00
|
|
|
timezone_hash(PyDateTime_TimeZone *self)
|
|
|
|
{
|
|
|
|
return delta_hash((PyDateTime_Delta *)self->offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check argument type passed to tzname, utcoffset, or dst methods.
|
|
|
|
Returns 0 for good argument. Returns -1 and sets exception info
|
|
|
|
otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
_timezone_check_argument(PyObject *dt, const char *meth)
|
|
|
|
{
|
|
|
|
if (dt == Py_None || PyDateTime_Check(dt))
|
|
|
|
return 0;
|
|
|
|
PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance"
|
|
|
|
" or None, not %.200s", meth, Py_TYPE(dt)->tp_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-07-06 20:19:45 -03:00
|
|
|
static PyObject *
|
|
|
|
timezone_repr(PyDateTime_TimeZone *self)
|
|
|
|
{
|
|
|
|
/* Note that although timezone is not subclassable, it is convenient
|
|
|
|
to use Py_TYPE(self)->tp_name here. */
|
|
|
|
const char *type_name = Py_TYPE(self)->tp_name;
|
|
|
|
|
|
|
|
if (((PyObject *)self) == PyDateTime_TimeZone_UTC)
|
|
|
|
return PyUnicode_FromFormat("%s.utc", type_name);
|
|
|
|
|
|
|
|
if (self->name == NULL)
|
|
|
|
return PyUnicode_FromFormat("%s(%R)", type_name, self->offset);
|
|
|
|
|
|
|
|
return PyUnicode_FromFormat("%s(%R, %R)", type_name, self->offset,
|
|
|
|
self->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
static PyObject *
|
|
|
|
timezone_str(PyDateTime_TimeZone *self)
|
|
|
|
{
|
2017-07-31 11:26:50 -03:00
|
|
|
int hours, minutes, seconds, microseconds;
|
2010-06-14 11:15:50 -03:00
|
|
|
PyObject *offset;
|
|
|
|
char sign;
|
|
|
|
|
|
|
|
if (self->name != NULL) {
|
|
|
|
Py_INCREF(self->name);
|
|
|
|
return self->name;
|
|
|
|
}
|
2015-09-07 19:12:49 -03:00
|
|
|
if ((PyObject *)self == PyDateTime_TimeZone_UTC ||
|
2015-09-06 14:07:21 -03:00
|
|
|
(GET_TD_DAYS(self->offset) == 0 &&
|
|
|
|
GET_TD_SECONDS(self->offset) == 0 &&
|
|
|
|
GET_TD_MICROSECONDS(self->offset) == 0))
|
|
|
|
return PyUnicode_FromString("UTC");
|
2010-06-14 11:15:50 -03:00
|
|
|
/* Offset is normalized, so it is negative if days < 0 */
|
|
|
|
if (GET_TD_DAYS(self->offset) < 0) {
|
|
|
|
sign = '-';
|
|
|
|
offset = delta_negative((PyDateTime_Delta *)self->offset);
|
|
|
|
if (offset == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sign = '+';
|
|
|
|
offset = self->offset;
|
|
|
|
Py_INCREF(offset);
|
|
|
|
}
|
|
|
|
/* Offset is not negative here. */
|
2017-07-31 11:26:50 -03:00
|
|
|
microseconds = GET_TD_MICROSECONDS(offset);
|
2010-06-14 11:15:50 -03:00
|
|
|
seconds = GET_TD_SECONDS(offset);
|
|
|
|
Py_DECREF(offset);
|
|
|
|
minutes = divmod(seconds, 60, &seconds);
|
|
|
|
hours = divmod(minutes, 60, &minutes);
|
2017-07-31 11:26:50 -03:00
|
|
|
if (microseconds != 0) {
|
|
|
|
return PyUnicode_FromFormat("UTC%c%02d:%02d:%02d.%06d",
|
|
|
|
sign, hours, minutes,
|
|
|
|
seconds, microseconds);
|
|
|
|
}
|
|
|
|
if (seconds != 0) {
|
|
|
|
return PyUnicode_FromFormat("UTC%c%02d:%02d:%02d",
|
|
|
|
sign, hours, minutes, seconds);
|
|
|
|
}
|
2011-03-21 14:15:42 -03:00
|
|
|
return PyUnicode_FromFormat("UTC%c%02d:%02d", sign, hours, minutes);
|
2010-06-14 11:15:50 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_tzname(PyDateTime_TimeZone *self, PyObject *dt)
|
|
|
|
{
|
|
|
|
if (_timezone_check_argument(dt, "tzname") == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return timezone_str(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_utcoffset(PyDateTime_TimeZone *self, PyObject *dt)
|
|
|
|
{
|
|
|
|
if (_timezone_check_argument(dt, "utcoffset") == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Py_INCREF(self->offset);
|
|
|
|
return self->offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_dst(PyObject *self, PyObject *dt)
|
|
|
|
{
|
|
|
|
if (_timezone_check_argument(dt, "dst") == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt)
|
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
if (!PyDateTime_Check(dt)) {
|
2010-06-14 11:15:50 -03:00
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"fromutc: argument must be a datetime");
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
if (!HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
|
2010-06-14 11:15:50 -03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
|
|
|
|
"is not self");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return add_datetime_timedelta(dt, (PyDateTime_Delta *)self->offset, 1);
|
|
|
|
}
|
|
|
|
|
2010-06-23 18:40:15 -03:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
timezone_getinitargs(PyDateTime_TimeZone *self, PyObject *Py_UNUSED(ignored))
|
2010-06-23 18:40:15 -03:00
|
|
|
{
|
|
|
|
if (self->name == NULL)
|
|
|
|
return Py_BuildValue("(O)", self->offset);
|
|
|
|
return Py_BuildValue("(OO)", self->offset, self->name);
|
|
|
|
}
|
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
static PyMethodDef timezone_methods[] = {
|
|
|
|
{"tzname", (PyCFunction)timezone_tzname, METH_O,
|
|
|
|
PyDoc_STR("If name is specified when timezone is created, returns the name."
|
2010-06-15 16:24:52 -03:00
|
|
|
" Otherwise returns offset as 'UTC(+|-)HH:MM'.")},
|
2010-06-14 11:15:50 -03:00
|
|
|
|
|
|
|
{"utcoffset", (PyCFunction)timezone_utcoffset, METH_O,
|
2010-06-15 16:24:52 -03:00
|
|
|
PyDoc_STR("Return fixed offset.")},
|
2010-06-14 11:15:50 -03:00
|
|
|
|
|
|
|
{"dst", (PyCFunction)timezone_dst, METH_O,
|
2010-06-15 16:24:52 -03:00
|
|
|
PyDoc_STR("Return None.")},
|
2010-06-14 11:15:50 -03:00
|
|
|
|
|
|
|
{"fromutc", (PyCFunction)timezone_fromutc, METH_O,
|
|
|
|
PyDoc_STR("datetime in UTC -> datetime in local time.")},
|
|
|
|
|
2010-06-23 18:40:15 -03:00
|
|
|
{"__getinitargs__", (PyCFunction)timezone_getinitargs, METH_NOARGS,
|
|
|
|
PyDoc_STR("pickle support")},
|
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char timezone_doc[] =
|
2010-06-14 11:15:50 -03:00
|
|
|
PyDoc_STR("Fixed offset from UTC implementation of tzinfo.");
|
|
|
|
|
|
|
|
static PyTypeObject PyDateTime_TimeZoneType = {
|
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.timezone", /* tp_name */
|
|
|
|
sizeof(PyDateTime_TimeZone), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
(destructor)timezone_dealloc, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-06-14 11:15:50 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-07-06 20:19:45 -03:00
|
|
|
(reprfunc)timezone_repr, /* tp_repr */
|
2010-06-14 11:15:50 -03:00
|
|
|
0, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
(hashfunc)timezone_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
(reprfunc)timezone_str, /* tp_str */
|
|
|
|
0, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
|
|
timezone_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
(richcmpfunc)timezone_richcompare,/* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
timezone_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
2020-05-28 13:14:46 -03:00
|
|
|
0, /* tp_base; filled in PyInit__datetime */
|
2010-06-14 11:15:50 -03:00
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
0, /* tp_alloc */
|
|
|
|
timezone_new, /* tp_new */
|
|
|
|
};
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* PyDateTime_Time implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Accessor properties.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
time_hour(PyDateTime_Time *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(TIME_GET_HOUR(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
time_minute(PyDateTime_Time *self, void *unused)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(TIME_GET_MINUTE(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* The name time_second conflicted with some platform header file. */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
py_time_second(PyDateTime_Time *self, void *unused)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(TIME_GET_SECOND(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
time_microsecond(PyDateTime_Time *self, void *unused)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(TIME_GET_MICROSECOND(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyObject *
|
|
|
|
time_tzinfo(PyDateTime_Time *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = HASTZINFO(self) ? self->tzinfo : Py_None;
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
time_fold(PyDateTime_Time *self, void *unused)
|
|
|
|
{
|
|
|
|
return PyLong_FromLong(TIME_GET_FOLD(self));
|
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyGetSetDef time_getset[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
{"hour", (getter)time_hour},
|
|
|
|
{"minute", (getter)time_minute},
|
|
|
|
{"second", (getter)py_time_second},
|
|
|
|
{"microsecond", (getter)time_microsecond},
|
2016-07-22 19:47:04 -03:00
|
|
|
{"tzinfo", (getter)time_tzinfo},
|
|
|
|
{"fold", (getter)time_fold},
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* Constructors.
|
|
|
|
*/
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2006-02-27 13:20:04 -04:00
|
|
|
static char *time_kws[] = {"hour", "minute", "second", "microsecond",
|
2016-07-22 19:47:04 -03:00
|
|
|
"tzinfo", "fold", NULL};
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2018-12-07 07:42:10 -04:00
|
|
|
static PyObject *
|
|
|
|
time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo)
|
|
|
|
{
|
|
|
|
PyDateTime_Time *me;
|
|
|
|
char aware = (char)(tzinfo != Py_None);
|
|
|
|
|
|
|
|
if (aware && check_tzinfo_subclass(tzinfo) < 0) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
me = (PyDateTime_Time *) (type->tp_alloc(type, aware));
|
|
|
|
if (me != NULL) {
|
|
|
|
const char *pdata = PyBytes_AS_STRING(state);
|
|
|
|
|
|
|
|
memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE);
|
|
|
|
me->hashcode = -1;
|
|
|
|
me->hastzinfo = aware;
|
|
|
|
if (aware) {
|
|
|
|
Py_INCREF(tzinfo);
|
|
|
|
me->tzinfo = tzinfo;
|
|
|
|
}
|
|
|
|
if (pdata[0] & (1 << 7)) {
|
|
|
|
me->data[0] -= 128;
|
|
|
|
me->fold = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
me->fold = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (PyObject *)me;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self = NULL;
|
|
|
|
int hour = 0;
|
|
|
|
int minute = 0;
|
|
|
|
int second = 0;
|
|
|
|
int usecond = 0;
|
|
|
|
PyObject *tzinfo = Py_None;
|
2016-07-22 19:47:04 -03:00
|
|
|
int fold = 0;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* Check for invocation from pickle with __getstate__ state */
|
2018-12-07 07:42:10 -04:00
|
|
|
if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) {
|
|
|
|
PyObject *state = PyTuple_GET_ITEM(args, 0);
|
|
|
|
if (PyTuple_GET_SIZE(args) == 2) {
|
|
|
|
tzinfo = PyTuple_GET_ITEM(args, 1);
|
|
|
|
}
|
|
|
|
if (PyBytes_Check(state)) {
|
|
|
|
if (PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE &&
|
|
|
|
(0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24)
|
|
|
|
{
|
|
|
|
return time_from_pickle(type, state, tzinfo);
|
2016-07-22 19:47:04 -03:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
}
|
|
|
|
else if (PyUnicode_Check(state)) {
|
|
|
|
if (PyUnicode_READY(state)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyUnicode_GET_LENGTH(state) == _PyDateTime_TIME_DATASIZE &&
|
2019-08-29 04:36:15 -03:00
|
|
|
(0x7F & PyUnicode_READ_CHAR(state, 0)) < 24)
|
2018-12-07 07:42:10 -04:00
|
|
|
{
|
|
|
|
state = PyUnicode_AsLatin1String(state);
|
|
|
|
if (state == NULL) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
|
|
|
|
/* More informative error message. */
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"Failed to encode latin1 string when unpickling "
|
|
|
|
"a time object. "
|
|
|
|
"pickle.load(data, encoding='latin1') is assumed.");
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-11-21 22:37:50 -04:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
self = time_from_pickle(type, state, tzinfo);
|
|
|
|
Py_DECREF(state);
|
|
|
|
return self;
|
2016-07-22 19:47:04 -03:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
tzinfo = Py_None;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws,
|
2010-05-09 12:52:27 -03:00
|
|
|
&hour, &minute, &second, &usecond,
|
2016-07-22 19:47:04 -03:00
|
|
|
&tzinfo, &fold)) {
|
|
|
|
self = new_time_ex2(hour, minute, second, usecond, tzinfo, fold,
|
|
|
|
type);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* Destructor.
|
|
|
|
*/
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static void
|
|
|
|
time_dealloc(PyDateTime_Time *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (HASTZINFO(self)) {
|
|
|
|
Py_XDECREF(self->tzinfo);
|
|
|
|
}
|
|
|
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* Indirect access to tzinfo methods.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
2003-01-10 23:39:11 -04:00
|
|
|
|
|
|
|
/* These are all METH_NOARGS, so don't need to check the arglist. */
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
time_utcoffset(PyObject *self, PyObject *unused) {
|
|
|
|
return call_utcoffset(GET_TIME_TZINFO(self), Py_None);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2003-01-09 23:49:02 -04:00
|
|
|
|
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
time_dst(PyObject *self, PyObject *unused) {
|
|
|
|
return call_dst(GET_TIME_TZINFO(self), Py_None);
|
2003-01-09 23:49:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
time_tzname(PyDateTime_Time *self, PyObject *unused) {
|
2010-07-07 20:56:38 -03:00
|
|
|
return call_tzname(GET_TIME_TZINFO(self), Py_None);
|
2003-01-09 23:49:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Various ways to turn a time into a string.
|
|
|
|
*/
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
time_repr(PyDateTime_Time *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
const char *type_name = Py_TYPE(self)->tp_name;
|
|
|
|
int h = TIME_GET_HOUR(self);
|
|
|
|
int m = TIME_GET_MINUTE(self);
|
|
|
|
int s = TIME_GET_SECOND(self);
|
|
|
|
int us = TIME_GET_MICROSECOND(self);
|
2016-07-22 19:47:04 -03:00
|
|
|
int fold = TIME_GET_FOLD(self);
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
|
|
|
|
if (us)
|
|
|
|
result = PyUnicode_FromFormat("%s(%d, %d, %d, %d)",
|
|
|
|
type_name, h, m, s, us);
|
|
|
|
else if (s)
|
|
|
|
result = PyUnicode_FromFormat("%s(%d, %d, %d)",
|
|
|
|
type_name, h, m, s);
|
|
|
|
else
|
|
|
|
result = PyUnicode_FromFormat("%s(%d, %d)", type_name, h, m);
|
|
|
|
if (result != NULL && HASTZINFO(self))
|
|
|
|
result = append_keyword_tzinfo(result, self->tzinfo);
|
2016-07-22 19:47:04 -03:00
|
|
|
if (result != NULL && fold)
|
|
|
|
result = append_keyword_fold(result, fold);
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
time_str(PyDateTime_Time *self)
|
|
|
|
{
|
2019-07-08 05:19:25 -03:00
|
|
|
return _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_isoformat);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2016-03-06 15:58:43 -04:00
|
|
|
time_isoformat(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
char buf[100];
|
2020-02-24 02:40:43 -04:00
|
|
|
const char *timespec = NULL;
|
2016-03-06 15:58:43 -04:00
|
|
|
static char *keywords[] = {"timespec", NULL};
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result;
|
2013-01-27 00:20:14 -04:00
|
|
|
int us = TIME_GET_MICROSECOND(self);
|
2020-02-24 02:40:43 -04:00
|
|
|
static const char *specs[][2] = {
|
2016-03-06 15:58:43 -04:00
|
|
|
{"hours", "%02d"},
|
|
|
|
{"minutes", "%02d:%02d"},
|
|
|
|
{"seconds", "%02d:%02d:%02d"},
|
|
|
|
{"milliseconds", "%02d:%02d:%02d.%03d"},
|
|
|
|
{"microseconds", "%02d:%02d:%02d.%06d"},
|
|
|
|
};
|
|
|
|
size_t given_spec;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2016-03-06 15:58:43 -04:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|s:isoformat", keywords, ×pec))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (timespec == NULL || strcmp(timespec, "auto") == 0) {
|
|
|
|
if (us == 0) {
|
|
|
|
/* seconds */
|
|
|
|
given_spec = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* microseconds */
|
|
|
|
given_spec = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (given_spec = 0; given_spec < Py_ARRAY_LENGTH(specs); given_spec++) {
|
|
|
|
if (strcmp(timespec, specs[given_spec][0]) == 0) {
|
|
|
|
if (given_spec == 3) {
|
|
|
|
/* milliseconds */
|
|
|
|
us = us / 1000;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (given_spec == Py_ARRAY_LENGTH(specs)) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Unknown timespec value");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = PyUnicode_FromFormat(specs[given_spec][1],
|
|
|
|
TIME_GET_HOUR(self), TIME_GET_MINUTE(self),
|
|
|
|
TIME_GET_SECOND(self), us);
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (result == NULL || !HASTZINFO(self) || self->tzinfo == Py_None)
|
2010-05-09 12:52:27 -03:00
|
|
|
return result;
|
|
|
|
|
|
|
|
/* We need to append the UTC offset. */
|
|
|
|
if (format_utcoffset(buf, sizeof(buf), ":", self->tzinfo,
|
|
|
|
Py_None) < 0) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyUnicode_AppendAndDel(&result, PyUnicode_FromString(buf));
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result;
|
|
|
|
PyObject *tuple;
|
|
|
|
PyObject *format;
|
|
|
|
static char *keywords[] = {"format", NULL};
|
|
|
|
|
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "U:strftime", keywords,
|
|
|
|
&format))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Python's strftime does insane things with the year part of the
|
|
|
|
* timetuple. The year is forced to (the otherwise nonsensical)
|
2011-01-07 20:13:34 -04:00
|
|
|
* 1900 to work around that.
|
2010-05-09 12:52:27 -03:00
|
|
|
*/
|
|
|
|
tuple = Py_BuildValue("iiiiiiiii",
|
|
|
|
1900, 1, 1, /* year, month, day */
|
|
|
|
TIME_GET_HOUR(self),
|
|
|
|
TIME_GET_MINUTE(self),
|
|
|
|
TIME_GET_SECOND(self),
|
|
|
|
0, 1, -1); /* weekday, daynum, dst */
|
|
|
|
if (tuple == NULL)
|
|
|
|
return NULL;
|
|
|
|
assert(PyTuple_Size(tuple) == 9);
|
|
|
|
result = wrap_strftime((PyObject *)self, format, tuple,
|
|
|
|
Py_None);
|
|
|
|
Py_DECREF(tuple);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-09 23:49:02 -04:00
|
|
|
/*
|
|
|
|
* Miscellaneous methods.
|
|
|
|
*/
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyObject *
|
2006-08-24 14:29:38 -03:00
|
|
|
time_richcompare(PyObject *self, PyObject *other, int op)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
PyObject *offset1, *offset2;
|
2010-05-09 12:52:27 -03:00
|
|
|
int diff;
|
|
|
|
|
2011-08-10 22:28:54 -03:00
|
|
|
if (! PyTime_Check(other))
|
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2010-07-07 20:56:38 -03:00
|
|
|
|
|
|
|
if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) {
|
|
|
|
diff = memcmp(((PyDateTime_Time *)self)->data,
|
|
|
|
((PyDateTime_Time *)other)->data,
|
|
|
|
_PyDateTime_TIME_DATASIZE);
|
|
|
|
return diff_to_bool(diff, op);
|
|
|
|
}
|
|
|
|
offset1 = time_utcoffset(self, NULL);
|
|
|
|
if (offset1 == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2010-07-07 20:56:38 -03:00
|
|
|
offset2 = time_utcoffset(other, NULL);
|
|
|
|
if (offset2 == NULL)
|
|
|
|
goto done;
|
2010-05-09 12:52:27 -03:00
|
|
|
/* If they're both naive, or both aware and have the same offsets,
|
|
|
|
* we get off cheap. Note that if they're both naive, offset1 ==
|
2010-07-07 20:56:38 -03:00
|
|
|
* offset2 == Py_None at this point.
|
2010-05-09 12:52:27 -03:00
|
|
|
*/
|
2010-07-07 20:56:38 -03:00
|
|
|
if ((offset1 == offset2) ||
|
|
|
|
(PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
|
|
|
|
delta_cmp(offset1, offset2) == 0)) {
|
2010-05-09 12:52:27 -03:00
|
|
|
diff = memcmp(((PyDateTime_Time *)self)->data,
|
|
|
|
((PyDateTime_Time *)other)->data,
|
|
|
|
_PyDateTime_TIME_DATASIZE);
|
2010-07-07 20:56:38 -03:00
|
|
|
result = diff_to_bool(diff, op);
|
|
|
|
}
|
|
|
|
/* The hard case: both aware with different UTC offsets */
|
|
|
|
else if (offset1 != Py_None && offset2 != Py_None) {
|
|
|
|
int offsecs1, offsecs2;
|
|
|
|
assert(offset1 != offset2); /* else last "if" handled it */
|
|
|
|
offsecs1 = TIME_GET_HOUR(self) * 3600 +
|
|
|
|
TIME_GET_MINUTE(self) * 60 +
|
|
|
|
TIME_GET_SECOND(self) -
|
|
|
|
GET_TD_DAYS(offset1) * 86400 -
|
|
|
|
GET_TD_SECONDS(offset1);
|
|
|
|
offsecs2 = TIME_GET_HOUR(other) * 3600 +
|
|
|
|
TIME_GET_MINUTE(other) * 60 +
|
|
|
|
TIME_GET_SECOND(other) -
|
|
|
|
GET_TD_DAYS(offset2) * 86400 -
|
|
|
|
GET_TD_SECONDS(offset2);
|
|
|
|
diff = offsecs1 - offsecs2;
|
2010-05-09 12:52:27 -03:00
|
|
|
if (diff == 0)
|
|
|
|
diff = TIME_GET_MICROSECOND(self) -
|
|
|
|
TIME_GET_MICROSECOND(other);
|
2010-07-07 20:56:38 -03:00
|
|
|
result = diff_to_bool(diff, op);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2012-06-15 21:19:47 -03:00
|
|
|
else if (op == Py_EQ) {
|
|
|
|
result = Py_False;
|
|
|
|
Py_INCREF(result);
|
|
|
|
}
|
|
|
|
else if (op == Py_NE) {
|
|
|
|
result = Py_True;
|
|
|
|
Py_INCREF(result);
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
else {
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"can't compare offset-naive and "
|
|
|
|
"offset-aware times");
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
Py_DECREF(offset1);
|
|
|
|
Py_XDECREF(offset2);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2002-12-16 16:18:38 -04:00
|
|
|
time_hash(PyDateTime_Time *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (self->hashcode == -1) {
|
2016-07-22 19:47:04 -03:00
|
|
|
PyObject *offset, *self0;
|
2017-01-03 18:47:12 -04:00
|
|
|
if (TIME_GET_FOLD(self)) {
|
|
|
|
self0 = new_time_ex2(TIME_GET_HOUR(self),
|
|
|
|
TIME_GET_MINUTE(self),
|
|
|
|
TIME_GET_SECOND(self),
|
|
|
|
TIME_GET_MICROSECOND(self),
|
2016-07-22 19:47:04 -03:00
|
|
|
HASTZINFO(self) ? self->tzinfo : Py_None,
|
|
|
|
0, Py_TYPE(self));
|
|
|
|
if (self0 == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self0 = (PyObject *)self;
|
|
|
|
Py_INCREF(self0);
|
|
|
|
}
|
|
|
|
offset = time_utcoffset(self0, NULL);
|
|
|
|
Py_DECREF(self0);
|
2010-07-07 20:56:38 -03:00
|
|
|
|
|
|
|
if (offset == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Reduce this to a hash of another object. */
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == Py_None)
|
2010-05-09 12:52:27 -03:00
|
|
|
self->hashcode = generic_hash(
|
|
|
|
(unsigned char *)self->data, _PyDateTime_TIME_DATASIZE);
|
|
|
|
else {
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *temp1, *temp2;
|
|
|
|
int seconds, microseconds;
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(HASTZINFO(self));
|
2010-07-07 20:56:38 -03:00
|
|
|
seconds = TIME_GET_HOUR(self) * 3600 +
|
|
|
|
TIME_GET_MINUTE(self) * 60 +
|
|
|
|
TIME_GET_SECOND(self);
|
|
|
|
microseconds = TIME_GET_MICROSECOND(self);
|
|
|
|
temp1 = new_delta(0, seconds, microseconds, 1);
|
|
|
|
if (temp1 == NULL) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
temp2 = delta_subtract(temp1, offset);
|
|
|
|
Py_DECREF(temp1);
|
|
|
|
if (temp2 == NULL) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
self->hashcode = PyObject_Hash(temp2);
|
|
|
|
Py_DECREF(temp2);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(offset);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self->hashcode;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2002-12-24 01:41:27 -04:00
|
|
|
static PyObject *
|
|
|
|
time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *clone;
|
|
|
|
PyObject *tuple;
|
|
|
|
int hh = TIME_GET_HOUR(self);
|
|
|
|
int mm = TIME_GET_MINUTE(self);
|
|
|
|
int ss = TIME_GET_SECOND(self);
|
|
|
|
int us = TIME_GET_MICROSECOND(self);
|
|
|
|
PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None;
|
2016-07-22 19:47:04 -03:00
|
|
|
int fold = TIME_GET_FOLD(self);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i:replace",
|
2010-05-09 12:52:27 -03:00
|
|
|
time_kws,
|
2016-07-22 19:47:04 -03:00
|
|
|
&hh, &mm, &ss, &us, &tzinfo, &fold))
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2017-03-31 16:48:16 -03:00
|
|
|
if (fold != 0 && fold != 1) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"fold must be either 0 or 1");
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
tuple = Py_BuildValue("iiiiO", hh, mm, ss, us, tzinfo);
|
|
|
|
if (tuple == NULL)
|
|
|
|
return NULL;
|
|
|
|
clone = time_new(Py_TYPE(self), tuple, NULL);
|
2016-08-08 18:05:40 -03:00
|
|
|
if (clone != NULL) {
|
2016-07-22 19:47:04 -03:00
|
|
|
TIME_SET_FOLD(clone, fold);
|
2016-08-08 18:05:40 -03:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(tuple);
|
|
|
|
return clone;
|
2002-12-24 01:41:27 -04:00
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
static PyObject *
|
|
|
|
time_fromisoformat(PyObject *cls, PyObject *tstr) {
|
|
|
|
assert(tstr != NULL);
|
|
|
|
|
|
|
|
if (!PyUnicode_Check(tstr)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "fromisoformat: argument must be str");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_ssize_t len;
|
|
|
|
const char *p = PyUnicode_AsUTF8AndSize(tstr, &len);
|
|
|
|
|
2018-08-23 12:06:20 -03:00
|
|
|
if (p == NULL) {
|
|
|
|
goto invalid_string_error;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// The spec actually requires that time-only ISO 8601 strings start with
|
|
|
|
// T, but the extended format allows this to be omitted as long as there
|
|
|
|
// is no ambiguity with date strings.
|
|
|
|
if (*p == 'T') {
|
|
|
|
++p;
|
|
|
|
len -= 1;
|
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
int hour = 0, minute = 0, second = 0, microsecond = 0;
|
|
|
|
int tzoffset, tzimicrosecond = 0;
|
|
|
|
int rv = parse_isoformat_time(p, len,
|
|
|
|
&hour, &minute, &second, µsecond,
|
|
|
|
&tzoffset, &tzimicrosecond);
|
|
|
|
|
|
|
|
if (rv < 0) {
|
2018-08-23 12:06:20 -03:00
|
|
|
goto invalid_string_error;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset,
|
|
|
|
tzimicrosecond);
|
|
|
|
|
|
|
|
if (tzinfo == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *t;
|
|
|
|
if ( (PyTypeObject *)cls == &PyDateTime_TimeType ) {
|
|
|
|
t = new_time(hour, minute, second, microsecond, tzinfo, 0);
|
|
|
|
} else {
|
|
|
|
t = PyObject_CallFunction(cls, "iiiiO",
|
|
|
|
hour, minute, second, microsecond, tzinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_DECREF(tzinfo);
|
|
|
|
return t;
|
2018-08-23 12:06:20 -03:00
|
|
|
|
|
|
|
invalid_string_error:
|
|
|
|
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", tstr);
|
|
|
|
return NULL;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-31 21:52:50 -04:00
|
|
|
/* Pickle support, a simple use of __reduce__. */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-09 23:49:02 -04:00
|
|
|
/* Let basestate be the non-tzinfo data string.
|
|
|
|
* If tzinfo is None, this returns (basestate,), else (basestate, tzinfo).
|
|
|
|
* So it's a tuple in any (non-error) case.
|
2003-01-31 22:54:15 -04:00
|
|
|
* __getstate__ isn't exposed.
|
2003-01-09 23:49:02 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2016-07-22 19:47:04 -03:00
|
|
|
time_getstate(PyDateTime_Time *self, int proto)
|
2003-01-09 23:49:02 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *basestate;
|
|
|
|
PyObject *result = NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
basestate = PyBytes_FromStringAndSize((char *)self->data,
|
|
|
|
_PyDateTime_TIME_DATASIZE);
|
|
|
|
if (basestate != NULL) {
|
2016-07-22 19:47:04 -03:00
|
|
|
if (proto > 3 && TIME_GET_FOLD(self))
|
|
|
|
/* Set the first bit of the first byte */
|
|
|
|
PyBytes_AS_STRING(basestate)[0] |= (1 << 7);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (! HASTZINFO(self) || self->tzinfo == Py_None)
|
|
|
|
result = PyTuple_Pack(1, basestate);
|
|
|
|
else
|
|
|
|
result = PyTuple_Pack(2, basestate, self->tzinfo);
|
|
|
|
Py_DECREF(basestate);
|
|
|
|
}
|
|
|
|
return result;
|
2003-01-09 23:49:02 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyObject *
|
2016-11-21 18:29:42 -04:00
|
|
|
time_reduce_ex(PyDateTime_Time *self, PyObject *args)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2016-11-21 18:29:42 -04:00
|
|
|
int proto;
|
|
|
|
if (!PyArg_ParseTuple(args, "i:__reduce_ex__", &proto))
|
2016-07-22 19:47:04 -03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return Py_BuildValue("(ON)", Py_TYPE(self), time_getstate(self, proto));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
static PyObject *
|
|
|
|
time_reduce(PyDateTime_Time *self, PyObject *arg)
|
|
|
|
{
|
|
|
|
return Py_BuildValue("(ON)", Py_TYPE(self), time_getstate(self, 2));
|
|
|
|
}
|
|
|
|
|
2003-01-09 23:49:02 -04:00
|
|
|
static PyMethodDef time_methods[] = {
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"isoformat", _PyCFunction_CAST(time_isoformat), METH_VARARGS | METH_KEYWORDS,
|
2016-03-06 15:58:43 -04:00
|
|
|
PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]"
|
|
|
|
"[+HH:MM].\n\n"
|
2020-10-03 07:43:47 -03:00
|
|
|
"The optional argument timespec specifies the number "
|
|
|
|
"of additional terms\nof the time to include. Valid "
|
|
|
|
"options are 'auto', 'hours', 'minutes',\n'seconds', "
|
|
|
|
"'milliseconds' and 'microseconds'.\n")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"strftime", _PyCFunction_CAST(time_strftime), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("format -> strftime() style string.")},
|
2003-01-09 23:49:02 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"__format__", (PyCFunction)date_format, METH_VARARGS,
|
|
|
|
PyDoc_STR("Formats self with strftime.")},
|
2007-09-11 15:06:02 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utcoffset", (PyCFunction)time_utcoffset, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.utcoffset(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"tzname", (PyCFunction)time_tzname, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.tzname(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"dst", (PyCFunction)time_dst, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.dst(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"replace", _PyCFunction_CAST(time_replace), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("Return time with new specified fields.")},
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
{"fromisoformat", (PyCFunction)time_fromisoformat, METH_O | METH_CLASS,
|
2022-05-05 21:31:24 -03:00
|
|
|
PyDoc_STR("string -> time from a string in ISO 8601 format")},
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
{"__reduce_ex__", (PyCFunction)time_reduce_ex, METH_VARARGS,
|
2016-07-22 19:47:04 -03:00
|
|
|
PyDoc_STR("__reduce_ex__(proto) -> (cls, state)")},
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
{"__reduce__", (PyCFunction)time_reduce, METH_NOARGS,
|
|
|
|
PyDoc_STR("__reduce__() -> (cls, state)")},
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char time_doc[] =
|
2004-12-19 16:13:24 -04:00
|
|
|
PyDoc_STR("time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) --> a time object\n\
|
|
|
|
\n\
|
|
|
|
All arguments are optional. tzinfo may be None, or an instance of\n\
|
2013-08-27 13:40:23 -03:00
|
|
|
a tzinfo subclass. The remaining arguments may be ints.\n");
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2006-03-22 05:28:35 -04:00
|
|
|
static PyTypeObject PyDateTime_TimeType = {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.time", /* tp_name */
|
|
|
|
sizeof(PyDateTime_Time), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
(destructor)time_dealloc, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-05-09 12:52:27 -03:00
|
|
|
(reprfunc)time_repr, /* tp_repr */
|
2014-03-20 20:00:35 -03:00
|
|
|
0, /* tp_as_number */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
(hashfunc)time_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
(reprfunc)time_str, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
time_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
time_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
time_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
time_getset, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
time_alloc, /* tp_alloc */
|
|
|
|
time_new, /* tp_new */
|
|
|
|
0, /* tp_free */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/*
|
|
|
|
* PyDateTime_DateTime implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Accessor properties. Properties for day, month, and year are inherited
|
|
|
|
* from date.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_hour(PyDateTime_DateTime *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(DATE_GET_HOUR(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_minute(PyDateTime_DateTime *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(DATE_GET_MINUTE(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyObject *
|
|
|
|
datetime_second(PyDateTime_DateTime *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(DATE_GET_SECOND(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_microsecond(PyDateTime_DateTime *self, void *unused)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return PyLong_FromLong(DATE_GET_MICROSECOND(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_tzinfo(PyDateTime_DateTime *self, void *unused)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = HASTZINFO(self) ? self->tzinfo : Py_None;
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
datetime_fold(PyDateTime_DateTime *self, void *unused)
|
|
|
|
{
|
|
|
|
return PyLong_FromLong(DATE_GET_FOLD(self));
|
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyGetSetDef datetime_getset[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
{"hour", (getter)datetime_hour},
|
|
|
|
{"minute", (getter)datetime_minute},
|
|
|
|
{"second", (getter)datetime_second},
|
|
|
|
{"microsecond", (getter)datetime_microsecond},
|
2016-07-22 19:47:04 -03:00
|
|
|
{"tzinfo", (getter)datetime_tzinfo},
|
|
|
|
{"fold", (getter)datetime_fold},
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constructors.
|
|
|
|
*/
|
|
|
|
|
2006-02-27 13:20:04 -04:00
|
|
|
static char *datetime_kws[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
"year", "month", "day", "hour", "minute", "second",
|
2016-07-22 19:47:04 -03:00
|
|
|
"microsecond", "tzinfo", "fold", NULL
|
2002-12-24 01:41:27 -04:00
|
|
|
};
|
|
|
|
|
2018-12-07 07:42:10 -04:00
|
|
|
static PyObject *
|
|
|
|
datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo)
|
|
|
|
{
|
|
|
|
PyDateTime_DateTime *me;
|
|
|
|
char aware = (char)(tzinfo != Py_None);
|
|
|
|
|
|
|
|
if (aware && check_tzinfo_subclass(tzinfo) < 0) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware));
|
|
|
|
if (me != NULL) {
|
|
|
|
const char *pdata = PyBytes_AS_STRING(state);
|
|
|
|
|
|
|
|
memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE);
|
|
|
|
me->hashcode = -1;
|
|
|
|
me->hastzinfo = aware;
|
|
|
|
if (aware) {
|
|
|
|
Py_INCREF(tzinfo);
|
|
|
|
me->tzinfo = tzinfo;
|
|
|
|
}
|
|
|
|
if (pdata[2] & (1 << 7)) {
|
|
|
|
me->data[2] -= 128;
|
|
|
|
me->fold = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
me->fold = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (PyObject *)me;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self = NULL;
|
|
|
|
int year;
|
|
|
|
int month;
|
|
|
|
int day;
|
|
|
|
int hour = 0;
|
|
|
|
int minute = 0;
|
|
|
|
int second = 0;
|
|
|
|
int usecond = 0;
|
2016-07-22 19:47:04 -03:00
|
|
|
int fold = 0;
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo = Py_None;
|
|
|
|
|
|
|
|
/* Check for invocation from pickle with __getstate__ state */
|
2018-12-07 07:42:10 -04:00
|
|
|
if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) {
|
|
|
|
PyObject *state = PyTuple_GET_ITEM(args, 0);
|
|
|
|
if (PyTuple_GET_SIZE(args) == 2) {
|
|
|
|
tzinfo = PyTuple_GET_ITEM(args, 1);
|
|
|
|
}
|
|
|
|
if (PyBytes_Check(state)) {
|
|
|
|
if (PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE &&
|
|
|
|
MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F))
|
|
|
|
{
|
|
|
|
return datetime_from_pickle(type, state, tzinfo);
|
2016-07-22 19:47:04 -03:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
}
|
|
|
|
else if (PyUnicode_Check(state)) {
|
|
|
|
if (PyUnicode_READY(state)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATETIME_DATASIZE &&
|
|
|
|
MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2) & 0x7F))
|
|
|
|
{
|
|
|
|
state = PyUnicode_AsLatin1String(state);
|
|
|
|
if (state == NULL) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
|
|
|
|
/* More informative error message. */
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"Failed to encode latin1 string when unpickling "
|
|
|
|
"a datetime object. "
|
|
|
|
"pickle.load(data, encoding='latin1') is assumed.");
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-11-21 22:37:50 -04:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
self = datetime_from_pickle(type, state, tzinfo);
|
|
|
|
Py_DECREF(state);
|
|
|
|
return self;
|
2016-07-22 19:47:04 -03:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2018-12-07 07:42:10 -04:00
|
|
|
tzinfo = Py_None;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws,
|
2010-05-09 12:52:27 -03:00
|
|
|
&year, &month, &day, &hour, &minute,
|
2016-07-22 19:47:04 -03:00
|
|
|
&second, &usecond, &tzinfo, &fold)) {
|
|
|
|
self = new_datetime_ex2(year, month, day,
|
2010-05-09 12:52:27 -03:00
|
|
|
hour, minute, second, usecond,
|
2016-07-22 19:47:04 -03:00
|
|
|
tzinfo, fold, type);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-09-28 18:31:35 -03:00
|
|
|
/* TM_FUNC is the shared type of _PyTime_localtime() and
|
|
|
|
* _PyTime_gmtime(). */
|
|
|
|
typedef int (*TM_FUNC)(time_t timer, struct tm*);
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
/* As of version 2015f max fold in IANA database is
|
|
|
|
* 23 hours at 1969-09-30 13:00:00 in Kwajalein. */
|
2016-09-06 14:46:49 -03:00
|
|
|
static long long max_fold_seconds = 24 * 3600;
|
2016-07-22 19:47:04 -03:00
|
|
|
/* NB: date(1970,1,1).toordinal() == 719163 */
|
2016-09-18 22:12:21 -03:00
|
|
|
static long long epoch = 719163LL * 24 * 60 * 60;
|
2016-07-22 19:47:04 -03:00
|
|
|
|
2016-09-06 14:46:49 -03:00
|
|
|
static long long
|
2016-07-22 19:47:04 -03:00
|
|
|
utc_to_seconds(int year, int month, int day,
|
|
|
|
int hour, int minute, int second)
|
|
|
|
{
|
2017-02-10 05:34:02 -04:00
|
|
|
long long ordinal;
|
|
|
|
|
|
|
|
/* ymd_to_ord() doesn't support year <= 0 */
|
|
|
|
if (year < MINYEAR || year > MAXYEAR) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "year %i is out of range", year);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ordinal = ymd_to_ord(year, month, day);
|
2016-07-22 19:47:04 -03:00
|
|
|
return ((ordinal * 24 + hour) * 60 + minute) * 60 + second;
|
|
|
|
}
|
|
|
|
|
2016-09-06 14:46:49 -03:00
|
|
|
static long long
|
|
|
|
local(long long u)
|
2016-07-22 19:47:04 -03:00
|
|
|
{
|
|
|
|
struct tm local_time;
|
2016-07-25 14:54:51 -03:00
|
|
|
time_t t;
|
|
|
|
u -= epoch;
|
|
|
|
t = u;
|
|
|
|
if (t != u) {
|
|
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
|
|
"timestamp out of range for platform time_t");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-09-28 18:31:35 -03:00
|
|
|
if (_PyTime_localtime(t, &local_time) != 0)
|
2016-07-22 19:47:04 -03:00
|
|
|
return -1;
|
|
|
|
return utc_to_seconds(local_time.tm_year + 1900,
|
|
|
|
local_time.tm_mon + 1,
|
|
|
|
local_time.tm_mday,
|
|
|
|
local_time.tm_hour,
|
|
|
|
local_time.tm_min,
|
|
|
|
local_time.tm_sec);
|
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Internal helper.
|
|
|
|
* Build datetime from a time_t and a distinct count of microseconds.
|
|
|
|
* Pass localtime or gmtime for f, to control the interpretation of timet.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo)
|
|
|
|
{
|
2016-09-10 16:58:31 -03:00
|
|
|
struct tm tm;
|
2016-07-22 19:47:04 -03:00
|
|
|
int year, month, day, hour, minute, second, fold = 0;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2016-09-28 18:31:35 -03:00
|
|
|
if (f(timet, &tm) != 0)
|
|
|
|
return NULL;
|
2012-03-13 20:15:40 -03:00
|
|
|
|
2016-09-10 16:58:31 -03:00
|
|
|
year = tm.tm_year + 1900;
|
|
|
|
month = tm.tm_mon + 1;
|
|
|
|
day = tm.tm_mday;
|
|
|
|
hour = tm.tm_hour;
|
|
|
|
minute = tm.tm_min;
|
2012-03-13 20:15:40 -03:00
|
|
|
/* The platform localtime/gmtime may insert leap seconds,
|
2016-09-10 16:58:31 -03:00
|
|
|
* indicated by tm.tm_sec > 59. We don't care about them,
|
2012-03-13 20:15:40 -03:00
|
|
|
* except to the extent that passing them on to the datetime
|
|
|
|
* constructor would raise ValueError for a reason that
|
|
|
|
* made no sense to the user.
|
|
|
|
*/
|
2016-09-10 16:58:31 -03:00
|
|
|
second = Py_MIN(59, tm.tm_sec);
|
2016-07-22 19:47:04 -03:00
|
|
|
|
2017-02-10 05:34:02 -04:00
|
|
|
/* local timezone requires to compute fold */
|
2018-07-25 13:54:58 -03:00
|
|
|
if (tzinfo == Py_None && f == _PyTime_localtime
|
|
|
|
/* On Windows, passing a negative value to local results
|
|
|
|
* in an OSError because localtime_s on Windows does
|
|
|
|
* not support negative timestamps. Unfortunately this
|
|
|
|
* means that fold detection for time values between
|
|
|
|
* 0 and max_fold_seconds will result in an identical
|
|
|
|
* error since we subtract max_fold_seconds to detect a
|
|
|
|
* fold. However, since we know there haven't been any
|
|
|
|
* folds in the interval [0, max_fold_seconds) in any
|
|
|
|
* timezone, we can hackily just forego fold detection
|
|
|
|
* for this time range.
|
|
|
|
*/
|
|
|
|
#ifdef MS_WINDOWS
|
|
|
|
&& (timet - max_fold_seconds > 0)
|
|
|
|
#endif
|
|
|
|
) {
|
2016-09-06 14:46:49 -03:00
|
|
|
long long probe_seconds, result_seconds, transition;
|
2016-07-22 19:47:04 -03:00
|
|
|
|
|
|
|
result_seconds = utc_to_seconds(year, month, day,
|
|
|
|
hour, minute, second);
|
Check result of utc_to_seconds and skip fold probe in pure Python (#91582)
The `utc_to_seconds` call can fail, here's a minimal reproducer on
Linux:
TZ=UTC python -c "from datetime import *; datetime.fromtimestamp(253402300799 + 1)"
The old behavior still raised an error in a similar way, but only
because subsequent calculations happened to fail as well. Better to fail
fast.
This also refactors the tests to split out the `fromtimestamp` and
`utcfromtimestamp` tests, and to get us closer to the actual desired
limits of the functions. As part of this, we also changed the way we
detect platforms where the same limits don't necessarily apply (e.g.
Windows).
As part of refactoring the tests to hit this condition explicitly (even
though the user-facing behvior doesn't change in any way we plan to
guarantee), I noticed that there was a difference in the places that
`datetime.utcfromtimestamp` fails in the C and pure Python versions, which
was fixed by skipping the "probe for fold" logic for UTC specifically —
since UTC doesn't have any folds or gaps, we were never going to find a
fold value anyway. This should prevent some failures in the pure python
`utcfromtimestamp` method on timestamps close to 0001-01-01.
There are two separate news entries for this because one is a
potentially user-facing change, the other is an internal code
correctness change that, if anything, changes some error messages. The
two happen to be coupled because of the test refactoring, but they are
probably best thought of as independent changes.
Fixes GH-91581
2022-05-12 18:00:50 -03:00
|
|
|
if (result_seconds == -1 && PyErr_Occurred()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
/* Probe max_fold_seconds to detect a fold. */
|
|
|
|
probe_seconds = local(epoch + timet - max_fold_seconds);
|
|
|
|
if (probe_seconds == -1)
|
|
|
|
return NULL;
|
|
|
|
transition = result_seconds - probe_seconds - max_fold_seconds;
|
|
|
|
if (transition < 0) {
|
|
|
|
probe_seconds = local(epoch + timet + transition);
|
|
|
|
if (probe_seconds == -1)
|
|
|
|
return NULL;
|
|
|
|
if (probe_seconds == result_seconds)
|
|
|
|
fold = 1;
|
|
|
|
}
|
|
|
|
}
|
2018-01-16 14:06:31 -04:00
|
|
|
return new_datetime_subclass_fold_ex(year, month, day, hour, minute,
|
|
|
|
second, us, tzinfo, fold, cls);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Internal helper.
|
|
|
|
* Build datetime from a Python timestamp. Pass localtime or gmtime for f,
|
|
|
|
* to control the interpretation of the timestamp. Since a double doesn't
|
|
|
|
* have enough bits to cover a datetime's full range of precision, it's
|
|
|
|
* better to call datetime_from_timet_and_us provided you have a way
|
|
|
|
* to get that much precision (e.g., C time() isn't good enough).
|
|
|
|
*/
|
|
|
|
static PyObject *
|
2012-03-13 09:35:55 -03:00
|
|
|
datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo)
|
|
|
|
{
|
|
|
|
time_t timet;
|
2012-03-13 09:35:55 -03:00
|
|
|
long us;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2015-03-29 20:10:14 -03:00
|
|
|
if (_PyTime_ObjectToTimeval(timestamp,
|
2015-09-08 20:02:23 -03:00
|
|
|
&timet, &us, _PyTime_ROUND_HALF_EVEN) == -1)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2015-03-29 19:09:18 -03:00
|
|
|
|
2012-03-13 20:15:40 -03:00
|
|
|
return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Internal helper.
|
|
|
|
* Build most accurate possible datetime for current time. Pass localtime or
|
|
|
|
* gmtime for f as appropriate.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
|
|
|
|
{
|
2015-03-29 19:09:18 -03:00
|
|
|
_PyTime_t ts = _PyTime_GetSystemClock();
|
2015-09-18 08:23:02 -03:00
|
|
|
time_t secs;
|
|
|
|
int us;
|
2015-03-29 19:09:18 -03:00
|
|
|
|
2015-09-18 08:23:02 -03:00
|
|
|
if (_PyTime_AsTimevalTime_t(ts, &secs, &us, _PyTime_ROUND_FLOOR) < 0)
|
2015-03-29 19:09:18 -03:00
|
|
|
return NULL;
|
2015-09-18 08:23:02 -03:00
|
|
|
assert(0 <= us && us <= 999999);
|
2015-03-29 19:09:18 -03:00
|
|
|
|
2015-09-18 08:23:02 -03:00
|
|
|
return datetime_from_timet_and_us(cls, f, secs, us, tzinfo);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2014-01-07 16:41:53 -04:00
|
|
|
/*[clinic input]
|
2013-10-19 04:09:25 -03:00
|
|
|
|
|
|
|
@classmethod
|
2013-11-18 13:32:13 -04:00
|
|
|
datetime.datetime.now
|
2013-10-19 04:09:25 -03:00
|
|
|
|
|
|
|
tz: object = None
|
|
|
|
Timezone object.
|
|
|
|
|
|
|
|
Returns new datetime object representing current time local to tz.
|
|
|
|
|
|
|
|
If no tz is specified, uses local timezone.
|
2014-01-07 16:41:53 -04:00
|
|
|
[clinic start generated code]*/
|
2013-10-19 04:09:25 -03:00
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2014-01-24 10:17:25 -04:00
|
|
|
datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
|
2015-04-03 17:53:51 -03:00
|
|
|
/*[clinic end generated code: output=b3386e5345e2b47a input=80d09869c5267d00]*/
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2013-10-19 04:09:25 -03:00
|
|
|
/* Return best possible local time -- this isn't constrained by the
|
|
|
|
* precision of a timestamp.
|
|
|
|
*/
|
|
|
|
if (check_tzinfo_subclass(tz) < 0)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2003-01-23 15:58:02 -04:00
|
|
|
|
2014-01-24 10:17:25 -04:00
|
|
|
self = datetime_best_possible((PyObject *)type,
|
2016-09-28 18:31:35 -03:00
|
|
|
tz == Py_None ? _PyTime_localtime :
|
|
|
|
_PyTime_gmtime,
|
2013-10-19 04:09:25 -03:00
|
|
|
tz);
|
|
|
|
if (self != NULL && tz != Py_None) {
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Convert UTC to tzinfo's zone. */
|
2016-01-05 15:27:54 -04:00
|
|
|
self = _PyObject_CallMethodId(tz, &PyId_fromutc, "N", self);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Return best possible UTC time -- this isn't constrained by the
|
|
|
|
* precision of a timestamp.
|
|
|
|
*/
|
|
|
|
static PyObject *
|
|
|
|
datetime_utcnow(PyObject *cls, PyObject *dummy)
|
|
|
|
{
|
2016-09-28 18:31:35 -03:00
|
|
|
return datetime_best_possible(cls, _PyTime_gmtime, Py_None);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/* Return new local datetime from timestamp (Python timestamp -- a double). */
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *self;
|
2012-03-13 09:35:55 -03:00
|
|
|
PyObject *timestamp;
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *tzinfo = Py_None;
|
|
|
|
static char *keywords[] = {"timestamp", "tz", NULL};
|
|
|
|
|
2012-03-13 09:35:55 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp",
|
2010-05-09 12:52:27 -03:00
|
|
|
keywords, ×tamp, &tzinfo))
|
|
|
|
return NULL;
|
|
|
|
if (check_tzinfo_subclass(tzinfo) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
self = datetime_from_timestamp(cls,
|
2016-09-28 18:31:35 -03:00
|
|
|
tzinfo == Py_None ? _PyTime_localtime :
|
|
|
|
_PyTime_gmtime,
|
2010-05-09 12:52:27 -03:00
|
|
|
timestamp,
|
|
|
|
tzinfo);
|
|
|
|
if (self != NULL && tzinfo != Py_None) {
|
|
|
|
/* Convert UTC to tzinfo's zone. */
|
2016-01-05 15:27:54 -04:00
|
|
|
self = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "N", self);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Return new UTC datetime from timestamp (Python timestamp -- a double). */
|
|
|
|
static PyObject *
|
|
|
|
datetime_utcfromtimestamp(PyObject *cls, PyObject *args)
|
|
|
|
{
|
2012-03-13 09:35:55 -03:00
|
|
|
PyObject *timestamp;
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2012-03-13 09:35:55 -03:00
|
|
|
if (PyArg_ParseTuple(args, "O:utcfromtimestamp", ×tamp))
|
2016-09-28 18:31:35 -03:00
|
|
|
result = datetime_from_timestamp(cls, _PyTime_gmtime, timestamp,
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_None);
|
|
|
|
return result;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
2010-06-17 15:30:34 -03:00
|
|
|
/* Return new datetime from _strptime.strptime_datetime(). */
|
2005-01-13 00:12:31 -04:00
|
|
|
static PyObject *
|
|
|
|
datetime_strptime(PyObject *cls, PyObject *args)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
static PyObject *module = NULL;
|
2011-09-28 02:41:54 -03:00
|
|
|
PyObject *string, *format;
|
2011-10-14 05:20:37 -03:00
|
|
|
_Py_IDENTIFIER(_strptime_datetime);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2011-09-28 02:41:54 -03:00
|
|
|
if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format))
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
|
|
|
|
2010-06-17 15:30:34 -03:00
|
|
|
if (module == NULL) {
|
2021-12-12 04:45:20 -04:00
|
|
|
module = PyImport_ImportModule("_strptime");
|
2010-06-28 11:36:55 -03:00
|
|
|
if (module == NULL)
|
2010-06-17 15:30:34 -03:00
|
|
|
return NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2016-12-09 10:24:31 -04:00
|
|
|
return _PyObject_CallMethodIdObjArgs(module, &PyId__strptime_datetime,
|
|
|
|
cls, string, format, NULL);
|
2005-01-13 00:12:31 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Return new datetime from date/datetime and time arguments. */
|
|
|
|
static PyObject *
|
|
|
|
datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2016-08-02 18:49:30 -03:00
|
|
|
static char *keywords[] = {"date", "time", "tzinfo", NULL};
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *date;
|
|
|
|
PyObject *time;
|
2016-08-02 18:49:30 -03:00
|
|
|
PyObject *tzinfo = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
|
2016-08-02 18:49:30 -03:00
|
|
|
if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords,
|
2010-05-09 12:52:27 -03:00
|
|
|
&PyDateTime_DateType, &date,
|
2016-08-02 18:49:30 -03:00
|
|
|
&PyDateTime_TimeType, &time, &tzinfo)) {
|
|
|
|
if (tzinfo == NULL) {
|
|
|
|
if (HASTZINFO(time))
|
|
|
|
tzinfo = ((PyDateTime_Time *)time)->tzinfo;
|
|
|
|
else
|
|
|
|
tzinfo = Py_None;
|
|
|
|
}
|
2018-01-16 14:06:31 -04:00
|
|
|
result = new_datetime_subclass_fold_ex(GET_YEAR(date),
|
|
|
|
GET_MONTH(date),
|
|
|
|
GET_DAY(date),
|
|
|
|
TIME_GET_HOUR(time),
|
|
|
|
TIME_GET_MINUTE(time),
|
|
|
|
TIME_GET_SECOND(time),
|
|
|
|
TIME_GET_MICROSECOND(time),
|
|
|
|
tzinfo,
|
|
|
|
TIME_GET_FOLD(time),
|
|
|
|
cls);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return result;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2018-08-23 12:06:20 -03:00
|
|
|
static PyObject *
|
2018-10-22 13:32:52 -03:00
|
|
|
_sanitize_isoformat_str(PyObject *dtstr)
|
|
|
|
{
|
2022-05-05 21:31:24 -03:00
|
|
|
Py_ssize_t len = PyUnicode_GetLength(dtstr);
|
|
|
|
if (len < 7) { // All valid ISO 8601 strings are at least 7 characters long
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-23 12:06:20 -03:00
|
|
|
// `fromisoformat` allows surrogate characters in exactly one position,
|
|
|
|
// the separator; to allow datetime_fromisoformat to make the simplifying
|
|
|
|
// assumption that all valid strings can be encoded in UTF-8, this function
|
|
|
|
// replaces any surrogate character separators with `T`.
|
2018-10-22 13:32:52 -03:00
|
|
|
//
|
|
|
|
// The result of this, if not NULL, returns a new reference
|
2022-05-05 21:31:24 -03:00
|
|
|
const void* const unicode_data = PyUnicode_DATA(dtstr);
|
2022-05-13 07:41:05 -03:00
|
|
|
const int kind = PyUnicode_KIND(dtstr);
|
2022-05-05 21:31:24 -03:00
|
|
|
|
|
|
|
// Depending on the format of the string, the separator can only ever be
|
|
|
|
// in positions 7, 8 or 10. We'll check each of these for a surrogate and
|
|
|
|
// if we find one, replace it with `T`. If there is more than one surrogate,
|
|
|
|
// we don't have to bother sanitizing it, because the function will later
|
|
|
|
// fail when we try to encode the string as ASCII.
|
|
|
|
static const size_t potential_separators[3] = {7, 8, 10};
|
|
|
|
size_t surrogate_separator = 0;
|
|
|
|
for(size_t idx = 0;
|
|
|
|
idx < sizeof(potential_separators) / sizeof(*potential_separators);
|
|
|
|
++idx) {
|
|
|
|
size_t pos = potential_separators[idx];
|
|
|
|
if (pos > (size_t)len) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Py_UNICODE_IS_SURROGATE(PyUnicode_READ(kind, unicode_data, pos))) {
|
|
|
|
surrogate_separator = pos;
|
|
|
|
break;
|
|
|
|
}
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
if (surrogate_separator == 0) {
|
2018-10-22 13:32:52 -03:00
|
|
|
Py_INCREF(dtstr);
|
2018-08-23 12:06:20 -03:00
|
|
|
return dtstr;
|
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
PyObject *str_out = _PyUnicode_Copy(dtstr);
|
2018-08-23 12:06:20 -03:00
|
|
|
if (str_out == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
if (PyUnicode_WriteChar(str_out, surrogate_separator, (Py_UCS4)'T')) {
|
2018-08-23 12:06:20 -03:00
|
|
|
Py_DECREF(str_out);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return str_out;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
|
|
|
|
static Py_ssize_t
|
|
|
|
_find_isoformat_datetime_separator(const char *dtstr, Py_ssize_t len) {
|
|
|
|
// The valid date formats can all be distinguished by characters 4 and 5
|
|
|
|
// and further narrowed down by character
|
|
|
|
// which tells us where to look for the separator character.
|
|
|
|
// Format | As-rendered | Position
|
|
|
|
// ---------------------------------------
|
|
|
|
// %Y-%m-%d | YYYY-MM-DD | 10
|
|
|
|
// %Y%m%d | YYYYMMDD | 8
|
|
|
|
// %Y-W%V | YYYY-Www | 8
|
|
|
|
// %YW%V | YYYYWww | 7
|
|
|
|
// %Y-W%V-%u | YYYY-Www-d | 10
|
|
|
|
// %YW%V%u | YYYYWwwd | 8
|
|
|
|
// %Y-%j | YYYY-DDD | 8
|
|
|
|
// %Y%j | YYYYDDD | 7
|
|
|
|
//
|
|
|
|
// Note that because we allow *any* character for the separator, in the
|
|
|
|
// case where character 4 is W, it's not straightforward to determine where
|
|
|
|
// the separator is — in the case of YYYY-Www-d, you have actual ambiguity,
|
|
|
|
// e.g. 2020-W01-0000 could be YYYY-Www-D0HH or YYYY-Www-HHMM, when the
|
|
|
|
// separator character is a number in the former case or a hyphen in the
|
|
|
|
// latter case.
|
|
|
|
//
|
|
|
|
// The case of YYYYWww can be distinguished from YYYYWwwd by tracking ahead
|
|
|
|
// to either the end of the string or the first non-numeric character —
|
|
|
|
// since the time components all come in pairs YYYYWww#HH can be
|
|
|
|
// distinguished from YYYYWwwd#HH by the fact that there will always be an
|
|
|
|
// odd number of digits before the first non-digit character in the former
|
|
|
|
// case.
|
|
|
|
static const char date_separator = '-';
|
|
|
|
static const char week_indicator = 'W';
|
|
|
|
|
|
|
|
if (len == 7) {
|
|
|
|
return 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dtstr[4] == date_separator) {
|
|
|
|
// YYYY-???
|
|
|
|
|
|
|
|
if (dtstr[5] == week_indicator) {
|
|
|
|
// YYYY-W??
|
|
|
|
|
|
|
|
if (len < 8) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len > 8 && dtstr[8] == date_separator) {
|
|
|
|
// YYYY-Www-D (10) or YYYY-Www-HH (8)
|
|
|
|
if (len == 9) { return -1; }
|
|
|
|
if (len > 10 && is_digit(dtstr[10])) {
|
|
|
|
// This is as far as we'll try to go to resolve the
|
|
|
|
// ambiguity for the moment — if we have YYYY-Www-##, the
|
|
|
|
// separator is either a hyphen at 8 or a number at 10.
|
|
|
|
//
|
|
|
|
// We'll assume it's a hyphen at 8 because it's way more
|
|
|
|
// likely that someone will use a hyphen as a separator
|
|
|
|
// than a number, but at this point it's really best effort
|
|
|
|
// because this is an extension of the spec anyway.
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 10;
|
|
|
|
} else {
|
|
|
|
// YYYY-Www (8)
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// YYYY-MM-DD (10)
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// YYYY???
|
|
|
|
if (dtstr[4] == week_indicator) {
|
|
|
|
// YYYYWww (7) or YYYYWwwd (8)
|
|
|
|
size_t idx = 7;
|
|
|
|
for (; idx < (size_t)len; ++idx) {
|
|
|
|
// Keep going until we run out of digits.
|
|
|
|
if (!is_digit(dtstr[idx])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx < 9) {
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx % 2 == 0) {
|
|
|
|
// If the index of the last number is even, it's YYYYWww
|
|
|
|
return 7;
|
|
|
|
} else {
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// YYYYMMDD (8)
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
static PyObject *
|
2018-10-22 13:32:52 -03:00
|
|
|
datetime_fromisoformat(PyObject *cls, PyObject *dtstr)
|
|
|
|
{
|
2017-12-21 01:33:49 -04:00
|
|
|
assert(dtstr != NULL);
|
|
|
|
|
|
|
|
if (!PyUnicode_Check(dtstr)) {
|
2018-10-22 13:32:52 -03:00
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"fromisoformat: argument must be str");
|
2017-12-21 01:33:49 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// We only need to sanitize this string if the separator is a surrogate
|
|
|
|
// character. In the situation where the separator location is ambiguous,
|
|
|
|
// we don't have to sanitize it anything because that can only happen when
|
|
|
|
// the separator is either '-' or a number. This should mostly be a noop
|
|
|
|
// but it makes the reference counting easier if we still sanitize.
|
2018-10-22 13:32:52 -03:00
|
|
|
PyObject *dtstr_clean = _sanitize_isoformat_str(dtstr);
|
|
|
|
if (dtstr_clean == NULL) {
|
2022-05-05 21:31:24 -03:00
|
|
|
goto invalid_string_error;
|
2018-08-23 12:06:20 -03:00
|
|
|
}
|
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
Py_ssize_t len;
|
2018-10-22 13:32:52 -03:00
|
|
|
const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr_clean, &len);
|
2018-08-23 12:06:20 -03:00
|
|
|
|
|
|
|
if (dt_ptr == NULL) {
|
2018-10-22 13:32:52 -03:00
|
|
|
if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
|
|
|
|
// Encoding errors are invalid string errors at this point
|
|
|
|
goto invalid_string_error;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
goto error;
|
|
|
|
}
|
2018-08-23 12:06:20 -03:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
const Py_ssize_t separator_location = _find_isoformat_datetime_separator(
|
|
|
|
dt_ptr, len);
|
|
|
|
|
|
|
|
|
2018-08-23 12:06:20 -03:00
|
|
|
const char *p = dt_ptr;
|
2017-12-21 01:33:49 -04:00
|
|
|
|
|
|
|
int year = 0, month = 0, day = 0;
|
|
|
|
int hour = 0, minute = 0, second = 0, microsecond = 0;
|
|
|
|
int tzoffset = 0, tzusec = 0;
|
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
// date runs up to separator_location
|
|
|
|
int rv = parse_isoformat_date(p, separator_location, &year, &month, &day);
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2022-05-05 21:31:24 -03:00
|
|
|
if (!rv && len > separator_location) {
|
2017-12-21 01:33:49 -04:00
|
|
|
// In UTF-8, the length of multi-byte characters is encoded in the MSB
|
2022-05-05 21:31:24 -03:00
|
|
|
p += separator_location;
|
|
|
|
if ((p[0] & 0x80) == 0) {
|
|
|
|
p += 1;
|
2018-10-22 13:32:52 -03:00
|
|
|
}
|
|
|
|
else {
|
2022-05-05 21:31:24 -03:00
|
|
|
switch (p[0] & 0xf0) {
|
2017-12-21 01:33:49 -04:00
|
|
|
case 0xe0:
|
2022-05-05 21:31:24 -03:00
|
|
|
p += 3;
|
2017-12-21 01:33:49 -04:00
|
|
|
break;
|
|
|
|
case 0xf0:
|
2022-05-05 21:31:24 -03:00
|
|
|
p += 4;
|
2017-12-21 01:33:49 -04:00
|
|
|
break;
|
|
|
|
default:
|
2022-05-05 21:31:24 -03:00
|
|
|
p += 2;
|
2017-12-21 01:33:49 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
len -= (p - dt_ptr);
|
2018-10-22 13:32:52 -03:00
|
|
|
rv = parse_isoformat_time(p, len, &hour, &minute, &second,
|
|
|
|
µsecond, &tzoffset, &tzusec);
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
if (rv < 0) {
|
2018-08-23 12:06:20 -03:00
|
|
|
goto invalid_string_error;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2018-10-22 13:32:52 -03:00
|
|
|
PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec);
|
2017-12-21 01:33:49 -04:00
|
|
|
if (tzinfo == NULL) {
|
2018-08-23 12:06:20 -03:00
|
|
|
goto error;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2018-01-16 14:06:31 -04:00
|
|
|
PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute,
|
|
|
|
second, microsecond, tzinfo, cls);
|
2017-12-21 01:33:49 -04:00
|
|
|
|
|
|
|
Py_DECREF(tzinfo);
|
2018-10-22 13:32:52 -03:00
|
|
|
Py_DECREF(dtstr_clean);
|
2017-12-21 01:33:49 -04:00
|
|
|
return dt;
|
2018-08-23 12:06:20 -03:00
|
|
|
|
|
|
|
invalid_string_error:
|
|
|
|
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
|
|
|
|
|
|
|
|
error:
|
2018-10-22 13:32:52 -03:00
|
|
|
Py_XDECREF(dtstr_clean);
|
2018-08-23 12:06:20 -03:00
|
|
|
|
|
|
|
return NULL;
|
2017-12-21 01:33:49 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
/*
|
|
|
|
* Destructor.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_dealloc(PyDateTime_DateTime *self)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (HASTZINFO(self)) {
|
|
|
|
Py_XDECREF(self->tzinfo);
|
|
|
|
}
|
|
|
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Indirect access to tzinfo methods.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* These are all METH_NOARGS, so don't need to check the arglist. */
|
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
datetime_utcoffset(PyObject *self, PyObject *unused) {
|
|
|
|
return call_utcoffset(GET_DT_TZINFO(self), self);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
datetime_dst(PyObject *self, PyObject *unused) {
|
|
|
|
return call_dst(GET_DT_TZINFO(self), self);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2010-07-07 20:56:38 -03:00
|
|
|
datetime_tzname(PyObject *self, PyObject *unused) {
|
|
|
|
return call_tzname(GET_DT_TZINFO(self), self);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-01-10 23:39:11 -04:00
|
|
|
* datetime arithmetic.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* factor must be 1 (to add) or -1 (to subtract). The result inherits
|
|
|
|
* the tzinfo state of date.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta,
|
2010-05-09 12:52:27 -03:00
|
|
|
int factor)
|
|
|
|
{
|
|
|
|
/* Note that the C-level additions can't overflow, because of
|
|
|
|
* invariant bounds on the member values.
|
|
|
|
*/
|
|
|
|
int year = GET_YEAR(date);
|
|
|
|
int month = GET_MONTH(date);
|
|
|
|
int day = GET_DAY(date) + GET_TD_DAYS(delta) * factor;
|
|
|
|
int hour = DATE_GET_HOUR(date);
|
|
|
|
int minute = DATE_GET_MINUTE(date);
|
|
|
|
int second = DATE_GET_SECOND(date) + GET_TD_SECONDS(delta) * factor;
|
|
|
|
int microsecond = DATE_GET_MICROSECOND(date) +
|
|
|
|
GET_TD_MICROSECONDS(delta) * factor;
|
|
|
|
|
|
|
|
assert(factor == 1 || factor == -1);
|
|
|
|
if (normalize_datetime(&year, &month, &day,
|
2017-02-10 05:34:02 -04:00
|
|
|
&hour, &minute, &second, µsecond) < 0) {
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2017-02-10 05:34:02 -04:00
|
|
|
}
|
|
|
|
|
2019-02-04 15:42:04 -04:00
|
|
|
return new_datetime_subclass_ex(year, month, day,
|
|
|
|
hour, minute, second, microsecond,
|
|
|
|
HASTZINFO(date) ? date->tzinfo : Py_None,
|
|
|
|
(PyObject *)Py_TYPE(date));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_add(PyObject *left, PyObject *right)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (PyDateTime_Check(left)) {
|
|
|
|
/* datetime + ??? */
|
|
|
|
if (PyDelta_Check(right))
|
|
|
|
/* datetime + delta */
|
|
|
|
return add_datetime_timedelta(
|
|
|
|
(PyDateTime_DateTime *)left,
|
|
|
|
(PyDateTime_Delta *)right,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
else if (PyDelta_Check(left)) {
|
|
|
|
/* delta + datetime */
|
|
|
|
return add_datetime_timedelta((PyDateTime_DateTime *) right,
|
|
|
|
(PyDateTime_Delta *) left,
|
|
|
|
1);
|
|
|
|
}
|
2011-08-10 22:28:54 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_subtract(PyObject *left, PyObject *right)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *result = Py_NotImplemented;
|
|
|
|
|
|
|
|
if (PyDateTime_Check(left)) {
|
|
|
|
/* datetime - ??? */
|
|
|
|
if (PyDateTime_Check(right)) {
|
|
|
|
/* datetime - datetime */
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *offset1, *offset2, *offdiff = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
int delta_d, delta_s, delta_us;
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (GET_DT_TZINFO(left) == GET_DT_TZINFO(right)) {
|
|
|
|
offset2 = offset1 = Py_None;
|
|
|
|
Py_INCREF(offset1);
|
|
|
|
Py_INCREF(offset2);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
offset1 = datetime_utcoffset(left, NULL);
|
|
|
|
if (offset1 == NULL)
|
|
|
|
return NULL;
|
|
|
|
offset2 = datetime_utcoffset(right, NULL);
|
|
|
|
if (offset2 == NULL) {
|
|
|
|
Py_DECREF(offset1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if ((offset1 != Py_None) != (offset2 != Py_None)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"can't subtract offset-naive and "
|
|
|
|
"offset-aware datetimes");
|
|
|
|
Py_DECREF(offset1);
|
|
|
|
Py_DECREF(offset2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((offset1 != offset2) &&
|
|
|
|
delta_cmp(offset1, offset2) != 0) {
|
|
|
|
offdiff = delta_subtract(offset1, offset2);
|
|
|
|
if (offdiff == NULL) {
|
|
|
|
Py_DECREF(offset1);
|
|
|
|
Py_DECREF(offset2);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(offset1);
|
|
|
|
Py_DECREF(offset2);
|
2010-05-09 12:52:27 -03:00
|
|
|
delta_d = ymd_to_ord(GET_YEAR(left),
|
|
|
|
GET_MONTH(left),
|
|
|
|
GET_DAY(left)) -
|
|
|
|
ymd_to_ord(GET_YEAR(right),
|
|
|
|
GET_MONTH(right),
|
|
|
|
GET_DAY(right));
|
|
|
|
/* These can't overflow, since the values are
|
|
|
|
* normalized. At most this gives the number of
|
|
|
|
* seconds in one day.
|
|
|
|
*/
|
|
|
|
delta_s = (DATE_GET_HOUR(left) -
|
|
|
|
DATE_GET_HOUR(right)) * 3600 +
|
|
|
|
(DATE_GET_MINUTE(left) -
|
|
|
|
DATE_GET_MINUTE(right)) * 60 +
|
|
|
|
(DATE_GET_SECOND(left) -
|
|
|
|
DATE_GET_SECOND(right));
|
|
|
|
delta_us = DATE_GET_MICROSECOND(left) -
|
|
|
|
DATE_GET_MICROSECOND(right);
|
|
|
|
result = new_delta(delta_d, delta_s, delta_us, 1);
|
2013-11-07 19:50:58 -04:00
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offdiff != NULL) {
|
2016-04-10 12:12:01 -03:00
|
|
|
Py_SETREF(result, delta_subtract(result, offdiff));
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(offdiff);
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
else if (PyDelta_Check(right)) {
|
|
|
|
/* datetime - delta */
|
|
|
|
result = add_datetime_timedelta(
|
|
|
|
(PyDateTime_DateTime *)left,
|
|
|
|
(PyDateTime_Delta *)right,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == Py_NotImplemented)
|
|
|
|
Py_INCREF(result);
|
|
|
|
return result;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Various ways to turn a datetime into a string. */
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_repr(PyDateTime_DateTime *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
const char *type_name = Py_TYPE(self)->tp_name;
|
|
|
|
PyObject *baserepr;
|
|
|
|
|
|
|
|
if (DATE_GET_MICROSECOND(self)) {
|
|
|
|
baserepr = PyUnicode_FromFormat(
|
|
|
|
"%s(%d, %d, %d, %d, %d, %d, %d)",
|
|
|
|
type_name,
|
|
|
|
GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
DATE_GET_MICROSECOND(self));
|
|
|
|
}
|
|
|
|
else if (DATE_GET_SECOND(self)) {
|
|
|
|
baserepr = PyUnicode_FromFormat(
|
|
|
|
"%s(%d, %d, %d, %d, %d, %d)",
|
|
|
|
type_name,
|
|
|
|
GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
baserepr = PyUnicode_FromFormat(
|
|
|
|
"%s(%d, %d, %d, %d, %d)",
|
|
|
|
type_name,
|
|
|
|
GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self), DATE_GET_MINUTE(self));
|
|
|
|
}
|
2016-07-22 19:47:04 -03:00
|
|
|
if (baserepr != NULL && DATE_GET_FOLD(self) != 0)
|
|
|
|
baserepr = append_keyword_fold(baserepr, DATE_GET_FOLD(self));
|
2010-05-09 12:52:27 -03:00
|
|
|
if (baserepr == NULL || ! HASTZINFO(self))
|
|
|
|
return baserepr;
|
|
|
|
return append_keyword_tzinfo(baserepr, self->tzinfo);
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_str(PyDateTime_DateTime *self)
|
|
|
|
{
|
2016-12-08 19:33:39 -04:00
|
|
|
return _PyObject_CallMethodId((PyObject *)self, &PyId_isoformat, "s", " ");
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
datetime_isoformat(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int sep = 'T';
|
2016-03-06 15:58:43 -04:00
|
|
|
char *timespec = NULL;
|
|
|
|
static char *keywords[] = {"sep", "timespec", NULL};
|
2010-05-09 12:52:27 -03:00
|
|
|
char buffer[100];
|
2016-03-06 15:58:43 -04:00
|
|
|
PyObject *result = NULL;
|
2010-05-09 12:52:27 -03:00
|
|
|
int us = DATE_GET_MICROSECOND(self);
|
2020-02-24 02:40:43 -04:00
|
|
|
static const char *specs[][2] = {
|
2016-03-06 15:58:43 -04:00
|
|
|
{"hours", "%04d-%02d-%02d%c%02d"},
|
|
|
|
{"minutes", "%04d-%02d-%02d%c%02d:%02d"},
|
|
|
|
{"seconds", "%04d-%02d-%02d%c%02d:%02d:%02d"},
|
|
|
|
{"milliseconds", "%04d-%02d-%02d%c%02d:%02d:%02d.%03d"},
|
|
|
|
{"microseconds", "%04d-%02d-%02d%c%02d:%02d:%02d.%06d"},
|
|
|
|
};
|
|
|
|
size_t given_spec;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2016-03-06 15:58:43 -04:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|Cs:isoformat", keywords, &sep, ×pec))
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2016-03-06 15:58:43 -04:00
|
|
|
|
|
|
|
if (timespec == NULL || strcmp(timespec, "auto") == 0) {
|
|
|
|
if (us == 0) {
|
|
|
|
/* seconds */
|
|
|
|
given_spec = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* microseconds */
|
|
|
|
given_spec = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (given_spec = 0; given_spec < Py_ARRAY_LENGTH(specs); given_spec++) {
|
|
|
|
if (strcmp(timespec, specs[given_spec][0]) == 0) {
|
|
|
|
if (given_spec == 3) {
|
|
|
|
us = us / 1000;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (given_spec == Py_ARRAY_LENGTH(specs)) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Unknown timespec value");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = PyUnicode_FromFormat(specs[given_spec][1],
|
2010-05-09 12:52:27 -03:00
|
|
|
GET_YEAR(self), GET_MONTH(self),
|
|
|
|
GET_DAY(self), (int)sep,
|
|
|
|
DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self), us);
|
2016-03-06 15:58:43 -04:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
if (!result || !HASTZINFO(self))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
/* We need to append the UTC offset. */
|
|
|
|
if (format_utcoffset(buffer, sizeof(buffer), ":", self->tzinfo,
|
|
|
|
(PyObject *)self) < 0) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyUnicode_AppendAndDel(&result, PyUnicode_FromString(buffer));
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_ctime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return format_ctime((PyDateTime_Date *)self,
|
|
|
|
DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Miscellaneous methods. */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
flip_fold(PyObject *dt)
|
|
|
|
{
|
|
|
|
return new_datetime_ex2(GET_YEAR(dt),
|
|
|
|
GET_MONTH(dt),
|
|
|
|
GET_DAY(dt),
|
|
|
|
DATE_GET_HOUR(dt),
|
|
|
|
DATE_GET_MINUTE(dt),
|
|
|
|
DATE_GET_SECOND(dt),
|
|
|
|
DATE_GET_MICROSECOND(dt),
|
|
|
|
HASTZINFO(dt) ?
|
|
|
|
((PyDateTime_DateTime *)dt)->tzinfo : Py_None,
|
|
|
|
!DATE_GET_FOLD(dt),
|
|
|
|
Py_TYPE(dt));
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
get_flip_fold_offset(PyObject *dt)
|
|
|
|
{
|
|
|
|
PyObject *result, *flip_dt;
|
|
|
|
|
|
|
|
flip_dt = flip_fold(dt);
|
|
|
|
if (flip_dt == NULL)
|
|
|
|
return NULL;
|
|
|
|
result = datetime_utcoffset(flip_dt, NULL);
|
|
|
|
Py_DECREF(flip_dt);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PEP 495 exception: Whenever one or both of the operands in
|
|
|
|
* inter-zone comparison is such that its utcoffset() depends
|
2018-03-28 16:14:26 -03:00
|
|
|
* on the value of its fold attribute, the result is False.
|
2016-07-22 19:47:04 -03:00
|
|
|
*
|
|
|
|
* Return 1 if exception applies, 0 if not, and -1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pep495_eq_exception(PyObject *self, PyObject *other,
|
|
|
|
PyObject *offset_self, PyObject *offset_other)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
PyObject *flip_offset;
|
|
|
|
|
|
|
|
flip_offset = get_flip_fold_offset(self);
|
|
|
|
if (flip_offset == NULL)
|
|
|
|
return -1;
|
|
|
|
if (flip_offset != offset_self &&
|
|
|
|
delta_cmp(flip_offset, offset_self))
|
|
|
|
{
|
|
|
|
result = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
Py_DECREF(flip_offset);
|
|
|
|
|
|
|
|
flip_offset = get_flip_fold_offset(other);
|
|
|
|
if (flip_offset == NULL)
|
|
|
|
return -1;
|
|
|
|
if (flip_offset != offset_other &&
|
|
|
|
delta_cmp(flip_offset, offset_other))
|
|
|
|
result = 1;
|
|
|
|
done:
|
|
|
|
Py_DECREF(flip_offset);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2006-08-24 14:29:38 -03:00
|
|
|
datetime_richcompare(PyObject *self, PyObject *other, int op)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *result = NULL;
|
|
|
|
PyObject *offset1, *offset2;
|
2010-05-09 12:52:27 -03:00
|
|
|
int diff;
|
|
|
|
|
|
|
|
if (! PyDateTime_Check(other)) {
|
|
|
|
if (PyDate_Check(other)) {
|
|
|
|
/* Prevent invocation of date_richcompare. We want to
|
|
|
|
return NotImplemented here to give the other object
|
|
|
|
a chance. But since DateTime is a subclass of
|
|
|
|
Date, if the other object is a Date, it would
|
|
|
|
compute an ordering based on the date part alone,
|
|
|
|
and we don't want that. So force unequal or
|
|
|
|
uncomparable here in that case. */
|
|
|
|
if (op == Py_EQ)
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
if (op == Py_NE)
|
|
|
|
Py_RETURN_TRUE;
|
|
|
|
return cmperror(self, other);
|
|
|
|
}
|
2011-08-10 22:28:54 -03:00
|
|
|
Py_RETURN_NOTIMPLEMENTED;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (GET_DT_TZINFO(self) == GET_DT_TZINFO(other)) {
|
|
|
|
diff = memcmp(((PyDateTime_DateTime *)self)->data,
|
|
|
|
((PyDateTime_DateTime *)other)->data,
|
|
|
|
_PyDateTime_DATETIME_DATASIZE);
|
|
|
|
return diff_to_bool(diff, op);
|
|
|
|
}
|
|
|
|
offset1 = datetime_utcoffset(self, NULL);
|
|
|
|
if (offset1 == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2010-07-07 20:56:38 -03:00
|
|
|
offset2 = datetime_utcoffset(other, NULL);
|
|
|
|
if (offset2 == NULL)
|
|
|
|
goto done;
|
2010-05-09 12:52:27 -03:00
|
|
|
/* If they're both naive, or both aware and have the same offsets,
|
|
|
|
* we get off cheap. Note that if they're both naive, offset1 ==
|
2010-07-07 20:56:38 -03:00
|
|
|
* offset2 == Py_None at this point.
|
2010-05-09 12:52:27 -03:00
|
|
|
*/
|
2010-07-07 20:56:38 -03:00
|
|
|
if ((offset1 == offset2) ||
|
|
|
|
(PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
|
|
|
|
delta_cmp(offset1, offset2) == 0)) {
|
2010-05-09 12:52:27 -03:00
|
|
|
diff = memcmp(((PyDateTime_DateTime *)self)->data,
|
|
|
|
((PyDateTime_DateTime *)other)->data,
|
|
|
|
_PyDateTime_DATETIME_DATASIZE);
|
2016-07-22 19:47:04 -03:00
|
|
|
if ((op == Py_EQ || op == Py_NE) && diff == 0) {
|
|
|
|
int ex = pep495_eq_exception(self, other, offset1, offset2);
|
|
|
|
if (ex == -1)
|
|
|
|
goto done;
|
|
|
|
if (ex)
|
|
|
|
diff = 1;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
result = diff_to_bool(diff, op);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
else if (offset1 != Py_None && offset2 != Py_None) {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDateTime_Delta *delta;
|
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
assert(offset1 != offset2); /* else last "if" handled it */
|
2010-05-09 12:52:27 -03:00
|
|
|
delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self,
|
|
|
|
other);
|
|
|
|
if (delta == NULL)
|
2010-07-07 20:56:38 -03:00
|
|
|
goto done;
|
2010-05-09 12:52:27 -03:00
|
|
|
diff = GET_TD_DAYS(delta);
|
|
|
|
if (diff == 0)
|
|
|
|
diff = GET_TD_SECONDS(delta) |
|
|
|
|
GET_TD_MICROSECONDS(delta);
|
|
|
|
Py_DECREF(delta);
|
2016-07-22 19:47:04 -03:00
|
|
|
if ((op == Py_EQ || op == Py_NE) && diff == 0) {
|
|
|
|
int ex = pep495_eq_exception(self, other, offset1, offset2);
|
|
|
|
if (ex == -1)
|
|
|
|
goto done;
|
|
|
|
if (ex)
|
|
|
|
diff = 1;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
result = diff_to_bool(diff, op);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2012-06-15 21:19:47 -03:00
|
|
|
else if (op == Py_EQ) {
|
|
|
|
result = Py_False;
|
|
|
|
Py_INCREF(result);
|
|
|
|
}
|
|
|
|
else if (op == Py_NE) {
|
|
|
|
result = Py_True;
|
|
|
|
Py_INCREF(result);
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
else {
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"can't compare offset-naive and "
|
|
|
|
"offset-aware datetimes");
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
Py_DECREF(offset1);
|
|
|
|
Py_XDECREF(offset2);
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2010-10-17 17:54:53 -03:00
|
|
|
static Py_hash_t
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_hash(PyDateTime_DateTime *self)
|
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
if (self->hashcode == -1) {
|
2016-07-22 19:47:04 -03:00
|
|
|
PyObject *offset, *self0;
|
|
|
|
if (DATE_GET_FOLD(self)) {
|
|
|
|
self0 = new_datetime_ex2(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
DATE_GET_MICROSECOND(self),
|
|
|
|
HASTZINFO(self) ? self->tzinfo : Py_None,
|
|
|
|
0, Py_TYPE(self));
|
|
|
|
if (self0 == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self0 = (PyObject *)self;
|
|
|
|
Py_INCREF(self0);
|
|
|
|
}
|
|
|
|
offset = datetime_utcoffset(self0, NULL);
|
|
|
|
Py_DECREF(self0);
|
2010-07-07 20:56:38 -03:00
|
|
|
|
|
|
|
if (offset == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Reduce this to a hash of another object. */
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == Py_None)
|
2010-05-09 12:52:27 -03:00
|
|
|
self->hashcode = generic_hash(
|
|
|
|
(unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE);
|
|
|
|
else {
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *temp1, *temp2;
|
|
|
|
int days, seconds;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
assert(HASTZINFO(self));
|
|
|
|
days = ymd_to_ord(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self));
|
|
|
|
seconds = DATE_GET_HOUR(self) * 3600 +
|
2010-07-07 20:56:38 -03:00
|
|
|
DATE_GET_MINUTE(self) * 60 +
|
2010-05-09 12:52:27 -03:00
|
|
|
DATE_GET_SECOND(self);
|
2010-07-07 20:56:38 -03:00
|
|
|
temp1 = new_delta(days, seconds,
|
|
|
|
DATE_GET_MICROSECOND(self),
|
|
|
|
1);
|
|
|
|
if (temp1 == NULL) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
temp2 = delta_subtract(temp1, offset);
|
|
|
|
Py_DECREF(temp1);
|
|
|
|
if (temp2 == NULL) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
self->hashcode = PyObject_Hash(temp2);
|
|
|
|
Py_DECREF(temp2);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(offset);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return self->hashcode;
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2002-12-24 01:41:27 -04:00
|
|
|
static PyObject *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
2002-12-24 01:41:27 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *clone;
|
|
|
|
PyObject *tuple;
|
|
|
|
int y = GET_YEAR(self);
|
|
|
|
int m = GET_MONTH(self);
|
|
|
|
int d = GET_DAY(self);
|
|
|
|
int hh = DATE_GET_HOUR(self);
|
|
|
|
int mm = DATE_GET_MINUTE(self);
|
|
|
|
int ss = DATE_GET_SECOND(self);
|
|
|
|
int us = DATE_GET_MICROSECOND(self);
|
|
|
|
PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None;
|
2016-07-22 19:47:04 -03:00
|
|
|
int fold = DATE_GET_FOLD(self);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiiiiO$i:replace",
|
2010-05-09 12:52:27 -03:00
|
|
|
datetime_kws,
|
|
|
|
&y, &m, &d, &hh, &mm, &ss, &us,
|
2016-07-22 19:47:04 -03:00
|
|
|
&tzinfo, &fold))
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2017-03-31 16:48:16 -03:00
|
|
|
if (fold != 0 && fold != 1) {
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
|
|
"fold must be either 0 or 1");
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
tuple = Py_BuildValue("iiiiiiiO", y, m, d, hh, mm, ss, us, tzinfo);
|
|
|
|
if (tuple == NULL)
|
|
|
|
return NULL;
|
|
|
|
clone = datetime_new(Py_TYPE(self), tuple, NULL);
|
2016-08-08 18:05:40 -03:00
|
|
|
if (clone != NULL) {
|
2016-07-22 19:47:04 -03:00
|
|
|
DATE_SET_FOLD(clone, fold);
|
2016-08-08 18:05:40 -03:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_DECREF(tuple);
|
|
|
|
return clone;
|
2002-12-24 01:41:27 -04:00
|
|
|
}
|
|
|
|
|
2012-06-22 13:23:23 -03:00
|
|
|
static PyObject *
|
2016-07-22 19:47:04 -03:00
|
|
|
local_timezone_from_timestamp(time_t timestamp)
|
2012-06-22 13:23:23 -03:00
|
|
|
{
|
|
|
|
PyObject *result = NULL;
|
|
|
|
PyObject *delta;
|
2016-09-10 16:58:31 -03:00
|
|
|
struct tm local_time_tm;
|
2012-06-22 13:23:23 -03:00
|
|
|
PyObject *nameo = NULL;
|
|
|
|
const char *zone = NULL;
|
|
|
|
|
2016-09-28 18:31:35 -03:00
|
|
|
if (_PyTime_localtime(timestamp, &local_time_tm) != 0)
|
2016-09-10 16:58:31 -03:00
|
|
|
return NULL;
|
2012-06-22 13:23:23 -03:00
|
|
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
2016-09-10 16:58:31 -03:00
|
|
|
zone = local_time_tm.tm_zone;
|
|
|
|
delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1);
|
2012-06-22 13:23:23 -03:00
|
|
|
#else /* HAVE_STRUCT_TM_TM_ZONE */
|
|
|
|
{
|
2016-07-22 19:47:04 -03:00
|
|
|
PyObject *local_time, *utc_time;
|
2016-09-10 16:58:31 -03:00
|
|
|
struct tm utc_time_tm;
|
2016-07-22 19:47:04 -03:00
|
|
|
char buf[100];
|
2016-09-10 16:58:31 -03:00
|
|
|
strftime(buf, sizeof(buf), "%Z", &local_time_tm);
|
2016-07-22 19:47:04 -03:00
|
|
|
zone = buf;
|
2016-09-10 16:58:31 -03:00
|
|
|
local_time = new_datetime(local_time_tm.tm_year + 1900,
|
|
|
|
local_time_tm.tm_mon + 1,
|
|
|
|
local_time_tm.tm_mday,
|
|
|
|
local_time_tm.tm_hour,
|
|
|
|
local_time_tm.tm_min,
|
|
|
|
local_time_tm.tm_sec, 0, Py_None, 0);
|
2016-07-22 19:47:04 -03:00
|
|
|
if (local_time == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-09-28 18:31:35 -03:00
|
|
|
if (_PyTime_gmtime(timestamp, &utc_time_tm) != 0)
|
2016-09-10 16:58:31 -03:00
|
|
|
return NULL;
|
|
|
|
utc_time = new_datetime(utc_time_tm.tm_year + 1900,
|
|
|
|
utc_time_tm.tm_mon + 1,
|
|
|
|
utc_time_tm.tm_mday,
|
|
|
|
utc_time_tm.tm_hour,
|
|
|
|
utc_time_tm.tm_min,
|
|
|
|
utc_time_tm.tm_sec, 0, Py_None, 0);
|
2016-07-22 19:47:04 -03:00
|
|
|
if (utc_time == NULL) {
|
|
|
|
Py_DECREF(local_time);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
delta = datetime_subtract(local_time, utc_time);
|
2012-06-22 13:23:23 -03:00
|
|
|
Py_DECREF(local_time);
|
2016-07-22 19:47:04 -03:00
|
|
|
Py_DECREF(utc_time);
|
2012-06-22 13:23:23 -03:00
|
|
|
}
|
|
|
|
#endif /* HAVE_STRUCT_TM_TM_ZONE */
|
2016-07-22 19:47:04 -03:00
|
|
|
if (delta == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-06-22 13:23:23 -03:00
|
|
|
if (zone != NULL) {
|
|
|
|
nameo = PyUnicode_DecodeLocale(zone, "surrogateescape");
|
|
|
|
if (nameo == NULL)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
result = new_timezone(delta, nameo);
|
2013-06-29 15:52:33 -03:00
|
|
|
Py_XDECREF(nameo);
|
2012-06-22 13:23:23 -03:00
|
|
|
error:
|
|
|
|
Py_DECREF(delta);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
static PyObject *
|
|
|
|
local_timezone(PyDateTime_DateTime *utc_time)
|
|
|
|
{
|
|
|
|
time_t timestamp;
|
|
|
|
PyObject *delta;
|
|
|
|
PyObject *one_second;
|
|
|
|
PyObject *seconds;
|
|
|
|
|
|
|
|
delta = datetime_subtract((PyObject *)utc_time, PyDateTime_Epoch);
|
|
|
|
if (delta == NULL)
|
|
|
|
return NULL;
|
|
|
|
one_second = new_delta(0, 1, 0, 0);
|
|
|
|
if (one_second == NULL) {
|
|
|
|
Py_DECREF(delta);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
seconds = divide_timedelta_timedelta((PyDateTime_Delta *)delta,
|
|
|
|
(PyDateTime_Delta *)one_second);
|
|
|
|
Py_DECREF(one_second);
|
|
|
|
Py_DECREF(delta);
|
|
|
|
if (seconds == NULL)
|
|
|
|
return NULL;
|
|
|
|
timestamp = _PyLong_AsTime_t(seconds);
|
|
|
|
Py_DECREF(seconds);
|
|
|
|
if (timestamp == -1 && PyErr_Occurred())
|
|
|
|
return NULL;
|
|
|
|
return local_timezone_from_timestamp(timestamp);
|
|
|
|
}
|
|
|
|
|
2016-09-06 14:46:49 -03:00
|
|
|
static long long
|
2016-07-22 19:47:04 -03:00
|
|
|
local_to_seconds(int year, int month, int day,
|
|
|
|
int hour, int minute, int second, int fold);
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
local_timezone_from_local(PyDateTime_DateTime *local_dt)
|
|
|
|
{
|
2016-09-06 14:46:49 -03:00
|
|
|
long long seconds;
|
2016-07-22 19:47:04 -03:00
|
|
|
time_t timestamp;
|
|
|
|
seconds = local_to_seconds(GET_YEAR(local_dt),
|
|
|
|
GET_MONTH(local_dt),
|
|
|
|
GET_DAY(local_dt),
|
|
|
|
DATE_GET_HOUR(local_dt),
|
|
|
|
DATE_GET_MINUTE(local_dt),
|
|
|
|
DATE_GET_SECOND(local_dt),
|
|
|
|
DATE_GET_FOLD(local_dt));
|
|
|
|
if (seconds == -1)
|
|
|
|
return NULL;
|
|
|
|
/* XXX: add bounds check */
|
|
|
|
timestamp = seconds - epoch;
|
|
|
|
return local_timezone_from_timestamp(timestamp);
|
|
|
|
}
|
|
|
|
|
2012-06-22 15:11:58 -03:00
|
|
|
static PyDateTime_DateTime *
|
2003-01-10 23:39:11 -04:00
|
|
|
datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
2002-12-25 03:40:55 -04:00
|
|
|
{
|
2012-06-22 14:23:21 -03:00
|
|
|
PyDateTime_DateTime *result;
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject *offset;
|
|
|
|
PyObject *temp;
|
2016-07-22 19:47:04 -03:00
|
|
|
PyObject *self_tzinfo;
|
2012-06-22 13:23:23 -03:00
|
|
|
PyObject *tzinfo = Py_None;
|
2010-05-09 12:52:27 -03:00
|
|
|
static char *keywords[] = {"tz", NULL};
|
|
|
|
|
2012-06-22 13:23:23 -03:00
|
|
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:astimezone", keywords,
|
2014-07-25 18:59:48 -03:00
|
|
|
&tzinfo))
|
2012-06-22 13:23:23 -03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (check_tzinfo_subclass(tzinfo) == -1)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
if (!HASTZINFO(self) || self->tzinfo == Py_None) {
|
2018-06-10 18:02:58 -03:00
|
|
|
naive:
|
2016-07-22 19:47:04 -03:00
|
|
|
self_tzinfo = local_timezone_from_local(self);
|
|
|
|
if (self_tzinfo == NULL)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
self_tzinfo = self->tzinfo;
|
|
|
|
Py_INCREF(self_tzinfo);
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* Conversion to self's own time zone is a NOP. */
|
2016-07-22 19:47:04 -03:00
|
|
|
if (self_tzinfo == tzinfo) {
|
|
|
|
Py_DECREF(self_tzinfo);
|
2010-05-09 12:52:27 -03:00
|
|
|
Py_INCREF(self);
|
2012-06-22 15:11:58 -03:00
|
|
|
return self;
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert self to UTC. */
|
2016-07-22 19:47:04 -03:00
|
|
|
offset = call_utcoffset(self_tzinfo, (PyObject *)self);
|
|
|
|
Py_DECREF(self_tzinfo);
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2018-06-10 18:02:58 -03:00
|
|
|
else if(offset == Py_None) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
goto naive;
|
|
|
|
}
|
|
|
|
else if (!PyDelta_Check(offset)) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s,"
|
|
|
|
" expected timedelta or None", Py_TYPE(offset)->tp_name);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
/* result = self - offset */
|
2012-06-22 14:23:21 -03:00
|
|
|
result = (PyDateTime_DateTime *)add_datetime_timedelta(self,
|
|
|
|
(PyDateTime_Delta *)offset, -1);
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(offset);
|
|
|
|
if (result == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
|
|
|
|
2016-07-22 19:47:04 -03:00
|
|
|
/* Make sure result is aware and UTC. */
|
|
|
|
if (!HASTZINFO(result)) {
|
|
|
|
temp = (PyObject *)result;
|
|
|
|
result = (PyDateTime_DateTime *)
|
|
|
|
new_datetime_ex2(GET_YEAR(result),
|
|
|
|
GET_MONTH(result),
|
|
|
|
GET_DAY(result),
|
|
|
|
DATE_GET_HOUR(result),
|
|
|
|
DATE_GET_MINUTE(result),
|
|
|
|
DATE_GET_SECOND(result),
|
|
|
|
DATE_GET_MICROSECOND(result),
|
|
|
|
PyDateTime_TimeZone_UTC,
|
|
|
|
DATE_GET_FOLD(result),
|
|
|
|
Py_TYPE(result));
|
|
|
|
Py_DECREF(temp);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Result is already aware - just replace tzinfo. */
|
|
|
|
temp = result->tzinfo;
|
|
|
|
result->tzinfo = PyDateTime_TimeZone_UTC;
|
|
|
|
Py_INCREF(result->tzinfo);
|
|
|
|
Py_DECREF(temp);
|
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Attach new tzinfo and let fromutc() do the rest. */
|
2012-06-22 14:23:21 -03:00
|
|
|
temp = result->tzinfo;
|
2012-06-22 13:23:23 -03:00
|
|
|
if (tzinfo == Py_None) {
|
|
|
|
tzinfo = local_timezone(result);
|
|
|
|
if (tzinfo == NULL) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Py_INCREF(tzinfo);
|
2012-06-22 14:23:21 -03:00
|
|
|
result->tzinfo = tzinfo;
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(temp);
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2012-06-22 14:23:21 -03:00
|
|
|
temp = (PyObject *)result;
|
2012-06-22 15:11:58 -03:00
|
|
|
result = (PyDateTime_DateTime *)
|
2019-07-11 05:59:05 -03:00
|
|
|
_PyObject_CallMethodIdOneArg(tzinfo, &PyId_fromutc, temp);
|
2010-07-07 20:56:38 -03:00
|
|
|
Py_DECREF(temp);
|
2003-01-23 12:36:11 -04:00
|
|
|
|
2012-06-22 15:11:58 -03:00
|
|
|
return result;
|
2002-12-25 03:40:55 -04:00
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_timetuple(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
int dstflag = -1;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
if (HASTZINFO(self) && self->tzinfo != Py_None) {
|
2010-07-07 20:56:38 -03:00
|
|
|
PyObject * dst;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
dst = call_dst(self->tzinfo, (PyObject *)self);
|
|
|
|
if (dst == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
if (dst != Py_None)
|
|
|
|
dstflag = delta_bool((PyDateTime_Delta *)dst);
|
|
|
|
Py_DECREF(dst);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
|
|
|
return build_struct_time(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
dstflag);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-09-06 14:46:49 -03:00
|
|
|
static long long
|
2016-07-22 19:47:04 -03:00
|
|
|
local_to_seconds(int year, int month, int day,
|
|
|
|
int hour, int minute, int second, int fold)
|
|
|
|
{
|
2016-09-06 14:46:49 -03:00
|
|
|
long long t, a, b, u1, u2, t1, t2, lt;
|
2016-07-22 19:47:04 -03:00
|
|
|
t = utc_to_seconds(year, month, day, hour, minute, second);
|
|
|
|
/* Our goal is to solve t = local(u) for u. */
|
|
|
|
lt = local(t);
|
|
|
|
if (lt == -1)
|
|
|
|
return -1;
|
|
|
|
a = lt - t;
|
|
|
|
u1 = t - a;
|
|
|
|
t1 = local(u1);
|
|
|
|
if (t1 == -1)
|
|
|
|
return -1;
|
|
|
|
if (t1 == t) {
|
|
|
|
/* We found one solution, but it may not be the one we need.
|
|
|
|
* Look for an earlier solution (if `fold` is 0), or a
|
|
|
|
* later one (if `fold` is 1). */
|
|
|
|
if (fold)
|
|
|
|
u2 = u1 + max_fold_seconds;
|
|
|
|
else
|
|
|
|
u2 = u1 - max_fold_seconds;
|
|
|
|
lt = local(u2);
|
|
|
|
if (lt == -1)
|
|
|
|
return -1;
|
|
|
|
b = lt - u2;
|
|
|
|
if (a == b)
|
|
|
|
return u1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
b = t1 - u1;
|
|
|
|
assert(a != b);
|
|
|
|
}
|
|
|
|
u2 = t - b;
|
|
|
|
t2 = local(u2);
|
|
|
|
if (t2 == -1)
|
|
|
|
return -1;
|
|
|
|
if (t2 == t)
|
|
|
|
return u2;
|
|
|
|
if (t1 == t)
|
|
|
|
return u1;
|
|
|
|
/* We have found both offsets a and b, but neither t - a nor t - b is
|
|
|
|
* a solution. This means t is in the gap. */
|
|
|
|
return fold?Py_MIN(u1, u2):Py_MAX(u1, u2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* date(1970,1,1).toordinal() == 719163 */
|
|
|
|
#define EPOCH_SECONDS (719163LL * 24 * 60 * 60)
|
|
|
|
|
2012-06-08 13:33:09 -03:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2012-06-08 13:33:09 -03:00
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
|
|
|
|
if (HASTZINFO(self) && self->tzinfo != Py_None) {
|
|
|
|
PyObject *delta;
|
|
|
|
delta = datetime_subtract((PyObject *)self, PyDateTime_Epoch);
|
|
|
|
if (delta == NULL)
|
|
|
|
return NULL;
|
2018-04-29 15:59:33 -03:00
|
|
|
result = delta_total_seconds(delta, NULL);
|
2012-06-08 13:33:09 -03:00
|
|
|
Py_DECREF(delta);
|
|
|
|
}
|
|
|
|
else {
|
2016-09-06 14:46:49 -03:00
|
|
|
long long seconds;
|
2016-07-22 19:47:04 -03:00
|
|
|
seconds = local_to_seconds(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self),
|
|
|
|
DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
DATE_GET_FOLD(self));
|
|
|
|
if (seconds == -1)
|
2012-06-08 13:33:09 -03:00
|
|
|
return NULL;
|
2016-07-22 19:47:04 -03:00
|
|
|
result = PyFloat_FromDouble(seconds - EPOCH_SECONDS +
|
|
|
|
DATE_GET_MICROSECOND(self) / 1e6);
|
2012-06-08 13:33:09 -03:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-12-16 16:18:38 -04:00
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2003-01-10 23:39:11 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return new_date(GET_YEAR(self),
|
|
|
|
GET_MONTH(self),
|
|
|
|
GET_DAY(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2003-01-10 23:39:11 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return new_time(DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
DATE_GET_MICROSECOND(self),
|
2016-07-22 19:47:04 -03:00
|
|
|
Py_None,
|
|
|
|
DATE_GET_FOLD(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_gettimetz(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2003-01-10 23:39:11 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
return new_time(DATE_GET_HOUR(self),
|
|
|
|
DATE_GET_MINUTE(self),
|
|
|
|
DATE_GET_SECOND(self),
|
|
|
|
DATE_GET_MICROSECOND(self),
|
2016-07-22 19:47:04 -03:00
|
|
|
GET_DT_TZINFO(self),
|
|
|
|
DATE_GET_FOLD(self));
|
2003-01-10 23:39:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2018-04-29 15:59:33 -03:00
|
|
|
datetime_utctimetuple(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-07-07 20:56:38 -03:00
|
|
|
int y, m, d, hh, mm, ss;
|
|
|
|
PyObject *tzinfo;
|
|
|
|
PyDateTime_DateTime *utcself;
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2010-07-07 20:56:38 -03:00
|
|
|
tzinfo = GET_DT_TZINFO(self);
|
|
|
|
if (tzinfo == Py_None) {
|
|
|
|
utcself = self;
|
|
|
|
Py_INCREF(utcself);
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
else {
|
|
|
|
PyObject *offset;
|
|
|
|
offset = call_utcoffset(tzinfo, (PyObject *)self);
|
|
|
|
if (offset == NULL)
|
2010-06-21 12:21:14 -03:00
|
|
|
return NULL;
|
2010-07-07 20:56:38 -03:00
|
|
|
if (offset == Py_None) {
|
|
|
|
Py_DECREF(offset);
|
|
|
|
utcself = self;
|
|
|
|
Py_INCREF(utcself);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
utcself = (PyDateTime_DateTime *)add_datetime_timedelta(self,
|
|
|
|
(PyDateTime_Delta *)offset, -1);
|
|
|
|
Py_DECREF(offset);
|
|
|
|
if (utcself == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
}
|
2010-07-07 20:56:38 -03:00
|
|
|
y = GET_YEAR(utcself);
|
|
|
|
m = GET_MONTH(utcself);
|
|
|
|
d = GET_DAY(utcself);
|
|
|
|
hh = DATE_GET_HOUR(utcself);
|
|
|
|
mm = DATE_GET_MINUTE(utcself);
|
|
|
|
ss = DATE_GET_SECOND(utcself);
|
|
|
|
|
|
|
|
Py_DECREF(utcself);
|
2010-05-09 12:52:27 -03:00
|
|
|
return build_struct_time(y, m, d, hh, mm, ss, 0);
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2003-01-31 21:52:50 -04:00
|
|
|
/* Pickle support, a simple use of __reduce__. */
|
2003-01-09 22:05:14 -04:00
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
/* Let basestate be the non-tzinfo data string.
|
2002-12-16 16:18:38 -04:00
|
|
|
* If tzinfo is None, this returns (basestate,), else (basestate, tzinfo).
|
|
|
|
* So it's a tuple in any (non-error) case.
|
2003-01-31 22:54:15 -04:00
|
|
|
* __getstate__ isn't exposed.
|
2002-12-16 16:18:38 -04:00
|
|
|
*/
|
|
|
|
static PyObject *
|
2016-07-22 19:47:04 -03:00
|
|
|
datetime_getstate(PyDateTime_DateTime *self, int proto)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2010-05-09 12:52:27 -03:00
|
|
|
PyObject *basestate;
|
|
|
|
PyObject *result = NULL;
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
basestate = PyBytes_FromStringAndSize((char *)self->data,
|
|
|
|
_PyDateTime_DATETIME_DATASIZE);
|
|
|
|
if (basestate != NULL) {
|
2016-07-22 19:47:04 -03:00
|
|
|
if (proto > 3 && DATE_GET_FOLD(self))
|
|
|
|
/* Set the first bit of the third byte */
|
|
|
|
PyBytes_AS_STRING(basestate)[2] |= (1 << 7);
|
2010-05-09 12:52:27 -03:00
|
|
|
if (! HASTZINFO(self) || self->tzinfo == Py_None)
|
|
|
|
result = PyTuple_Pack(1, basestate);
|
|
|
|
else
|
|
|
|
result = PyTuple_Pack(2, basestate, self->tzinfo);
|
|
|
|
Py_DECREF(basestate);
|
|
|
|
}
|
|
|
|
return result;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
2016-11-21 18:29:42 -04:00
|
|
|
datetime_reduce_ex(PyDateTime_DateTime *self, PyObject *args)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2016-11-21 18:29:42 -04:00
|
|
|
int proto;
|
|
|
|
if (!PyArg_ParseTuple(args, "i:__reduce_ex__", &proto))
|
2016-07-22 19:47:04 -03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return Py_BuildValue("(ON)", Py_TYPE(self), datetime_getstate(self, proto));
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
static PyObject *
|
|
|
|
datetime_reduce(PyDateTime_DateTime *self, PyObject *arg)
|
|
|
|
{
|
|
|
|
return Py_BuildValue("(ON)", Py_TYPE(self), datetime_getstate(self, 2));
|
|
|
|
}
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyMethodDef datetime_methods[] = {
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Class methods: */
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2013-11-18 13:32:13 -04:00
|
|
|
DATETIME_DATETIME_NOW_METHODDEF
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utcnow", (PyCFunction)datetime_utcnow,
|
|
|
|
METH_NOARGS | METH_CLASS,
|
|
|
|
PyDoc_STR("Return a new datetime representing UTC day and time.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"fromtimestamp", _PyCFunction_CAST(datetime_fromtimestamp),
|
2010-05-09 12:52:27 -03:00
|
|
|
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
|
|
|
|
PyDoc_STR("timestamp[, tz] -> tz's local time from POSIX timestamp.")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp,
|
|
|
|
METH_VARARGS | METH_CLASS,
|
2015-03-01 15:52:07 -04:00
|
|
|
PyDoc_STR("Construct a naive UTC datetime from a POSIX timestamp.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"strptime", (PyCFunction)datetime_strptime,
|
|
|
|
METH_VARARGS | METH_CLASS,
|
|
|
|
PyDoc_STR("string, format -> new datetime parsed from a string "
|
|
|
|
"(like time.strptime()).")},
|
2005-01-13 00:12:31 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"combine", _PyCFunction_CAST(datetime_combine),
|
2010-05-09 12:52:27 -03:00
|
|
|
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
|
|
|
|
PyDoc_STR("date, time -> datetime with same date and time fields")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2017-12-21 01:33:49 -04:00
|
|
|
{"fromisoformat", (PyCFunction)datetime_fromisoformat,
|
|
|
|
METH_O | METH_CLASS,
|
2022-05-05 21:31:24 -03:00
|
|
|
PyDoc_STR("string -> datetime from a string in most ISO 8601 formats")},
|
2017-12-21 01:33:49 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* Instance methods: */
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"date", (PyCFunction)datetime_getdate, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return date object with same year, month and day.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"time", (PyCFunction)datetime_gettime, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return time object with same time but with tzinfo=None.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"timetz", (PyCFunction)datetime_gettimetz, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return time object with same time and tzinfo.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"ctime", (PyCFunction)datetime_ctime, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return ctime() style string.")},
|
2003-01-10 23:39:11 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"timetuple", (PyCFunction)datetime_timetuple, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return time tuple, compatible with time.localtime().")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2012-06-08 13:33:09 -03:00
|
|
|
{"timestamp", (PyCFunction)datetime_timestamp, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return POSIX timestamp as float.")},
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"isoformat", _PyCFunction_CAST(datetime_isoformat), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("[sep] -> string in ISO 8601 format, "
|
2016-03-06 15:58:43 -04:00
|
|
|
"YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n"
|
2010-05-09 12:52:27 -03:00
|
|
|
"sep is used to separate the year from the time, and "
|
2016-03-06 15:58:43 -04:00
|
|
|
"defaults to 'T'.\n"
|
2020-10-03 07:43:47 -03:00
|
|
|
"The optional argument timespec specifies the number "
|
|
|
|
"of additional terms\nof the time to include. Valid "
|
|
|
|
"options are 'auto', 'hours', 'minutes',\n'seconds', "
|
|
|
|
"'milliseconds' and 'microseconds'.\n")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.utcoffset(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"tzname", (PyCFunction)datetime_tzname, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.tzname(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{"dst", (PyCFunction)datetime_dst, METH_NOARGS,
|
|
|
|
PyDoc_STR("Return self.tzinfo.dst(self).")},
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"replace", _PyCFunction_CAST(datetime_replace), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("Return datetime with new specified fields.")},
|
2002-12-24 01:41:27 -04:00
|
|
|
|
2022-05-03 16:42:14 -03:00
|
|
|
{"astimezone", _PyCFunction_CAST(datetime_astimezone), METH_VARARGS | METH_KEYWORDS,
|
2010-05-09 12:52:27 -03:00
|
|
|
PyDoc_STR("tz -> convert to local time in new timezone tz\n")},
|
2002-12-25 03:40:55 -04:00
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
{"__reduce_ex__", (PyCFunction)datetime_reduce_ex, METH_VARARGS,
|
2016-07-22 19:47:04 -03:00
|
|
|
PyDoc_STR("__reduce_ex__(proto) -> (cls, state)")},
|
2003-01-30 18:06:23 -04:00
|
|
|
|
2016-11-21 18:29:42 -04:00
|
|
|
{"__reduce__", (PyCFunction)datetime_reduce, METH_NOARGS,
|
|
|
|
PyDoc_STR("__reduce__() -> (cls, state)")},
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2015-12-25 13:53:18 -04:00
|
|
|
static const char datetime_doc[] =
|
2004-12-19 16:13:24 -04:00
|
|
|
PyDoc_STR("datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\
|
|
|
|
\n\
|
|
|
|
The year, month and day arguments are required. tzinfo may be None, or an\n\
|
2013-08-27 13:40:23 -03:00
|
|
|
instance of a tzinfo subclass. The remaining arguments may be ints.\n");
|
2002-12-16 16:18:38 -04:00
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
static PyNumberMethods datetime_as_number = {
|
2010-05-09 12:52:27 -03:00
|
|
|
datetime_add, /* nb_add */
|
|
|
|
datetime_subtract, /* nb_subtract */
|
|
|
|
0, /* nb_multiply */
|
|
|
|
0, /* nb_remainder */
|
|
|
|
0, /* nb_divmod */
|
|
|
|
0, /* nb_power */
|
|
|
|
0, /* nb_negative */
|
|
|
|
0, /* nb_positive */
|
|
|
|
0, /* nb_absolute */
|
|
|
|
0, /* nb_bool */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2006-03-22 05:28:35 -04:00
|
|
|
static PyTypeObject PyDateTime_DateTimeType = {
|
2010-05-09 12:52:27 -03:00
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
|
|
"datetime.datetime", /* tp_name */
|
|
|
|
sizeof(PyDateTime_DateTime), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
(destructor)datetime_dealloc, /* tp_dealloc */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_vectorcall_offset */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
2019-05-30 23:13:39 -03:00
|
|
|
0, /* tp_as_async */
|
2010-05-09 12:52:27 -03:00
|
|
|
(reprfunc)datetime_repr, /* tp_repr */
|
|
|
|
&datetime_as_number, /* tp_as_number */
|
|
|
|
0, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
(hashfunc)datetime_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
(reprfunc)datetime_str, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
datetime_doc, /* tp_doc */
|
|
|
|
0, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
datetime_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
0, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
datetime_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
datetime_getset, /* tp_getset */
|
2020-05-28 13:14:46 -03:00
|
|
|
0, /* tp_base; filled in
|
|
|
|
PyInit__datetime */
|
2010-05-09 12:52:27 -03:00
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
datetime_alloc, /* tp_alloc */
|
|
|
|
datetime_new, /* tp_new */
|
|
|
|
0, /* tp_free */
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
* Module methods and initialization.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PyMethodDef module_methods[] = {
|
2010-05-09 12:52:27 -03:00
|
|
|
{NULL, NULL}
|
2002-12-16 16:18:38 -04:00
|
|
|
};
|
|
|
|
|
2021-01-06 15:47:19 -04:00
|
|
|
/* Get a new C API by calling this function.
|
|
|
|
* Clients get at C API via PyDateTime_IMPORT, defined in datetime.h.
|
2004-06-20 19:41:32 -03:00
|
|
|
*/
|
2021-01-06 15:47:19 -04:00
|
|
|
static inline PyDateTime_CAPI *
|
|
|
|
get_datetime_capi(void)
|
|
|
|
{
|
|
|
|
PyDateTime_CAPI *capi = PyMem_Malloc(sizeof(PyDateTime_CAPI));
|
|
|
|
if (capi == NULL) {
|
|
|
|
PyErr_NoMemory();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
capi->DateType = &PyDateTime_DateType;
|
|
|
|
capi->DateTimeType = &PyDateTime_DateTimeType;
|
|
|
|
capi->TimeType = &PyDateTime_TimeType;
|
|
|
|
capi->DeltaType = &PyDateTime_DeltaType;
|
|
|
|
capi->TZInfoType = &PyDateTime_TZInfoType;
|
|
|
|
capi->Date_FromDate = new_date_ex;
|
|
|
|
capi->DateTime_FromDateAndTime = new_datetime_ex;
|
|
|
|
capi->Time_FromTime = new_time_ex;
|
|
|
|
capi->Delta_FromDelta = new_delta_ex;
|
|
|
|
capi->TimeZone_FromTimeZone = new_timezone;
|
|
|
|
capi->DateTime_FromTimestamp = datetime_fromtimestamp;
|
|
|
|
capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi;
|
|
|
|
capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2;
|
|
|
|
capi->Time_FromTimeAndFold = new_time_ex2;
|
|
|
|
// Make sure this function is called after PyDateTime_TimeZone_UTC has
|
|
|
|
// been initialized.
|
|
|
|
assert(PyDateTime_TimeZone_UTC != NULL);
|
|
|
|
capi->TimeZone_UTC = PyDateTime_TimeZone_UTC; // borrowed ref
|
|
|
|
return capi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
datetime_destructor(PyObject *op)
|
|
|
|
{
|
|
|
|
void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME);
|
|
|
|
PyMem_Free(ptr);
|
|
|
|
}
|
2004-06-20 19:41:32 -03:00
|
|
|
|
2020-11-20 06:39:40 -04:00
|
|
|
static int
|
|
|
|
_datetime_exec(PyObject *module)
|
2002-12-16 16:18:38 -04:00
|
|
|
{
|
2020-05-28 13:14:46 -03:00
|
|
|
// `&...` is not a constant expression according to a strict reading
|
|
|
|
// of C standards. Fill tp_base at run-time rather than statically.
|
|
|
|
// See https://bugs.python.org/issue40777
|
|
|
|
PyDateTime_IsoCalendarDateType.tp_base = &PyTuple_Type;
|
|
|
|
PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType;
|
|
|
|
PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType;
|
2020-05-16 11:02:59 -03:00
|
|
|
|
bpo-40024: Update C extension modules to use PyModule_AddType() (GH-19119)
Update _asyncio, _bz2, _csv, _curses, _datetime,
_io, _operator, _pickle, _queue, blake2,
multibytecodec and overlapped C extension modules
to use PyModule_AddType().
2020-03-24 19:08:51 -03:00
|
|
|
PyTypeObject *types[] = {
|
|
|
|
&PyDateTime_DateType,
|
|
|
|
&PyDateTime_DateTimeType,
|
|
|
|
&PyDateTime_TimeType,
|
|
|
|
&PyDateTime_DeltaType,
|
|
|
|
&PyDateTime_TZInfoType,
|
2020-05-16 11:02:59 -03:00
|
|
|
&PyDateTime_TimeZoneType,
|
bpo-40024: Update C extension modules to use PyModule_AddType() (GH-19119)
Update _asyncio, _bz2, _csv, _curses, _datetime,
_io, _operator, _pickle, _queue, blake2,
multibytecodec and overlapped C extension modules
to use PyModule_AddType().
2020-03-24 19:08:51 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) {
|
2020-11-20 06:39:40 -04:00
|
|
|
if (PyModule_AddType(module, types[i]) < 0) {
|
|
|
|
return -1;
|
bpo-40024: Update C extension modules to use PyModule_AddType() (GH-19119)
Update _asyncio, _bz2, _csv, _curses, _datetime,
_io, _operator, _pickle, _queue, blake2,
multibytecodec and overlapped C extension modules
to use PyModule_AddType().
2020-03-24 19:08:51 -03:00
|
|
|
}
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2020-05-16 11:02:59 -03:00
|
|
|
if (PyType_Ready(&PyDateTime_IsoCalendarDateType) < 0) {
|
2020-11-20 06:39:40 -04:00
|
|
|
return -1;
|
2020-05-16 11:02:59 -03:00
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2020-11-20 06:39:40 -04:00
|
|
|
#define DATETIME_ADD_MACRO(dict, c, value_expr) \
|
|
|
|
do { \
|
|
|
|
PyObject *value = (value_expr); \
|
|
|
|
if (value == NULL) { \
|
|
|
|
return -1; \
|
|
|
|
} \
|
|
|
|
if (PyDict_SetItemString(dict, c, value) < 0) { \
|
|
|
|
Py_DECREF(value); \
|
|
|
|
return -1; \
|
|
|
|
} \
|
|
|
|
Py_DECREF(value); \
|
|
|
|
} while(0)
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2020-11-20 06:39:40 -04:00
|
|
|
/* timedelta values */
|
|
|
|
PyObject *d = PyDateTime_DeltaType.tp_dict;
|
|
|
|
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "max",
|
|
|
|
new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0));
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* date values */
|
|
|
|
d = PyDateTime_DateType.tp_dict;
|
2020-11-20 06:39:40 -04:00
|
|
|
DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1));
|
|
|
|
DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31));
|
|
|
|
DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0));
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* time values */
|
|
|
|
d = PyDateTime_TimeType.tp_dict;
|
2020-11-20 06:39:40 -04:00
|
|
|
DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* datetime values */
|
|
|
|
d = PyDateTime_DateTimeType.tp_dict;
|
2020-11-20 06:39:40 -04:00
|
|
|
DATETIME_ADD_MACRO(d, "min",
|
|
|
|
new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59,
|
|
|
|
999999, Py_None, 0));
|
|
|
|
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2010-06-14 11:15:50 -03:00
|
|
|
/* timezone values */
|
|
|
|
d = PyDateTime_TimeZoneType.tp_dict;
|
2020-11-20 06:39:40 -04:00
|
|
|
PyObject *delta = new_delta(0, 0, 0, 0);
|
|
|
|
if (delta == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-06-14 11:15:50 -03:00
|
|
|
|
2020-11-20 06:39:40 -04:00
|
|
|
PyObject *x = create_timezone(delta, NULL);
|
2010-06-14 11:15:50 -03:00
|
|
|
Py_DECREF(delta);
|
2020-11-20 06:39:40 -04:00
|
|
|
if (x == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (PyDict_SetItemString(d, "utc", x) < 0) {
|
|
|
|
Py_DECREF(x);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-07-06 20:19:45 -03:00
|
|
|
PyDateTime_TimeZone_UTC = x;
|
2010-06-14 11:15:50 -03:00
|
|
|
|
2019-08-09 11:22:16 -03:00
|
|
|
/* bpo-37642: These attributes are rounded to the nearest minute for backwards
|
|
|
|
* compatibility, even though the constructor will accept a wider range of
|
|
|
|
* values. This may change in the future.*/
|
2010-06-14 11:15:50 -03:00
|
|
|
delta = new_delta(-1, 60, 0, 1); /* -23:59 */
|
2020-11-20 06:39:40 -04:00
|
|
|
if (delta == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-10-14 14:03:51 -03:00
|
|
|
x = create_timezone(delta, NULL);
|
2010-06-14 11:15:50 -03:00
|
|
|
Py_DECREF(delta);
|
2020-11-20 06:39:40 -04:00
|
|
|
DATETIME_ADD_MACRO(d, "min", x);
|
2010-06-14 11:15:50 -03:00
|
|
|
|
|
|
|
delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */
|
2020-11-20 06:39:40 -04:00
|
|
|
if (delta == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-10-14 14:03:51 -03:00
|
|
|
x = create_timezone(delta, NULL);
|
2010-06-14 11:15:50 -03:00
|
|
|
Py_DECREF(delta);
|
2020-11-20 06:39:40 -04:00
|
|
|
DATETIME_ADD_MACRO(d, "max", x);
|
2010-06-14 11:15:50 -03:00
|
|
|
|
2012-06-08 13:33:09 -03:00
|
|
|
/* Epoch */
|
|
|
|
PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0,
|
2016-07-22 19:47:04 -03:00
|
|
|
PyDateTime_TimeZone_UTC, 0);
|
2020-11-20 06:39:40 -04:00
|
|
|
if (PyDateTime_Epoch == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
2012-06-08 13:33:09 -03:00
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* module initialization */
|
2020-11-20 06:39:40 -04:00
|
|
|
if (PyModule_AddIntMacro(module, MINYEAR) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (PyModule_AddIntMacro(module, MAXYEAR) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2021-01-06 15:47:19 -04:00
|
|
|
PyDateTime_CAPI *capi = get_datetime_capi();
|
|
|
|
if (capi == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
x = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME, datetime_destructor);
|
2020-11-20 06:39:40 -04:00
|
|
|
if (x == NULL) {
|
2021-01-06 15:47:19 -04:00
|
|
|
PyMem_Free(capi);
|
2020-11-20 06:39:40 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyModule_AddObject(module, "datetime_CAPI", x) < 0) {
|
|
|
|
Py_DECREF(x);
|
|
|
|
return -1;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
2022-05-03 19:14:25 -03:00
|
|
|
if (PyModule_AddObjectRef(module, "UTC", PyDateTime_TimeZone_UTC) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-05-09 12:52:27 -03:00
|
|
|
/* A 4-year cycle has an extra leap day over what we'd get from
|
|
|
|
* pasting together 4 single years.
|
|
|
|
*/
|
2022-04-20 14:26:40 -03:00
|
|
|
static_assert(DI4Y == 4 * 365 + 1, "DI4Y");
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(DI4Y == days_before_year(4+1));
|
|
|
|
|
|
|
|
/* Similarly, a 400-year cycle has an extra leap day over what we'd
|
|
|
|
* get from pasting together 4 100-year cycles.
|
|
|
|
*/
|
2022-04-20 14:26:40 -03:00
|
|
|
static_assert(DI400Y == 4 * DI100Y + 1, "DI400Y");
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(DI400Y == days_before_year(400+1));
|
|
|
|
|
|
|
|
/* OTOH, a 100-year cycle has one fewer leap day than we'd get from
|
|
|
|
* pasting together 25 4-year cycles.
|
|
|
|
*/
|
2022-04-20 14:26:40 -03:00
|
|
|
static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y");
|
2010-05-09 12:52:27 -03:00
|
|
|
assert(DI100Y == days_before_year(100+1));
|
|
|
|
|
|
|
|
us_per_ms = PyLong_FromLong(1000);
|
|
|
|
us_per_second = PyLong_FromLong(1000000);
|
|
|
|
us_per_minute = PyLong_FromLong(60000000);
|
|
|
|
seconds_per_day = PyLong_FromLong(24 * 3600);
|
2017-03-30 03:09:41 -03:00
|
|
|
if (us_per_ms == NULL || us_per_second == NULL ||
|
2020-11-20 06:39:40 -04:00
|
|
|
us_per_minute == NULL || seconds_per_day == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-05-09 12:52:27 -03:00
|
|
|
|
|
|
|
/* The rest are too big for 32-bit ints, but even
|
|
|
|
* us_per_week fits in 40 bits, so doubles should be exact.
|
|
|
|
*/
|
|
|
|
us_per_hour = PyLong_FromDouble(3600000000.0);
|
|
|
|
us_per_day = PyLong_FromDouble(86400000000.0);
|
|
|
|
us_per_week = PyLong_FromDouble(604800000000.0);
|
2020-11-20 06:39:40 -04:00
|
|
|
if (us_per_hour == NULL || us_per_day == NULL || us_per_week == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct PyModuleDef datetimemodule = {
|
|
|
|
PyModuleDef_HEAD_INIT,
|
|
|
|
.m_name = "_datetime",
|
|
|
|
.m_doc = "Fast implementation of the datetime type.",
|
|
|
|
.m_size = -1,
|
|
|
|
.m_methods = module_methods,
|
|
|
|
};
|
|
|
|
|
|
|
|
PyMODINIT_FUNC
|
|
|
|
PyInit__datetime(void)
|
|
|
|
{
|
|
|
|
PyObject *mod = PyModule_Create(&datetimemodule);
|
|
|
|
if (mod == NULL)
|
2010-05-09 12:52:27 -03:00
|
|
|
return NULL;
|
2020-11-20 06:39:40 -04:00
|
|
|
|
|
|
|
if (_datetime_exec(mod) < 0) {
|
|
|
|
Py_DECREF(mod);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mod;
|
2002-12-16 16:18:38 -04:00
|
|
|
}
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
2003-01-10 23:39:11 -04:00
|
|
|
Some time zone algebra. For a datetime x, let
|
2003-01-01 17:51:37 -04:00
|
|
|
x.n = x stripped of its timezone -- its naive time.
|
|
|
|
x.o = x.utcoffset(), and assuming that doesn't raise an exception or
|
2010-05-09 12:52:27 -03:00
|
|
|
return None
|
2003-01-01 17:51:37 -04:00
|
|
|
x.d = x.dst(), and assuming that doesn't raise an exception or
|
2010-05-09 12:52:27 -03:00
|
|
|
return None
|
2003-01-01 17:51:37 -04:00
|
|
|
x.s = x's standard offset, x.o - x.d
|
|
|
|
|
|
|
|
Now some derived rules, where k is a duration (timedelta).
|
|
|
|
|
|
|
|
1. x.o = x.s + x.d
|
|
|
|
This follows from the definition of x.s.
|
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
2. If x and y have the same tzinfo member, x.s = y.s.
|
2003-01-01 17:51:37 -04:00
|
|
|
This is actually a requirement, an assumption we need to make about
|
|
|
|
sane tzinfo classes.
|
|
|
|
|
|
|
|
3. The naive UTC time corresponding to x is x.n - x.o.
|
|
|
|
This is again a requirement for a sane tzinfo class.
|
|
|
|
|
|
|
|
4. (x+k).s = x.s
|
2003-01-23 22:44:45 -04:00
|
|
|
This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
|
2003-01-01 17:51:37 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
5. (x+k).n = x.n + k
|
2003-01-01 17:51:37 -04:00
|
|
|
Again follows from how arithmetic is defined.
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
Now we can explain tz.fromutc(x). Let's assume it's an interesting case
|
2003-01-01 17:51:37 -04:00
|
|
|
(meaning that the various tzinfo methods exist, and don't blow up or return
|
|
|
|
None when called).
|
|
|
|
|
2003-01-10 23:39:11 -04:00
|
|
|
The function wants to return a datetime y with timezone tz, equivalent to x.
|
2003-01-23 22:44:45 -04:00
|
|
|
x is already in UTC.
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
By #3, we want
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
y.n - y.o = x.n [1]
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
The algorithm starts by attaching tz to x.n, and calling that y. So
|
|
|
|
x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
|
|
|
|
becomes true; in effect, we want to solve [2] for k:
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
(y+k).n - (y+k).o = x.n [2]
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
By #1, this is the same as
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
(y+k).n - ((y+k).s + (y+k).d) = x.n [3]
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
|
|
|
|
Substituting that into [3],
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
|
|
|
|
k - (y+k).s - (y+k).d = 0; rearranging,
|
|
|
|
k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
|
|
|
|
k = y.s - (y+k).d
|
2003-01-01 17:51:37 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
|
|
|
|
approximate k by ignoring the (y+k).d term at first. Note that k can't be
|
|
|
|
very large, since all offset-returning methods return a duration of magnitude
|
|
|
|
less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
|
|
|
|
be 0, so ignoring it has no consequence then.
|
2003-01-01 17:51:37 -04:00
|
|
|
|
|
|
|
In any case, the new value is
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
z = y + y.s [4]
|
2003-01-01 17:51:37 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
It's helpful to step back at look at [4] from a higher level: it's simply
|
|
|
|
mapping from UTC to tz's standard time.
|
2003-01-02 13:55:03 -04:00
|
|
|
|
|
|
|
At this point, if
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
z.n - z.o = x.n [5]
|
2003-01-02 13:55:03 -04:00
|
|
|
|
|
|
|
we have an equivalent time, and are almost done. The insecurity here is
|
2003-01-01 17:51:37 -04:00
|
|
|
at the start of daylight time. Picture US Eastern for concreteness. The wall
|
|
|
|
time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
|
2003-01-23 22:44:45 -04:00
|
|
|
sense then. The docs ask that an Eastern tzinfo class consider such a time to
|
|
|
|
be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
|
|
|
|
on the day DST starts. We want to return the 1:MM EST spelling because that's
|
2003-01-01 17:51:37 -04:00
|
|
|
the only spelling that makes sense on the local wall clock.
|
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
In fact, if [5] holds at this point, we do have the standard-time spelling,
|
|
|
|
but that takes a bit of proof. We first prove a stronger result. What's the
|
|
|
|
difference between the LHS and RHS of [5]? Let
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
diff = x.n - (z.n - z.o) [6]
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
Now
|
|
|
|
z.n = by [4]
|
2003-01-23 22:44:45 -04:00
|
|
|
(y + y.s).n = by #5
|
|
|
|
y.n + y.s = since y.n = x.n
|
|
|
|
x.n + y.s = since z and y are have the same tzinfo member,
|
|
|
|
y.s = z.s by #2
|
|
|
|
x.n + z.s
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
Plugging that back into [6] gives
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
diff =
|
2003-01-23 22:44:45 -04:00
|
|
|
x.n - ((x.n + z.s) - z.o) = expanding
|
|
|
|
x.n - x.n - z.s + z.o = cancelling
|
|
|
|
- z.s + z.o = by #2
|
2003-01-02 13:55:03 -04:00
|
|
|
z.d
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
So diff = z.d.
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
|
2003-01-23 22:44:45 -04:00
|
|
|
spelling we wanted in the endcase described above. We're done. Contrarily,
|
|
|
|
if z.d = 0, then we have a UTC equivalent, and are also done.
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
|
|
|
|
add to z (in effect, z is in tz's standard time, and we need to shift the
|
2003-01-23 22:44:45 -04:00
|
|
|
local clock into tz's daylight time).
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-02 13:55:03 -04:00
|
|
|
Let
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-03 20:26:59 -04:00
|
|
|
z' = z + z.d = z + diff [7]
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-03 20:26:59 -04:00
|
|
|
and we can again ask whether
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
z'.n - z'.o = x.n [8]
|
2003-01-01 23:14:59 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
If so, we're done. If not, the tzinfo class is insane, according to the
|
|
|
|
assumptions we've made. This also requires a bit of proof. As before, let's
|
|
|
|
compute the difference between the LHS and RHS of [8] (and skipping some of
|
|
|
|
the justifications for the kinds of substitutions we've done several times
|
|
|
|
already):
|
2003-01-03 20:26:59 -04:00
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
|
2010-05-09 12:52:27 -03:00
|
|
|
x.n - (z.n + diff - z'.o) = replacing diff via [6]
|
|
|
|
x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
|
|
|
|
x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
|
|
|
|
- z.n + z.n - z.o + z'.o = cancel z.n
|
|
|
|
- z.o + z'.o = #1 twice
|
|
|
|
-z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
|
|
|
|
z'.d - z.d
|
2003-01-03 20:26:59 -04:00
|
|
|
|
|
|
|
So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
|
2003-01-23 22:44:45 -04:00
|
|
|
we've found the UTC-equivalent so are done. In fact, we stop with [7] and
|
|
|
|
return z', not bothering to compute z'.d.
|
|
|
|
|
|
|
|
How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
|
|
|
|
a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
|
|
|
|
would have to change the result dst() returns: we start in DST, and moving
|
|
|
|
a little further into it takes us out of DST.
|
|
|
|
|
|
|
|
There isn't a sane case where this can happen. The closest it gets is at
|
|
|
|
the end of DST, where there's an hour in UTC with no spelling in a hybrid
|
|
|
|
tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
|
|
|
|
that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
|
|
|
|
UTC) because the docs insist on that, but 0:MM is taken as being in daylight
|
|
|
|
time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
|
|
|
|
clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
|
|
|
|
standard time. Since that's what the local clock *does*, we want to map both
|
|
|
|
UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
|
2003-01-03 20:26:59 -04:00
|
|
|
in local time, but so it goes -- it's the way the local clock works.
|
|
|
|
|
2003-01-23 22:44:45 -04:00
|
|
|
When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
|
|
|
|
so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
|
|
|
|
z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
|
2003-01-03 20:26:59 -04:00
|
|
|
(correctly) concludes that z' is not UTC-equivalent to x.
|
|
|
|
|
|
|
|
Because we know z.d said z was in daylight time (else [5] would have held and
|
|
|
|
we would have stopped then), and we know z.d != z'.d (else [8] would have held
|
2003-10-20 11:01:56 -03:00
|
|
|
and we would have stopped then), and there are only 2 possible values dst() can
|
2003-01-03 20:26:59 -04:00
|
|
|
return in Eastern, it follows that z'.d must be 0 (which it is in the example,
|
|
|
|
but the reasoning doesn't depend on the example -- it depends on there being
|
|
|
|
two possible dst() outcomes, one zero and the other non-zero). Therefore
|
2003-01-23 22:44:45 -04:00
|
|
|
z' must be in standard time, and is the spelling we want in this case.
|
|
|
|
|
|
|
|
Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
|
|
|
|
concerned (because it takes z' as being in standard time rather than the
|
|
|
|
daylight time we intend here), but returning it gives the real-life "local
|
|
|
|
clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
|
|
|
|
tz.
|
|
|
|
|
|
|
|
When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
|
|
|
|
the 1:MM standard time spelling we want.
|
|
|
|
|
|
|
|
So how can this break? One of the assumptions must be violated. Two
|
|
|
|
possibilities:
|
|
|
|
|
|
|
|
1) [2] effectively says that y.s is invariant across all y belong to a given
|
|
|
|
time zone. This isn't true if, for political reasons or continental drift,
|
|
|
|
a region decides to change its base offset from UTC.
|
|
|
|
|
|
|
|
2) There may be versions of "double daylight" time where the tail end of
|
|
|
|
the analysis gives up a step too early. I haven't thought about that
|
|
|
|
enough to say.
|
|
|
|
|
|
|
|
In any case, it's clear that the default fromutc() is strong enough to handle
|
|
|
|
"almost all" time zones: so long as the standard offset is invariant, it
|
|
|
|
doesn't matter if daylight time transition points change from year to year, or
|
|
|
|
if daylight time is skipped in some years; it doesn't matter how large or
|
|
|
|
small dst() may get within its bounds; and it doesn't even matter if some
|
|
|
|
perverse time zone returns a negative dst()). So a breaking case must be
|
|
|
|
pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
|
2003-01-01 17:51:37 -04:00
|
|
|
--------------------------------------------------------------------------- */
|