Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6691 lines (5835 sloc) 212 KB
/* C implementation for the date/time type documented at
* http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage
*/
/* 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
#include "Python.h"
#include "datetime.h"
#include "structmember.h"
#include <time.h>
#ifdef MS_WINDOWS
# include <winsock2.h> /* struct timeval */
#endif
#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType)
#define PyDate_CheckExact(op) (Py_TYPE(op) == &PyDateTime_DateType)
#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType)
#define PyDateTime_CheckExact(op) (Py_TYPE(op) == &PyDateTime_DateTimeType)
#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType)
#define PyTime_CheckExact(op) (Py_TYPE(op) == &PyDateTime_TimeType)
#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType)
#define PyDelta_CheckExact(op) (Py_TYPE(op) == &PyDateTime_DeltaType)
#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType)
#define PyTZInfo_CheckExact(op) (Py_TYPE(op) == &PyDateTime_TZInfoType)
/*[clinic input]
module datetime
class datetime.datetime "PyDateTime_DateTime *" "&PyDateTime_DateTimeType"
class datetime.date "PyDateTime_Date *" "&PyDateTime_DateType"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=25138ad6a696b785]*/
#include "clinic/_datetimemodule.c.h"
/* 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
# error "_datetime.c requires that C int have at least 32 bits"
#endif
#define MINYEAR 1
#define MAXYEAR 9999
#define MAXORDINAL 3652059 /* date(9999,12,31).toordinal() */
/* 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. */
#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
#define DATE_GET_FOLD PyDateTime_DATE_GET_FOLD
/* Date accessors for date and datetime. */
#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))
/* Date/Time accessors for datetime. */
#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)))
#define DATE_SET_FOLD(o, v) (PyDateTime_DATE_GET_FOLD(o) = (v))
/* Time accessors for time. */
#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
#define TIME_GET_FOLD PyDateTime_TIME_GET_FOLD
#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)))
#define TIME_SET_FOLD(o, v) (PyDateTime_TIME_GET_FOLD(o) = (v))
/* Delta accessors for timedelta. */
#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)
#define SET_TD_DAYS(o, v) ((o)->days = (v))
#define SET_TD_SECONDS(o, v) ((o)->seconds = (v))
#define SET_TD_MICROSECONDS(o, v) ((o)->microseconds = (v))
/* p is a pointer to a time or a datetime object; HASTZINFO(p) returns
* p->hastzinfo.
*/
#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo)
#define GET_TIME_TZINFO(p) (HASTZINFO(p) ? \
((PyDateTime_Time *)(p))->tzinfo : Py_None)
#define GET_DT_TZINFO(p) (HASTZINFO(p) ? \
((PyDateTime_DateTime *)(p))->tzinfo : Py_None)
/* M is a char or int claiming to be a valid month. The macro is equivalent
* to the two-sided Python test
* 1 <= M <= 12
*/
#define MONTH_IS_SANE(M) ((unsigned int)(M) - 1 < 12)
/* Forward declarations. */
static PyTypeObject PyDateTime_DateType;
static PyTypeObject PyDateTime_DateTimeType;
static PyTypeObject PyDateTime_DeltaType;
static PyTypeObject PyDateTime_TimeType;
static PyTypeObject PyDateTime_TZInfoType;
static PyTypeObject PyDateTime_TimeZoneType;
static int check_tzinfo_subclass(PyObject *p);
_Py_IDENTIFIER(as_integer_ratio);
_Py_IDENTIFIER(fromutc);
_Py_IDENTIFIER(isoformat);
_Py_IDENTIFIER(strftime);
/* ---------------------------------------------------------------------------
* 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) \
((((RESULT) ^ (I)) & ((RESULT) ^ (J))) < 0)
/* 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)
{
int quo;
assert(y > 0);
quo = x / y;
*r = x - quo * y;
if (*r < 0) {
--quo;
*r += y;
}
assert(0 <= *r && *r < y);
return quo;
}
/* 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;
temp = _PyLong_DivmodNear(m, n);
if (temp == NULL)
return NULL;
result = PyTuple_GET_ITEM(temp, 0);
Py_INCREF(result);
Py_DECREF(temp);
return result;
}
/* ---------------------------------------------------------------------------
* 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.
*/
static const int _days_in_month[] = {
0, /* unused; this vector uses 1-based indexing */
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const int _days_before_month[] = {
0, /* unused; this vector uses 1-based indexing */
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
/* year -> 1 if leap year, else 0. */
static int
is_leap(int year)
{
/* 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);
}
/* year, month -> number of days in that month in that year */
static int
days_in_month(int year, int month)
{
assert(month >= 1);
assert(month <= 12);
if (month == 2 && is_leap(year))
return 29;
else
return _days_in_month[month];
}
/* year, month -> number of days in year preceding first day of month */
static int
days_before_month(int year, int month)
{
int days;
assert(month >= 1);
assert(month <= 12);
days = _days_before_month[month];
if (month > 2 && is_leap(year))
++days;
return days;
}
/* 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)
{
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
* can see is 1.
*/
assert (year >= 1);
return y*365 + y/4 - y/100 + y/400;
}
/* Number of days in 4, 100, and 400 year cycles. That these have
* the correct values is asserted in the module init function.
*/
#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 */
/* ordinal -> year, month, day, considering 01-Jan-0001 as day 1. */
static void
ord_to_ymd(int ordinal, int *year, int *month, int *day)
{
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;
}
/* year, month, day -> ordinal, considering 01-Jan-0001 as day 1. */
static int
ymd_to_ord(int year, int month, int day)
{
return days_before_year(year) + days_before_month(year, month) + day;
}
/* Day of week, where Monday==0, ..., Sunday==6. 1/1/1 was a Monday. */
static int
weekday(int year, int month, int day)
{
return (ymd_to_ord(year, month, day) + 6) % 7;
}
/* 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)
{
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;
if (first_weekday > 3) /* if 1/1 was Fri, Sat, Sun */
week1_monday += 7;
return week1_monday;
}
/* ---------------------------------------------------------------------------
* 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)
{
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;
}
/* 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)
{
if (year < MINYEAR || year > MAXYEAR) {
PyErr_Format(PyExc_ValueError, "year %i is out of range", year);
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;
}
/* Check that time arguments are in range. Return 0 if they are. If they
* aren't, raise ValueError and return -1.
*/
static int
check_time_args(int h, int m, int s, int us, int fold)
{
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;
}
if (fold != 0 && fold != 1) {
PyErr_SetString(PyExc_ValueError,
"fold must be either 0 or 1");
return -1;
}
return 0;
}
/* ---------------------------------------------------------------------------
* 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)
{
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);
}
/* Fiddle days (d), seconds (s), and microseconds (us) so that
* 0 <= *s < 24*3600
* 0 <= *us < 1000000
* 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)
{
if (*us < 0 || *us >= 1000000) {
normalize_pair(s, us, 1000000);
/* |s| can't be bigger than about
* |original s| + |original us|/1000000 now.
*/
}
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);
}
/* Fiddle years (y), months (m), and days (d) so that
* 1 <= *m <= 12
* 1 <= *d <= days_in_month(*y, *m)
* The input values must be such that the internals don't overflow.
* The way this routine is used, we don't get close.
*/
static int
normalize_y_m_d(int *y, int *m, int *d)
{
int dim; /* # of days in month */
/* In actual use, m is always the month component extracted from a
* date/datetime object. Therefore it is always in [1, 12] range.
*/
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;
if (ordinal < 1 || ordinal > MAXORDINAL) {
goto error;
} else {
ord_to_ymd(ordinal, y, m, d);
return 0;
}
}
}
assert(*m > 0);
assert(*d > 0);
if (MINYEAR <= *y && *y <= MAXYEAR)
return 0;
error:
PyErr_SetString(PyExc_OverflowError,
"date value out of range");
return -1;
}
/* 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)
{
return normalize_y_m_d(year, month, day);
}
/* 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)
{
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);
}
/* ---------------------------------------------------------------------------
* 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
* 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).
*/
static PyObject *
time_alloc(PyTypeObject *type, Py_ssize_t aware)
{
PyObject *self;
self = (PyObject *)
PyObject_MALLOC(aware ?
sizeof(PyDateTime_Time) :
sizeof(_PyDateTime_BaseTime));
if (self == NULL)
return (PyObject *)PyErr_NoMemory();
(void)PyObject_INIT(self, type);
return self;
}
static PyObject *
datetime_alloc(PyTypeObject *type, Py_ssize_t aware)
{
PyObject *self;
self = (PyObject *)
PyObject_MALLOC(aware ?
sizeof(PyDateTime_DateTime) :
sizeof(_PyDateTime_BaseDateTime));
if (self == NULL)
return (PyObject *)PyErr_NoMemory();
(void)PyObject_INIT(self, type);
return self;
}
/* ---------------------------------------------------------------------------
* 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)
{
self->hashcode = -1;
SET_YEAR(self, y);
SET_MONTH(self, m);
SET_DAY(self, d);
}
/* ---------------------------------------------------------------------------
* String parsing utilities and helper functions
*/
static const char *
parse_digits(const char *ptr, int *var, size_t num_digits)
{
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;
}
static int
parse_isoformat_date(const char *dtstr, int *year, int *month, int *day)
{
/* Parse the date components of the result of date.isoformat()
*
* Return codes:
* 0: Success
* -1: Failed to parse date component
* -2: Failed to parse dateseparator
*/
const char *p = dtstr;
p = parse_digits(p, year, 4);
if (NULL == p) {
return -1;
}
if (*(p++) != '-') {
return -2;
}
p = parse_digits(p, month, 2);
if (NULL == p) {
return -1;
}
if (*(p++) != '-') {
return -2;
}
p = parse_digits(p, day, 2);
if (p == NULL) {
return -1;
}
return 0;
}
static int
parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
int *minute, int *second, int *microsecond)
{
const char *p = tstr;
const char *p_end = tstr_end;
int *vals[3] = {hour, minute, second};
// Parse [HH[:MM[:SS]]]
for (size_t i = 0; i < 3; ++i) {
p = parse_digits(p, vals[i], 2);
if (NULL == p) {
return -3;
}
char c = *(p++);
if (p >= p_end) {
return c != '\0';
}
else if (c == ':') {
continue;
}
else if (c == '.') {
break;
}
else {
return -4; // Malformed time separator
}
}
// Parse .fff[fff]
size_t len_remains = p_end - p;
if (!(len_remains == 6 || len_remains == 3)) {
return -3;
}
p = parse_digits(p, microsecond, len_remains);
if (NULL == p) {
return -3;
}
if (len_remains == 3) {
*microsecond *= 1000;
}
// Return 1 if it's not the end of the string
return *p != '\0';
}
static int
parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
int *second, int *microsecond, int *tzoffset,
int *tzmicrosecond)
{
// 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 {
if (*tzinfo_pos == '+' || *tzinfo_pos == '-') {
break;
}
} while (++tzinfo_pos < p_end);
int rv = parse_hh_mm_ss_ff(dtstr, tzinfo_pos, hour, minute, second,
microsecond);
if (rv < 0) {
return rv;
}
else if (tzinfo_pos == p_end) {
// 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;
}
else {
return 0;
}
}
// Parse time zone component
// Valid formats are:
// - +HH:MM (len 6)
// - +HH:MM:SS (len 9)
// - +HH:MM:SS.ffffff (len 16)
size_t tzlen = p_end - tzinfo_pos;
if (!(tzlen == 6 || tzlen == 9 || tzlen == 16)) {
return -5;
}
int tzsign = (*tzinfo_pos == '-') ? -1 : 1;
tzinfo_pos++;
int tzhour = 0, tzminute = 0, tzsecond = 0;
rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,
tzmicrosecond);
*tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond);
*tzmicrosecond *= tzsign;
return rv ? -5 : 1;
}
/* ---------------------------------------------------------------------------
* 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)
{
PyDateTime_Date *self;
if (check_date_args(year, month, day) < 0) {
return NULL;
}
self = (PyDateTime_Date *)(type->tp_alloc(type, 0));
if (self != NULL)
set_date_fields(self, year, month, day);
return (PyObject *)self;
}
#define new_date(year, month, day) \
new_date_ex(year, month, day, &PyDateTime_DateType)
// Forward declaration
static PyObject *
new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *);
/* Create date instance with no range checking, or call subclass constructor */
static PyObject *
new_date_subclass_ex(int year, int month, int day, PyObject *cls)
{
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);
}
else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) {
result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None,
(PyTypeObject *)cls);
}
else {
result = PyObject_CallFunction(cls, "iii", year, month, day);
}
return result;
}
/* Create a datetime instance with no range checking. */
static PyObject *
new_datetime_ex2(int year, int month, int day, int hour, int minute,
int second, int usecond, PyObject *tzinfo, int fold, PyTypeObject *type)
{
PyDateTime_DateTime *self;
char aware = tzinfo != Py_None;
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;
}
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;
}
DATE_SET_FOLD(self, fold);
}
return (PyObject *)self;
}
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, \
&PyDateTime_DateTimeType)
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);
}
/* Create a time instance with no range checking. */
static PyObject *
new_time_ex2(int hour, int minute, int second, int usecond,
PyObject *tzinfo, int fold, PyTypeObject *type)
{
PyDateTime_Time *self;
char aware = tzinfo != Py_None;
if (check_time_args(hour, minute, second, usecond, fold) < 0) {
return NULL;
}
if (check_tzinfo_subclass(tzinfo) < 0) {
return NULL;
}
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;
}
TIME_SET_FOLD(self, fold);
}
return (PyObject *)self;
}
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)
/* 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
* of range).
*/
static PyObject *
new_delta_ex(int days, int seconds, int microseconds, int normalize,
PyTypeObject *type)
{
PyDateTime_Delta *self;
if (normalize)
normalize_d_s_us(&days, &seconds, &microseconds);
assert(0 <= seconds && seconds < 24*3600);
assert(0 <= microseconds && microseconds < 1000000);
if (check_delta_day_range(days) < 0)
return NULL;
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;
}
#define new_delta(d, s, us, normalize) \
new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType)
typedef struct
{
PyObject_HEAD
PyObject *offset;
PyObject *name;
} PyDateTime_TimeZone;
/* The interned UTC timezone instance */
static PyObject *PyDateTime_TimeZone_UTC;
/* The interned Epoch datetime instance */
static PyObject *PyDateTime_Epoch;
/* 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 *
create_timezone(PyObject *offset, PyObject *name)
{
PyDateTime_TimeZone *self;
PyTypeObject *type = &PyDateTime_TimeZoneType;
assert(offset != NULL);
assert(PyDelta_Check(offset));
assert(name == NULL || PyUnicode_Check(name));
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;
}
if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
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"
" timedelta(hours=24),"
" not %R.", offset);
return NULL;
}
return create_timezone(offset, name);
}
/* ---------------------------------------------------------------------------
* tzinfo helpers.
*/
/* 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)
{
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;
}
/* 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)
{
PyObject *tzinfo = NULL;
if (PyDateTime_Check(self) && HASTZINFO(self))
tzinfo = ((PyDateTime_DateTime *)self)->tzinfo;
else if (PyTime_Check(self) && HASTZINFO(self))
tzinfo = ((PyDateTime_Time *)self)->tzinfo;
return tzinfo;
}
/* 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.
*/
static PyObject *
call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg)
{
PyObject *offset;
assert(tzinfo != NULL);
assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None);
assert(tzinfoarg != NULL);
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)) {
if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
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;
}
}
else {
PyErr_Format(PyExc_TypeError,
"tzinfo.%s() must return None or "
"timedelta, not '%.200s'",
name, Py_TYPE(offset)->tp_name);
Py_DECREF(offset);
return NULL;
}
return offset;
}
/* 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()
* doesn't return None or timedelta, TypeError is raised and this returns -1.
* 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).
*/
static PyObject *
call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg)
{
return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg);
}
/* 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()
* doesn't return None or timedelta, TypeError is raised and this
* returns -1. If dst() returns an invalid timedelta for a UTC offset,
* ValueError is raised and this returns -1. Else *none is set to 0 and
* the offset is returned (as timedelta, positive east of UTC).
*/
static PyObject *
call_dst(PyObject *tzinfo, PyObject *tzinfoarg)
{
return call_tzinfo_method(tzinfo, "dst", tzinfoarg);
}
/* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be
* an instance of the tzinfo class or None. If tzinfo isn't None, and
* tzname() doesn't return None or a string, TypeError is raised and this
* returns NULL. If the result is a string, we ensure it is a Unicode
* string.
*/
static PyObject *
call_tzname(PyObject *tzinfo, PyObject *tzinfoarg)
{
PyObject *result;
_Py_IDENTIFIER(tzname);
assert(tzinfo != NULL);
assert(check_tzinfo_subclass(tzinfo) >= 0);
assert(tzinfoarg != NULL);
if (tzinfo == Py_None)
Py_RETURN_NONE;
result = _PyObject_CallMethodIdObjArgs(tzinfo, &PyId_tzname,
tzinfoarg, NULL);
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;
}
return result;
}
/* 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)
{
PyObject *temp;
assert(PyUnicode_Check(repr));
assert(tzinfo);
if (tzinfo == Py_None)
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, tzinfo=%R)", temp, tzinfo);
Py_DECREF(temp);
return repr;
}
/* 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;
}
static inline PyObject *
tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
{
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);
if (delta == NULL) {
return NULL;
}
tzinfo = new_timezone(delta, NULL);
Py_DECREF(delta);
}
else {
tzinfo = Py_None;
Py_INCREF(Py_None);
}
return tzinfo;
}
/* ---------------------------------------------------------------------------
* String format helpers.
*/
static PyObject *
format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds)
{
static const char * const DayNames[] = {
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
};
static const char * const MonthNames[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
int wday = weekday(GET_YEAR(date), GET_MONTH(date), GET_DAY(date));
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));
}
static PyObject *delta_negative(PyDateTime_Delta *self);
/* Add formatted UTC offset string to buf. buf has no more than
* 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
* sign HH sep MM [sep SS [. UUUUUU]]
* 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
format_utcoffset(char *buf, size_t buflen, const char *sep,
PyObject *tzinfo, PyObject *tzinfoarg)
{
PyObject *offset;
int hours, minutes, seconds, microseconds;
char sign;
assert(buflen >= 1);
offset = call_utcoffset(tzinfo, tzinfoarg);
if (offset == NULL)
return -1;
if (offset == Py_None) {
Py_DECREF(offset);
*buf = '\0';
return 0;
}
/* Offset is normalized, so it is negative if days < 0 */
if (GET_TD_DAYS(offset) < 0) {
sign = '-';
Py_SETREF(offset, delta_negative((PyDateTime_Delta *)offset));
if (offset == NULL)
return -1;
}
else {
sign = '+';
}
/* Offset is not negative here. */
microseconds = GET_TD_MICROSECONDS(offset);
seconds = GET_TD_SECONDS(offset);
Py_DECREF(offset);
minutes = divmod(seconds, 60, &seconds);
hours = divmod(minutes, 60, &minutes);
if (microseconds) {
PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d.%06d", sign,
hours, sep, minutes, sep, seconds, microseconds);
return 0;
}
if (seconds) {
PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d", sign, hours,
sep, minutes, sep, seconds);
return 0;
}
PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes);
return 0;
}
static PyObject *
make_Zreplacement(PyObject *object, PyObject *tzinfoarg)
{
PyObject *temp;
PyObject *tzinfo = get_tzinfo_member(object);
PyObject *Zreplacement = PyUnicode_FromStringAndSize(NULL, 0);
_Py_IDENTIFIER(replace);
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);
Zreplacement = _PyObject_CallMethodId(temp, &PyId_replace, "ss", "%", "%%");
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;
Error:
Py_DECREF(Zreplacement);
return NULL;
}
static PyObject *
make_freplacement(PyObject *object)
{
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);
return PyBytes_FromStringAndSize(freplacement, strlen(freplacement));
}
/* 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
* giving special meanings to the %z, %Z and %f format codes via a
* preprocessing step on the format string.
* tzinfoarg is the argument to pass to the object's tzinfo method, if
* needed.
*/
static PyObject *
wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
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 */
pin = PyUnicode_AsUTF8AndSize(format, &flen);
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') {
/* Null byte follows %, copy only '%'.
*
* 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;
}
/* 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));
ptoappend = PyUnicode_AsUTF8AndSize(Zreplacement,
&ntoappend);
if (ptoappend == NULL)
goto Done;
}
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) {
if (totalnew > (PY_SSIZE_T_MAX >> 1)) { /* overflow */
PyErr_NoMemory();
goto Done;
}
totalnew <<= 1;
if (_PyBytes_Resize(&newfmt, totalnew) < 0)
goto Done;
pnew = PyBytes_AsString(newfmt) + usednew;
}
memcpy(pnew, ptoappend, ntoappend);
pnew += ntoappend;
usednew += ntoappend;
assert(usednew <= totalnew);
} /* end while() */
if (_PyBytes_Resize(&newfmt, usednew) < 0)
goto Done;
{
PyObject *format;
PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time == NULL)
goto Done;
format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
if (format != NULL) {
result = _PyObject_CallMethodIdObjArgs(time, &PyId_strftime,
format, timetuple, NULL);
Py_DECREF(format);
}
Py_DECREF(time);
}
Done:
Py_XDECREF(freplacement);
Py_XDECREF(zreplacement);
Py_XDECREF(Zreplacement);
Py_XDECREF(newfmt);
return result;
}
/* ---------------------------------------------------------------------------
* 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 *
time_time(void)
{
PyObject *result = NULL;
PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time != NULL) {
_Py_IDENTIFIER(time);
result = _PyObject_CallMethodId(time, &PyId_time, NULL);
Py_DECREF(time);
}
return result;
}
/* 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)
{
PyObject *time;
PyObject *result;
_Py_IDENTIFIER(struct_time);
PyObject *args;
time = PyImport_ImportModuleNoBlock("time");
if (time == NULL) {
return NULL;
}
args = Py_BuildValue("iiiiiiiii",
y, m, d,
hh, mm, ss,
weekday(y, m, d),
days_before_month(y, m) + d,
dstflag);
if (args == NULL) {
Py_DECREF(time);
return NULL;
}
result = _PyObject_CallMethodIdObjArgs(time, &PyId_struct_time,
args, NULL);
Py_DECREF(time);
Py_DECREF(args);
return result;
}
/* ---------------------------------------------------------------------------
* Miscellaneous helpers.
*/
/* For various reasons, we need to use tp_richcompare instead of tp_reserved.
* The comparisons here all most naturally compute a cmp()-like result.
* This little helper turns that into a bool result for rich comparisons.
*/
static PyObject *
diff_to_bool(int diff, int op)
{
Py_RETURN_RICHCOMPARE(diff, 0, op);
}
/* Raises a "can't compare" TypeError and returns NULL. */
static PyObject *
cmperror(PyObject *a, PyObject *b)
{
PyErr_Format(PyExc_TypeError,
"can't compare %s to %s",
Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name);
return NULL;
}
/* ---------------------------------------------------------------------------
* Cached Python objects; these are set by the module init function.
*/
/* Conversion factors. */
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 */
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 */
static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */
/* ---------------------------------------------------------------------------
* Class implementations.
*/
/*
* PyDateTime_Delta implementation.
*/
/* Convert a timedelta to a number of us,
* (24*3600*self.days + self.seconds)*1000000 + self.microseconds
* as a Python int.
* Doing mixed-radix arithmetic by hand instead is excruciating in C,
* due to ubiquitous overflow possibilities.
*/
static PyObject *
delta_to_microseconds(PyDateTime_Delta *self)
{
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);
/* x1 = */ x2 = NULL;
/* 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);
assert(result == NULL || PyLong_CheckExact(result));
Done:
Py_XDECREF(x1);
Py_XDECREF(x2);
Py_XDECREF(x3);
return result;
}
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)",
result->ob_type->tp_name);
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;
}
/* Convert a number of us (as a Python int) to a timedelta.
*/
static PyObject *
microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
{
int us;
int s;
int d;
PyObject *tuple = NULL;
PyObject *num = NULL;
PyObject *result = NULL;
tuple = checked_divmod(pyus, us_per_second);
if (tuple == NULL) {
goto Done;
}
num = PyTuple_GET_ITEM(tuple, 1); /* us */
us = _PyLong_AsInt(num);
num = NULL;
if (us == -1 && PyErr_Occurred()) {
goto Done;
}
if (!(0 <= us && us < 1000000)) {
goto BadDivmod;
}
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
Py_INCREF(num);
Py_DECREF(tuple);
tuple = checked_divmod(num, seconds_per_day);
if (tuple == NULL)
goto Done;
Py_DECREF(num);
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
s = _PyLong_AsInt(num);
num = NULL;
if (s == -1 && PyErr_Occurred()) {
goto Done;
}
if (!(0 <= s && s < 24*3600)) {
goto BadDivmod;
}
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
Py_INCREF(num);
d = _PyLong_AsInt(num);
if (d == -1 && PyErr_Occurred()) {
goto Done;
}
result = new_delta_ex(d, s, us, 0, type);
Done:
Py_XDECREF(tuple);
Py_XDECREF(num);
return result;
BadDivmod:
PyErr_SetString(PyExc_TypeError,
"divmod() returned a value out of range");
goto Done;
}
#define microseconds_to_delta(pymicros) \
microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType)
static PyObject *
multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
{
PyObject *pyus_in;
PyObject *pyus_out;
PyObject *result;
pyus_in = delta_to_microseconds(delta);
if (pyus_in == NULL)
return NULL;
pyus_out = PyNumber_Multiply(intobj, pyus_in);
Py_DECREF(pyus_in);
if (pyus_out == NULL)
return NULL;
result = microseconds_to_delta(pyus_out);
Py_DECREF(pyus_out);
return result;
}
static PyObject *
get_float_as_integer_ratio(PyObject *floatobj)
{
PyObject *ratio;
assert(floatobj && PyFloat_Check(floatobj));
ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
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;
}
/* op is 0 for multiplication, 1 for division */
static PyObject *
multiply_truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *floatobj, int op)
{
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;
ratio = get_float_as_integer_ratio(floatobj);
if (ratio == NULL) {
goto error;
}
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, op));
Py_DECREF(pyus_in);
pyus_in = NULL;
if (temp == NULL)
goto error;
pyus_out = divide_nearest(temp, PyTuple_GET_ITEM(ratio, !op));
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;
}
static PyObject *
divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj)
{
PyObject *pyus_in;
PyObject *pyus_out;
PyObject *result;
pyus_in = delta_to_microseconds(delta);
if (pyus_in == NULL)
return NULL;
pyus_out = PyNumber_FloorDivide(pyus_in, intobj);
Py_DECREF(pyus_in);
if (pyus_out == NULL)
return NULL;
result = microseconds_to_delta(pyus_out);
Py_DECREF(pyus_out);
return result;
}
static PyObject *
divide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
{
PyObject *pyus_left;
PyObject *pyus_right;
PyObject *result;
pyus_left = delta_to_microseconds(left);
if (pyus_left == NULL)
return NULL;
pyus_right = delta_to_microseconds(right);
if (pyus_right == NULL) {
Py_DECREF(pyus_left);
return NULL;
}
result = PyNumber_FloorDivide(pyus_left, pyus_right);
Py_DECREF(pyus_left);
Py_DECREF(pyus_right);
return result;
}
static PyObject *
truedivide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
{
PyObject *pyus_left;
PyObject *pyus_right;
PyObject *result;
pyus_left = delta_to_microseconds(left);
if (pyus_left == NULL)
return NULL;
pyus_right = delta_to_microseconds(right);
if (pyus_right == NULL) {
Py_DECREF(pyus_left);
return NULL;
}
result = PyNumber_TrueDivide(pyus_left, pyus_right);
Py_DECREF(pyus_left);
Py_DECREF(pyus_right);
return result;
}
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;
}
static PyObject *
delta_add(PyObject *left, PyObject *right)
{
PyObject *result = Py_NotImplemented;
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);
}
if (result == Py_NotImplemented)
Py_INCREF(result);
return result;
}
static PyObject *
delta_negative(PyDateTime_Delta *self)
{
return new_delta(-GET_TD_DAYS(self),
-GET_TD_SECONDS(self),
-GET_TD_MICROSECONDS(self),
1);
}
static PyObject *
delta_positive(PyDateTime_Delta *self)
{
/* 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);
}
static PyObject *
delta_abs(PyDateTime_Delta *self)
{
PyObject *result;
assert(GET_TD_MICROSECONDS(self) >= 0);
assert(GET_TD_SECONDS(self) >= 0);
if (GET_TD_DAYS(self) < 0)
result = delta_negative(self);
else
result = delta_positive(self);
return result;
}
static PyObject *
delta_subtract(PyObject *left, PyObject *right)
{
PyObject *result = Py_NotImplemented;
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);
}
if (result == Py_NotImplemented)
Py_INCREF(result);
return result;
}
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;
}
static PyObject *
delta_richcompare(PyObject *self, PyObject *other, int op)
{
if (PyDelta_Check(other)) {
int diff = delta_cmp(self, other);
return diff_to_bool(diff, op);
}
else {
Py_RETURN_NOTIMPLEMENTED;
}
}
static PyObject *delta_getstate(PyDateTime_Delta *self);
static Py_hash_t
delta_hash(PyDateTime_Delta *self)
{
if (self->hashcode == -1) {
PyObject *temp = delta_getstate(self);
if (temp != NULL) {
self->hashcode = PyObject_Hash(temp);
Py_DECREF(temp);
}
}
return self->hashcode;
}
static PyObject *
delta_multiply(PyObject *left, PyObject *right)
{
PyObject *result = Py_NotImplemented;
if (PyDelta_Check(left)) {
/* delta * ??? */
if (PyLong_Check(right))
result = multiply_int_timedelta(right,
(PyDateTime_Delta *) left);
else if (PyFloat_Check(right))
result = multiply_truedivide_timedelta_float(
(PyDateTime_Delta *) left, right, 0);
}
else if (PyLong_Check(left))
result = multiply_int_timedelta(left,
(PyDateTime_Delta *) right);
else if (PyFloat_Check(left))
result = multiply_truedivide_timedelta_float(
(PyDateTime_Delta *) right, left, 0);
if (result == Py_NotImplemented)
Py_INCREF(result);
return result;
}
static PyObject *
delta_divide(PyObject *left, PyObject *right)
{
PyObject *result = Py_NotImplemented;
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);
}
if (result == Py_NotImplemented)
Py_INCREF(result);
return result;
}
static PyObject *
delta_truedivide(PyObject *left, PyObject *right)
{
PyObject *result = Py_NotImplemented;
if (PyDelta_Check(left)) {
if (PyDelta_Check(right))
result = truedivide_timedelta_timedelta(
(PyDateTime_Delta *)left,
(PyDateTime_Delta *)right);
else if (PyFloat_Check(right))
result = multiply_truedivide_timedelta_float(
(PyDateTime_Delta *)left, right, 1);
else if (PyLong_Check(right))
result = truedivide_timedelta_int(
(PyDateTime_Delta *)left, right);
}
if (result == Py_NotImplemented)
Py_INCREF(result);
return result;
}
static PyObject *
delta_remainder(PyObject *left, PyObject *right)
{
PyObject *pyus_left;
PyObject *pyus_right;
PyObject *pyus_remainder;
PyObject *remainder;
if (!PyDelta_Check(left) || !PyDelta_Check(right))
Py_RETURN_NOTIMPLEMENTED;
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;
}
pyus_remainder = PyNumber_Remainder(pyus_left, pyus_right);
Py_DECREF(pyus_left);
Py_DECREF(pyus_right);
if (pyus_remainder == NULL)
return NULL;
remainder = microseconds_to_delta(pyus_remainder);
Py_DECREF(pyus_remainder);
if (remainder == NULL)
return NULL;
return remainder;
}
static PyObject *
delta_divmod(PyObject *left, PyObject *right)
{
PyObject *pyus_left;
PyObject *pyus_right;
PyObject *divmod;
PyObject *delta;
PyObject *result;
if (!PyDelta_Check(left) || !PyDelta_Check(right))
Py_RETURN_NOTIMPLEMENTED;
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;
}
divmod = checked_divmod(pyus_left, pyus_right);
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;
}
/* 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)
{
PyObject *prod;
PyObject *sum;
assert(num != NULL);
if (PyLong_Check(num)) {
prod = PyNumber_Multiply(num, factor);
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.
*/
assert(PyLong_CheckExact(factor));
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;
}
static PyObject *
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
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) {
y = accum("microseconds", x, us, _PyLong_One, &leftover_us);
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. */
double whole_us = round(leftover_us);
int x_is_odd;
PyObject *temp;
whole_us = round(leftover_us);
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. */
temp = PyNumber_And(x, _PyLong_One); /* temp <- x & 1 */
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;
}
temp = PyLong_FromLong((long)whole_us);
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);
Done:
return self;
#undef CLEANUP
}
static int
delta_bool(PyDateTime_Delta *self)
{
return (GET_TD_DAYS(self) != 0
|| GET_TD_SECONDS(self) != 0
|| GET_TD_MICROSECONDS(self) != 0);
}
static PyObject *
delta_repr(PyDateTime_Delta *self)
{
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;
}
static PyObject *
delta_str(PyDateTime_Delta *self)
{
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);
}
}
/* Pickle support, a simple use of __reduce__. */
/* __getstate__ isn't exposed */
static PyObject *
delta_getstate(PyDateTime_Delta *self)
{
return Py_BuildValue("iii", GET_TD_DAYS(self),
GET_TD_SECONDS(self),
GET_TD_MICROSECONDS(self));
}
static PyObject *
delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *total_seconds;
PyObject *total_microseconds;
total_microseconds = delta_to_microseconds((PyDateTime_Delta *)self);
if (total_microseconds == NULL)
return NULL;
total_seconds = PyNumber_TrueDivide(total_microseconds, us_per_second);
Py_DECREF(total_microseconds);
return total_seconds;
}
static PyObject *
delta_reduce(PyDateTime_Delta* self, PyObject *Py_UNUSED(ignored))
{
return Py_BuildValue("ON", Py_TYPE(self), delta_getstate(self));
}
#define OFFSET(field) offsetof(PyDateTime_Delta, field)
static PyMemberDef delta_members[] = {
{"days", T_INT, OFFSET(days), READONLY,
PyDoc_STR("Number of days.")},
{"seconds", T_INT, OFFSET(seconds), READONLY,
PyDoc_STR("Number of seconds (>= 0 and less than 1 day).")},
{"microseconds", T_INT, OFFSET(microseconds), READONLY,
PyDoc_STR("Number of microseconds (>= 0 and less than 1 second).")},
{NULL}
};
static PyMethodDef delta_methods[] = {
{"total_seconds", delta_total_seconds, METH_NOARGS,
PyDoc_STR("Total seconds in the duration.")},
{"__reduce__", (PyCFunction)delta_reduce, METH_NOARGS,
PyDoc_STR("__reduce__() -> (cls, state)")},
{NULL, NULL},
};
static const char delta_doc[] =
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.");
static PyNumberMethods delta_as_number = {
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 */
};
static PyTypeObject PyDateTime_DeltaType = {
PyVarObject_HEAD_INIT(NULL, 0)
"datetime.timedelta", /* tp_name */
sizeof(PyDateTime_Delta), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(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 */
};
/*
* PyDateTime_Date implementation.
*/
/* Accessor properties. */
static PyObject *
date_year(PyDateTime_Date *self, void *unused)
{
return PyLong_FromLong(GET_YEAR(self));
}
static PyObject *
date_month(PyDateTime_Date *self, void *unused)
{
return PyLong_FromLong(GET_MONTH(self));
}
static PyObject *
date_day(PyDateTime_Date *self, void *unused)
{
return PyLong_FromLong(GET_DAY(self));
}
static PyGetSetDef date_getset[] = {
{"year", (getter)date_year},
{"month", (getter)date_month},
{"day", (getter)date_day},
{NULL}
};
/* Constructors. */
static char *date_kws[] = {"year", "month", "day", NULL};
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;
}
static PyObject *
date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *self = NULL;
int year;
int month;
int day;
/* Check for invocation from pickle with __getstate__ state */
if (PyTuple_GET_SIZE(args) == 1) {
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;
}
}
}
if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws,
&year, &month, &day)) {
self = new_date_ex(year, month, day, type);
}
return self;
}
static PyObject *
date_fromtimestamp(PyObject *cls, PyObject *obj)
{
struct tm tm;
time_t t;
if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_FLOOR) == -1)
return NULL;
if (_PyTime_localtime(t, &tm) != 0)
return NULL;
return new_date_subclass_ex(tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
cls);
}
/* 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)
{
PyObject *time;
PyObject *result;
_Py_IDENTIFIER(fromtimestamp);
time = time_time();
if (time == NULL)
return NULL;
/* 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().
*/
result = _PyObject_CallMethodIdObjArgs(cls, &PyId_fromtimestamp,
time, NULL);
Py_DECREF(time);
return result;
}
/*[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]*/
static PyObject *
datetime_date_fromtimestamp(PyTypeObject *type, PyObject *timestamp)
/*[clinic end generated code: output=fd045fda58168869 input=eabb3fe7f40491fe]*/
{
return date_fromtimestamp((PyObject *) type, timestamp);
}
/* Return new date from proleptic Gregorian ordinal. Raises ValueError if
* the ordinal is out of range.
*/
static PyObject *
date_fromordinal(PyObject *cls, PyObject *args)
{
PyObject *result = NULL;
int ordinal;
if (PyArg_ParseTuple(args, "i:fromordinal", &ordinal)) {
int year;
int month;
int day;
if (ordinal < 1)
PyErr_SetString(PyExc_ValueError, "ordinal must be "
">= 1");
else {
ord_to_ymd(ordinal, &year, &month, &day);
result = new_date_subclass_ex(year, month, day, cls);
}
}
return result;
}
/* Return the new date from a string as generated by date.isoformat() */
static PyObject *
date_fromisoformat(PyObject *cls, PyObject *dtstr)
{
assert(dtstr != NULL);
if (!PyUnicode_Check(dtstr)) {
PyErr_SetString(PyExc_TypeError,
"fromisoformat: argument must be str");
return NULL;
}
Py_ssize_t len;
const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
if (dt_ptr == NULL) {
goto invalid_string_error;
}
int year = 0, month = 0, day = 0;
int rv;
if (len == 10) {
rv = parse_isoformat_date(dt_ptr, &year, &month, &day);
}
else {
rv = -1;
}
if (rv < 0) {
goto invalid_string_error;
}
return new_date_subclass_ex(year, month, day, cls);
invalid_string_error:
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
return NULL;
}
/*
* 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)
{
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);
if (normalize_date(&year, &month, &day) >= 0)
result = new_date(year, month, day);
return result;
}
static PyObject *
date_add(PyObject *left, PyObject *right)
{
if (PyDateTime_Check(left) || PyDateTime_Check(right))
Py_RETURN_NOTIMPLEMENTED;
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);
}
Py_RETURN_NOTIMPLEMENTED;
}
static PyObject *
date_subtract(PyObject *left, PyObject *right)
{
if (PyDateTime_Check(left) || PyDateTime_Check(right))
Py_RETURN_NOTIMPLEMENTED;
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);
}
}
Py_RETURN_NOTIMPLEMENTED;
}
/* Various ways to turn a date into a string. */
static PyObject *
date_repr(PyDateTime_Date *self)
{
return PyUnicode_FromFormat("%s(%d, %d, %d)",
Py_TYPE(self)->tp_name,
GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
}
static PyObject *
date_isoformat(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
return PyUnicode_FromFormat("%04d-%02d-%02d",
GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
}
/* str() calls the appropriate isoformat() method. */
static PyObject *
date_str(PyDateTime_Date *self)
{
return _PyObject_CallMethodId((PyObject *)self, &PyId_isoformat, NULL);
}
static PyObject *
date_ctime(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
return format_ctime(self, 0, 0, 0);
}
static PyObject *
date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw)
{
/* This method can be inherited, and needs to call the
* timetuple() method appropriate to self's class.
*/
PyObject *result;
PyObject *tuple;
PyObject *format;
_Py_IDENTIFIER(timetuple);
static char *keywords[] = {"format", NULL};
if (! PyArg_ParseTupleAndKeywords(args, kw, "U:strftime", keywords,
&format))
return NULL;
tuple = _PyObject_CallMethodId((PyObject *)self, &PyId_timetuple, NULL);
if (tuple == NULL)
return NULL;
result = wrap_strftime((PyObject *)self, format, tuple,
(PyObject *)self);
Py_DECREF(tuple);
return result;
}
static PyObject *
date_format(PyDateTime_Date *self, PyObject *args)
{
PyObject *format;
if (!PyArg_ParseTuple(args, "U:__format__", &format))
return NULL;
/* if the format is zero length, return str(self) */
if (PyUnicode_GetLength(format) == 0)
return PyObject_Str((PyObject *)self);
return _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId_strftime,
format, NULL);
}
/* ISO methods. */
static PyObject *
date_isoweekday(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
return PyLong_FromLong(dow + 1);
}
static PyObject *
date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
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;
}
return Py_BuildValue("iii", year, week + 1, day + 1);
}
/* Miscellaneous methods. */
static PyObject *
date_richcompare(PyObject *self, PyObject *other, int op)
{
if (PyDate_Check(other)) {
int diff = memcmp(((PyDateTime_Date *)self)->data,
((PyDateTime_Date *)other)->data,
_PyDateTime_DATE_DATASIZE);
return diff_to_bool(diff, op);
}
else
Py_RETURN_NOTIMPLEMENTED;
}
static PyObject *
date_timetuple(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
return build_struct_time(GET_YEAR(self),
GET_MONTH(self),
GET_DAY(self),
0, 0, 0, -1);
}
static PyObject *
date_replace(PyDateTime_Date *self, PyObject *args, PyObject *kw)
{
PyObject *clone;
PyObject *tuple;
int year = GET_YEAR(self);
int month = GET_MONTH(self);
int day = GET_DAY(self);
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;
}
static Py_hash_t
generic_hash(unsigned char *data, int len)
{
return _Py_HashBytes(data, len);
}
static PyObject *date_getstate(PyDateTime_Date *self);
static Py_hash_t
date_hash(PyDateTime_Date *self)
{
if (self->hashcode == -1) {
self->hashcode = generic_hash(
(unsigned char *)self->data, _PyDateTime_DATE_DATASIZE);
}
return self->hashcode;
}
static PyObject *
date_toordinal(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
return PyLong_FromLong(ymd_to_ord(GET_YEAR(self), GET_MONTH(self),
GET_DAY(self)));
}
static PyObject *
date_weekday(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
{
int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
return PyLong_FromLong(dow);
}
/* Pickle support, a simple use of __reduce__. */
/* __getstate__ isn't exposed */
static PyObject *
date_getstate(PyDateTime_Date *self)
{
PyObject* field;
field = PyBytes_FromStringAndSize((char*)self->data,
_PyDateTime_DATE_DATASIZE);
return Py_BuildValue("(N)", field);
}
static PyObject *
date_reduce(PyDateTime_Date *self, PyObject *arg)
{
return Py_BuildValue("(ON)", Py_TYPE(self), date_getstate(self));
}
static PyMethodDef date_methods[] = {
/* Class methods: */
DATETIME_DATE_FROMTIMESTAMP_METHODDEF
{"fromordinal", (PyCFunction)date_fromordinal, METH_VARARGS |
METH_CLASS,
PyDoc_STR("int -> date corresponding to a proleptic Gregorian "
"ordinal.")},
{"fromisoformat", (PyCFunction)date_fromisoformat, METH_O |
METH_CLASS,
PyDoc_STR("str -> Construct a date from the output of date.isoformat()")},
{"today", (PyCFunction)date_today, METH_NOARGS | METH_CLASS,
PyDoc_STR("Current date or datetime: same as "
"self.__class__.fromtimestamp(time.time()).")},
/* Instance methods: */
{"ctime", (PyCFunction)date_ctime, METH_NOARGS,
PyDoc_STR("Return ctime() style string.")},
{"strftime", (PyCFunction)(void(*)(void))date_strftime, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("format -> strftime() style string.")},
{"__format__", (PyCFunction)date_format, METH_VARARGS,
PyDoc_STR("Formats self with strftime.")},
{"timetuple", (PyCFunction)date_timetuple, METH_NOARGS,
PyDoc_STR("Return time tuple, compatible with time.localtime().")},
{"isocalendar", (PyCFunction)date_isocalendar, METH_NOARGS,
PyDoc_STR("Return a 3-tuple containing ISO year, week number, and "
"weekday.")},
{"isoformat", (PyCFunction)date_isoformat, METH_NOARGS,
PyDoc_STR("Return string in ISO 8601 format, YYYY-MM-DD.")},
{"isoweekday", (PyCFunction)date_isoweekday, METH_NOARGS,
PyDoc_STR("Return the day of the week represented by the date.\n"
"Monday == 1 ... Sunday == 7")},
{"toordinal", (PyCFunction)date_toordinal, METH_NOARGS,
PyDoc_STR("Return proleptic Gregorian ordinal. January 1 of year "
"1 is day 1.")},
{"weekday", (PyCFunction)date_weekday, METH_NOARGS,
PyDoc_STR("Return the day of the week represented by the date.\n"
"Monday == 0 ... Sunday == 6")},
{"replace", (PyCFunction)(void(*)(void))date_replace, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("Return date with new specified fields.")},
{"__reduce__", (PyCFunction)date_reduce, METH_NOARGS,
PyDoc_STR("__reduce__() -> (cls, state)")},
{NULL, NULL}
};
static const char date_doc[] =
PyDoc_STR("date(year, month, day) --> date object");
static PyNumberMethods date_as_number = {
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 */
};
static PyTypeObject PyDateTime_DateType = {
PyVarObject_HEAD_INIT(NULL, 0)
"datetime.date", /* tp_name */
sizeof(PyDateTime_Date), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(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 */
};
/*
* PyDateTime_TZInfo implementation.
*/
/* 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.
*/
static PyObject *
tzinfo_nogo(const char* methodname)
{
PyErr_Format(PyExc_NotImplementedError,
"a tzinfo subclass must implement %s()",
methodname);
return NULL;
}
/* Methods. A subclass must implement these. */
static PyObject *
tzinfo_tzname(PyDateTime_TZInfo *self, PyObject *dt)
{
return tzinfo_nogo("tzname");
}
static PyObject *
tzinfo_utcoffset(PyDateTime_TZInfo *self, PyObject *dt)
{
return tzinfo_nogo("utcoffset");
}
static PyObject *
tzinfo_dst(PyDateTime_TZInfo *self, PyObject *dt)
{
return tzinfo_nogo("dst");
}
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 *);
static PyObject *
tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt)
{
PyObject *result = NULL;
PyObject *off = NULL, *dst = NULL;
PyDateTime_Delta *delta = NULL;
if (!PyDateTime_Check(dt)) {
PyErr_SetString(PyExc_TypeError,
"fromutc: argument must be a datetime");
return NULL;
}
if (GET_DT_TZINFO(dt) != (PyObject *)self) {
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
"is not self");
return NULL;
}
off = datetime_utcoffset(dt, NULL);
if (off == NULL)
return NULL;
if (off == Py_None) {
PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
"utcoffset() result required");
goto Fail;
}
dst = datetime_dst(dt, NULL);
if (dst == NULL)
goto Fail;
if (dst == Py_None) {
PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
"dst() result required");
goto Fail;
}
delta = (PyDateTime_Delta *)delta_subtract(off, dst);
if (delta == NULL)
goto Fail;
result = add_datetime_timedelta((PyDateTime_DateTime *)dt, delta, 1);
if (result == NULL)
goto Fail;
Py_DECREF(dst);
dst = call_dst(GET_DT_TZINFO(dt), result);
if (dst == NULL)
goto Fail;
if (dst == Py_None)
goto Inconsistent;
if (delta_bool((PyDateTime_Delta *)dst) != 0) {
Py_SETREF(result, add_datetime_timedelta((PyDateTime_DateTime *)result,
(PyDateTime_Delta *)dst, 1));
if (result == NULL)
goto Fail;
}
Py_DECREF(delta);
Py_DECREF(dst);
Py_DECREF(off);
return result;
Inconsistent:
PyErr_SetString(PyExc_ValueError, "fromutc: tz.dst() gave "
"inconsistent results; cannot convert");
/* fall through to failure */
Fail:
Py_XDECREF(off);
Py_XDECREF(dst);
Py_XDECREF(delta);
Py_XDECREF(result);
return NULL;
}
/*
* Pickle support. This is solely so that tzinfo subclasses can use
* pickling -- tzinfo itself is supposed to be uninstantiable.
*/
static PyObject *
tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *args, *state;
PyObject *getinitargs, *getstate;
_Py_IDENTIFIER(__getinitargs__);
_Py_IDENTIFIER(__getstate__);
getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__);
if (getinitargs != NULL) {
args = _PyObject_CallNoArg(getinitargs);
Py_DECREF(getinitargs);
if (args == NULL) {
return NULL;
}
}
else {
PyErr_Clear();
args = PyTuple_New(0);
if (args == NULL) {
return NULL;
}
}
getstate = _PyObject_GetAttrId(self, &PyId___getstate__);
if (getstate != NULL) {
state = _PyObject_CallNoArg(getstate);
Py_DECREF(getstate);
if (state == NULL) {
Py_DECREF(args);
return NULL;
}
}
else {
PyObject **dictptr;
PyErr_Clear();
state = Py_None;
dictptr = _PyObject_GetDictPtr(self);
if (dictptr && *dictptr && PyDict_GET_SIZE(*dictptr)) {
state = *dictptr;
}
Py_INCREF(state);
}
if (state == Py_None) {
Py_DECREF(state);
return Py_BuildValue("(ON)", Py_TYPE(self), args);
}
else
return Py_BuildValue("(ONN)", Py_TYPE(self), args, state);
}
static PyMethodDef tzinfo_methods[] = {
{"tzname", (PyCFunction)tzinfo_tzname, METH_O,
PyDoc_STR("datetime -> string name of time zone.")},
{"utcoffset", (PyCFunction)tzinfo_utcoffset, METH_O,
PyDoc_STR("datetime -> timedelta showing offset from UTC, negative "
"values indicating West of UTC")},
{"dst", (PyCFunction)tzinfo_dst, METH_O,
PyDoc_STR("datetime -> DST offset as timedelta positive east of UTC.")},
{"fromutc", (PyCFunction)tzinfo_fromutc, METH_O,
PyDoc_STR("datetime in UTC -> datetime in local time.")},
{"__reduce__", tzinfo_reduce, METH_NOARGS,
PyDoc_STR("-> (cls, state)")},
{NULL, NULL}
};
static const char tzinfo_doc[] =
PyDoc_STR("Abstract base class for time zone info objects.");
static PyTypeObject PyDateTime_TZInfoType = {
PyVarObject_HEAD_INIT(NULL, 0)
"datetime.tzinfo", /* tp_name */
sizeof(PyDateTime_TZInfo), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
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 */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
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 */
};
static char *timezone_kws[] = {"offset", "name", NULL};
static PyObject *
timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *offset;
PyObject *name = NULL;
if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws,
&PyDateTime_DeltaType, &offset, &name))
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)
{
if (op != Py_EQ && op != Py_NE)
Py_RETURN_NOTIMPLEMENTED;
if (Py_TYPE(other) != &PyDateTime_TimeZoneType) {
if (op == Py_EQ)
Py_RETURN_FALSE;
else
Py_RETURN_TRUE;
}
return delta_richcompare(self->offset, other->offset, op);
}
static Py_hash_t
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;
}
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);
}
static PyObject *
timezone_str(PyDateTime_TimeZone *self)
{
int hours, minutes, seconds, microseconds;
PyObject *offset;
char sign;
if (self->name != NULL) {
Py_INCREF(self->name);
return self->name;
}
if ((PyObject *)self == PyDateTime_TimeZone_UTC ||
(GET_TD_DAYS(self->offset) == 0 &&
GET_TD_SECONDS(self->offset) == 0 &&
GET_TD_MICROSECONDS(self->offset) == 0))
return PyUnicode_FromString("UTC");
/* 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. */
microseconds = GET_TD_MICROSECONDS(offset);
seconds = GET_TD_SECONDS(offset);
Py_DECREF(offset);
minutes = divmod(seconds, 60, &seconds);
hours = divmod(minutes, 60, &minutes);
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);
}
return PyUnicode_FromFormat("UTC%c%02d:%02d", sign, hours, minutes);
}
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)
{
if (!PyDateTime_Check(dt)) {
PyErr_SetString(PyExc_TypeError,
"fromutc: argument must be a datetime");
return NULL;
}
if (!HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
"is not self");
return NULL;
}
return add_datetime_timedelta(dt, (PyDateTime_Delta *)self->offset, 1);
}
static PyObject *
timezone_getinitargs(PyDateTime_TimeZone *self, PyObject *Py_UNUSED(ignored))
{
if (self->name == NULL)
return Py_BuildValue("(O)", self->offset);
return Py_BuildValue("(OO)", self->offset, self->name);
}
static PyMethodDef timezone_methods[] = {
{"tzname", (PyCFunction)timezone_tzname, METH_O,
PyDoc_STR("If name is specified when timezone is created, returns the name."
" Otherwise returns offset as 'UTC(+|-)HH:MM'.")},
{"utcoffset", (PyCFunction)timezone_utcoffset, METH_O,
PyDoc_STR("Return fixed offset.")},
{"dst", (PyCFunction)timezone_dst, METH_O,
PyDoc_STR("Return None.")},
{"fromutc", (PyCFunction)timezone_fromutc, METH_O,
PyDoc_STR("datetime in UTC -> datetime in local time.")},
{"__getinitargs__", (PyCFunction)timezone_getinitargs, METH_NOARGS,
PyDoc_STR("pickle support")},
{NULL, NULL}
};
static const char timezone_doc[] =
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 */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)timezone_repr, /* tp_repr */
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 */
&PyDateTime_TZInfoType, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
timezone_new, /* tp_new */
};
/*
* PyDateTime_Time implementation.
*/
/* Accessor properties.
*/
static PyObject *
time_hour(PyDateTime_Time *self, void *unused)
{
return PyLong_FromLong(TIME_GET_HOUR(self));
}
static PyObject *
time_minute(PyDateTime_Time *self, void *unused)
{
return PyLong_FromLong(TIME_GET_MINUTE(self));
}
/* The name time_second conflicted with some platform header file. */
static PyObject *
py_time_second(PyDateTime_Time *self, void *unused)
{
return PyLong_FromLong(TIME_GET_SECOND(self));
}
static PyObject *
time_microsecond(PyDateTime_Time *self, void *unused)
{
return PyLong_FromLong(TIME_GET_MICROSECOND(self));
}
static PyObject *
time_tzinfo(PyDateTime_Time *self, void *unused)
{
PyObject *result = HASTZINFO(self) ? self->tzinfo : Py_None;
Py_INCREF(result);
return result;
}
static PyObject *
time_fold(PyDateTime_Time *self, void *unused)
{
return PyLong_FromLong(TIME_GET_FOLD(self));
}
static PyGetSetDef time_getset[] = {
{"hour", (getter)time_hour},
{"minute", (getter)time_minute},
{"second", (getter)py_time_second},
{"microsecond", (getter)time_microsecond},
{"tzinfo", (getter)time_tzinfo},
{"fold", (getter)time_fold},