Commit 73ca440e authored by Alexander Belopolsky's avatar Alexander Belopolsky

Issue #5288: Eliminated round-trips between timdelta and int offsets

parent 6ef08a0e
...@@ -88,8 +88,11 @@ ...@@ -88,8 +88,11 @@
/* p is a pointer to a time or a datetime object; HASTZINFO(p) returns /* p is a pointer to a time or a datetime object; HASTZINFO(p) returns
* p->hastzinfo. * p->hastzinfo.
*/ */
#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(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 /* M is a char or int claiming to be a valid month. The macro is equivalent
* to the two-sided Python test * to the two-sided Python test
* 1 <= M <= 12 * 1 <= M <= 12
...@@ -839,25 +842,6 @@ check_tzinfo_subclass(PyObject *p) ...@@ -839,25 +842,6 @@ check_tzinfo_subclass(PyObject *p)
return -1; return -1;
} }
/* Return tzinfo.methname(tzinfoarg), without any checking of results.
* If tzinfo is None, returns None.
*/
static PyObject *
call_tzinfo_method(PyObject *tzinfo, char *methname, PyObject *tzinfoarg)
{
PyObject *result;
assert(tzinfo && methname && tzinfoarg);
assert(check_tzinfo_subclass(tzinfo) >= 0);
if (tzinfo == Py_None) {
result = Py_None;
Py_INCREF(result);
}
else
result = PyObject_CallMethod(tzinfo, methname, "O", tzinfoarg);
return result;
}
/* If self has a tzinfo member, return a BORROWED reference to it. Else /* 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, * return NULL, which is NOT AN ERROR. There are no error returns here,
* and the caller must not decref the result. * and the caller must not decref the result.
...@@ -875,69 +859,53 @@ get_tzinfo_member(PyObject *self) ...@@ -875,69 +859,53 @@ get_tzinfo_member(PyObject *self)
return tzinfo; return tzinfo;
} }
/* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the /* Call getattr(tzinfo, name)(tzinfoarg), and check the result. tzinfo must
* result. tzinfo must be an instance of the tzinfo class. If the method * be an instance of the tzinfo class. If the method returns None, this
* returns None, this returns 0 and sets *none to 1. If the method doesn't * returns None. If the method doesn't return None or timedelta, TypeError is
* return None or timedelta, TypeError is raised and this returns -1. If it * raised and this returns NULL. If it returns a timedelta and the value is
* returnsa timedelta and the value is out of range or isn't a whole number * out of range or isn't a whole number of minutes, ValueError is raised and
* of minutes, ValueError is raised and this returns -1. * this returns NULL. Else result is returned.
* Else *none is set to 0 and the integer method result is returned.
*/ */
static int static PyObject *
call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg, call_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg)
int *none)
{ {
PyObject *u; PyObject *offset;
int result = -1;
assert(tzinfo != NULL); assert(tzinfo != NULL);
assert(PyTZInfo_Check(tzinfo)); assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None);
assert(tzinfoarg != NULL); assert(tzinfoarg != NULL);
*none = 0; if (tzinfo == Py_None)
u = call_tzinfo_method(tzinfo, name, tzinfoarg); Py_RETURN_NONE;
if (u == NULL) offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg);
return -1; if (offset == Py_None || offset == NULL)
return offset;
else if (u == Py_None) { if (PyDelta_Check(offset)) {
result = 0; if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
*none = 1; Py_DECREF(offset);
} PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
else if (PyDelta_Check(u)) { " representing a whole number of minutes");
const int days = GET_TD_DAYS(u); return NULL;
if (days < -1 || days > 0) }
result = 24*60; /* trigger ValueError below */ if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
else { GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
/* next line can't overflow because we know days Py_DECREF(offset);
* is -1 or 0 now PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
*/ " strictly between -timedelta(hours=24) and"
int ss = days * 24 * 3600 + GET_TD_SECONDS(u); " timedelta(hours=24).");
result = divmod(ss, 60, &ss); return NULL;
if (ss || GET_TD_MICROSECONDS(u)) {
PyErr_Format(PyExc_ValueError,
"tzinfo.%s() must return a "
"whole number of minutes",
name);
result = -1;
}
} }
} }
else { else {
Py_DECREF(offset);
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"tzinfo.%s() must return None or " "tzinfo.%s() must return None or "
"timedelta, not '%s'", "timedelta, not '%.200s'",
name, Py_TYPE(u)->tp_name); name, Py_TYPE(offset)->tp_name);
return NULL;
} }
Py_DECREF(u); return offset;
if (result < -1439 || result > 1439) {
PyErr_Format(PyExc_ValueError,
"tzinfo.%s() returned %d; must be in "
"-1439 .. 1439",
name, result);
result = -1;
}
return result;
} }
/* Call tzinfo.utcoffset(tzinfoarg), and extract an integer from the /* Call tzinfo.utcoffset(tzinfoarg), and extract an integer from the
...@@ -948,37 +916,10 @@ call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg, ...@@ -948,37 +916,10 @@ call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg,
* # of minutes), ValueError is raised and this returns -1. Else *none is * # of minutes), ValueError is raised and this returns -1. Else *none is
* set to 0 and the offset is returned (as int # of minutes east of UTC). * set to 0 and the offset is returned (as int # of minutes east of UTC).
*/ */
static int
call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg, int *none)
{
return call_utc_tzinfo_method(tzinfo, "utcoffset", tzinfoarg, none);
}
/* Call tzinfo.name(tzinfoarg), and return the offset as a timedelta or None.
*/
static PyObject * static PyObject *
offset_as_timedelta(PyObject *tzinfo, char *name, PyObject *tzinfoarg) { call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg)
PyObject *result; {
return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg);
assert(tzinfo && name && tzinfoarg);
if (tzinfo == Py_None) {
result = Py_None;
Py_INCREF(result);
}
else {
int none;
int offset = call_utc_tzinfo_method(tzinfo, name, tzinfoarg,
&none);
if (offset < 0 && PyErr_Occurred())
return NULL;
if (none) {
result = Py_None;
Py_INCREF(result);
}
else
result = new_delta(0, offset * 60, 0, 1);
}
return result;
} }
/* Call tzinfo.dst(tzinfoarg), and extract an integer from the /* Call tzinfo.dst(tzinfoarg), and extract an integer from the
...@@ -989,10 +930,10 @@ offset_as_timedelta(PyObject *tzinfo, char *name, PyObject *tzinfoarg) { ...@@ -989,10 +930,10 @@ offset_as_timedelta(PyObject *tzinfo, char *name, PyObject *tzinfoarg) {
* ValueError is raised and this returns -1. Else *none is set to 0 and * ValueError is raised and this returns -1. Else *none is set to 0 and
* the offset is returned (as an int # of minutes east of UTC). * the offset is returned (as an int # of minutes east of UTC).
*/ */
static int static PyObject *
call_dst(PyObject *tzinfo, PyObject *tzinfoarg, int *none) call_dst(PyObject *tzinfo, PyObject *tzinfoarg)
{ {
return call_utc_tzinfo_method(tzinfo, "dst", tzinfoarg, none); return call_tzinfo_method(tzinfo, "dst", tzinfoarg);
} }
/* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be /* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be
...@@ -1010,102 +951,23 @@ call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) ...@@ -1010,102 +951,23 @@ call_tzname(PyObject *tzinfo, PyObject *tzinfoarg)
assert(check_tzinfo_subclass(tzinfo) >= 0); assert(check_tzinfo_subclass(tzinfo) >= 0);
assert(tzinfoarg != NULL); assert(tzinfoarg != NULL);
if (tzinfo == Py_None) { if (tzinfo == Py_None)
result = Py_None; Py_RETURN_NONE;
Py_INCREF(result);
}
else
result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg);
if (result != NULL && result != Py_None) {
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;
}
typedef enum { result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg);
/* an exception has been set; the caller should pass it on */
OFFSET_ERROR,
/* type isn't date, datetime, or time subclass */
OFFSET_UNKNOWN,
/* date,
* datetime with !hastzinfo
* datetime with None tzinfo,
* datetime where utcoffset() returns None
* time with !hastzinfo
* time with None tzinfo,
* time where utcoffset() returns None
*/
OFFSET_NAIVE,
/* time or datetime where utcoffset() doesn't return None */
OFFSET_AWARE
} naivety;
/* Classify an object as to whether it's naive or offset-aware. See
* the "naivety" typedef for details. If the type is aware, *offset is set
* to minutes east of UTC (as returned by the tzinfo.utcoffset() method).
* If the type is offset-naive (or unknown, or error), *offset is set to 0.
* tzinfoarg is the argument to pass to the tzinfo.utcoffset() method.
*/
static naivety
classify_utcoffset(PyObject *op, PyObject *tzinfoarg, int *offset)
{
int none;
PyObject *tzinfo;
assert(tzinfoarg != NULL); if (result == NULL || result == Py_None)
*offset = 0; return result;
tzinfo = get_tzinfo_member(op); /* NULL means no tzinfo, not error */
if (tzinfo == Py_None) if (!PyUnicode_Check(result)) {
return OFFSET_NAIVE; PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must "
if (tzinfo == NULL) { "return None or a string, not '%s'",
/* note that a datetime passes the PyDate_Check test */ Py_TYPE(result)->tp_name);
return (PyTime_Check(op) || PyDate_Check(op)) ? Py_DECREF(result);
OFFSET_NAIVE : OFFSET_UNKNOWN; result = NULL;
}
*offset = call_utcoffset(tzinfo, tzinfoarg, &none);
if (*offset == -1 && PyErr_Occurred())
return OFFSET_ERROR;
return none ? OFFSET_NAIVE : OFFSET_AWARE;
}
/* Classify two objects as to whether they're naive or offset-aware.
* This isn't quite the same as calling classify_utcoffset() twice: for
* binary operations (comparison and subtraction), we generally want to
* ignore the tzinfo members if they're identical. This is by design,
* so that results match "naive" expectations when mixing objects from a
* single timezone. So in that case, this sets both offsets to 0 and
* both naiveties to OFFSET_NAIVE.
* The function returns 0 if everything's OK, and -1 on error.
*/
static int
classify_two_utcoffsets(PyObject *o1, int *offset1, naivety *n1,
PyObject *tzinfoarg1,
PyObject *o2, int *offset2, naivety *n2,
PyObject *tzinfoarg2)
{
if (get_tzinfo_member(o1) == get_tzinfo_member(o2)) {
*offset1 = *offset2 = 0;
*n1 = *n2 = OFFSET_NAIVE;
}
else {
*n1 = classify_utcoffset(o1, tzinfoarg1, offset1);
if (*n1 == OFFSET_ERROR)
return -1;
*n2 = classify_utcoffset(o2, tzinfoarg2, offset2);
if (*n2 == OFFSET_ERROR)
return -1;
} }
return 0;
return result;
} }
/* repr is like "someclass(arg1, arg2)". If tzinfo isn't None, /* repr is like "someclass(arg1, arg2)". If tzinfo isn't None,
...@@ -1157,6 +1019,8 @@ format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds) ...@@ -1157,6 +1019,8 @@ format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds)
GET_YEAR(date)); GET_YEAR(date));
} }
static PyObject *delta_negative(PyDateTime_Delta *self);
/* Add an hours & minutes UTC offset string to buf. buf has no more than /* Add an hours & minutes UTC offset string to buf. buf has no more than
* buflen bytes remaining. The UTC offset is gotten by calling * buflen bytes remaining. The UTC offset is gotten by calling
* tzinfo.uctoffset(tzinfoarg). If that returns None, \0 is stored into * tzinfo.uctoffset(tzinfoarg). If that returns None, \0 is stored into
...@@ -1171,28 +1035,41 @@ static int ...@@ -1171,28 +1035,41 @@ static int
format_utcoffset(char *buf, size_t buflen, const char *sep, format_utcoffset(char *buf, size_t buflen, const char *sep,
PyObject *tzinfo, PyObject *tzinfoarg) PyObject *tzinfo, PyObject *tzinfoarg)
{ {
int offset; PyObject *offset;
int hours; int hours, minutes, seconds;
int minutes;
char sign; char sign;
int none;
assert(buflen >= 1); assert(buflen >= 1);
offset = call_utcoffset(tzinfo, tzinfoarg, &none); offset = call_utcoffset(tzinfo, tzinfoarg);
if (offset == -1 && PyErr_Occurred()) if (offset == NULL)
return -1; return -1;
if (none) { if (offset == Py_None) {
Py_DECREF(offset);
*buf = '\0'; *buf = '\0';
return 0; return 0;
} }
sign = '+'; /* Offset is normalized, so it is negative if days < 0 */
if (offset < 0) { if (GET_TD_DAYS(offset) < 0) {
PyObject *temp = offset;
sign = '-'; sign = '-';
offset = - offset; offset = delta_negative((PyDateTime_Delta *)offset);
Py_DECREF(temp);
if (offset == NULL)
return -1;
}
else {
sign = '+';
} }
hours = divmod(offset, 60, &minutes); /* Offset is not negative here. */
seconds = GET_TD_SECONDS(offset);
Py_DECREF(offset);
minutes = divmod(seconds, 60, &seconds);
hours = divmod(minutes, 60, &minutes);
assert(seconds == 0);
/* XXX ignore sub-minute data, curently not allowed. */
PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes); PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes);
return 0; return 0;
} }
...@@ -1434,7 +1311,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, ...@@ -1434,7 +1311,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt)); format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
if (format != NULL) { if (format != NULL) {
result = PyObject_CallMethod(time, "strftime", "OO", result = PyObject_CallMethod(time, "strftime", "OO",
format, timetuple); format, timetuple, NULL);
Py_DECREF(format); Py_DECREF(format);
} }
Py_DECREF(time); Py_DECREF(time);
...@@ -1937,17 +1814,24 @@ delta_subtract(PyObject *left, PyObject *right) ...@@ -1937,17 +1814,24 @@ delta_subtract(PyObject *left, PyObject *right)
return 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 * static PyObject *
delta_richcompare(PyObject *self, PyObject *other, int op) delta_richcompare(PyObject *self, PyObject *other, int op)
{ {
if (PyDelta_Check(other)) { if (PyDelta_Check(other)) {
int diff = GET_TD_DAYS(self) - GET_TD_DAYS(other); int diff = delta_cmp(self, 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_to_bool(diff, op); return diff_to_bool(diff, op);
} }
else { else {
...@@ -3119,76 +3003,73 @@ tzinfo_dst(PyDateTime_TZInfo *self, PyObject *dt) ...@@ -3119,76 +3003,73 @@ tzinfo_dst(PyDateTime_TZInfo *self, PyObject *dt)
return tzinfo_nogo("dst"); 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 * static PyObject *
tzinfo_fromutc(PyDateTime_TZInfo *self, PyDateTime_DateTime *dt) tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt)
{ {
int y, m, d, hh, mm, ss, us; PyObject *result = NULL;
PyObject *off = NULL, *dst = NULL;
PyObject *result; PyDateTime_Delta *delta = NULL;
int off, dst;
int none;
int delta;
if (! PyDateTime_Check(dt)) { if (!PyDateTime_Check(dt)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"fromutc: argument must be a datetime"); "fromutc: argument must be a datetime");
return NULL; return NULL;
} }
if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) { if (GET_DT_TZINFO(dt) != (PyObject *)self) {
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo " PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
"is not self"); "is not self");
return NULL; return NULL;
} }
off = call_utcoffset(dt->tzinfo, (PyObject *)dt, &none); off = datetime_utcoffset(dt, NULL);
if (off == -1 && PyErr_Occurred()) if (off == NULL)
return NULL; return NULL;
if (none) { if (off == Py_None) {
PyErr_SetString(PyExc_ValueError, "fromutc: non-None " PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
"utcoffset() result required"); "utcoffset() result required");
return NULL; goto Fail;
} }
dst = call_dst(dt->tzinfo, (PyObject *)dt, &none); dst = datetime_dst(dt, NULL);
if (dst == -1 && PyErr_Occurred()) if (dst == NULL)
return NULL; goto Fail;
if (none) { if (dst == Py_None) {
PyErr_SetString(PyExc_ValueError, "fromutc: non-None " PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
"dst() result required"); "dst() result required");
return NULL; goto Fail;
} }
y = GET_YEAR(dt); delta = (PyDateTime_Delta *)delta_subtract(off, dst);
m = GET_MONTH(dt); if (delta == NULL)
d = GET_DAY(dt); goto Fail;
hh = DATE_GET_HOUR(dt); result = add_datetime_timedelta((PyDateTime_DateTime *)dt, delta, 1);
mm = DATE_GET_MINUTE(dt);
ss = DATE_GET_SECOND(dt);
us = DATE_GET_MICROSECOND(dt);
delta = off - dst;
mm += delta;
if ((mm < 0 || mm >= 60) &&
normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
return NULL;
result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo);
if (result == NULL) if (result == NULL)
return result;
dst = call_dst(dt->tzinfo, result, &none);
if (dst == -1 && PyErr_Occurred())
goto Fail; goto Fail;
if (none)
goto Inconsistent;
if (dst == 0)
return result;
mm += dst; Py_DECREF(dst);
if ((mm < 0 || mm >= 60) && dst = call_dst(GET_DT_TZINFO(dt), result);
normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) if (dst == NULL)
goto Fail; goto Fail;
Py_DECREF(result); if (dst == Py_None)
result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo); goto Inconsistent;
if (delta_bool(delta) != 0) {
PyObject *temp = result;
result = add_datetime_timedelta((PyDateTime_DateTime *)result,
(PyDateTime_Delta *)dst, 1);
Py_DECREF(temp);
if (result == NULL)
goto Fail;
}
Py_DECREF(delta);
Py_DECREF(dst);
Py_DECREF(off);
return result; return result;
Inconsistent: Inconsistent:
...@@ -3197,7 +3078,10 @@ Inconsistent: ...@@ -3197,7 +3078,10 @@ Inconsistent:
/* fall thru to failure */ /* fall thru to failure */
Fail: Fail:
Py_DECREF(result); Py_XDECREF(off);
Py_XDECREF(dst);
Py_XDECREF(delta);
Py_XDECREF(result);
return NULL; return NULL;
} }
...@@ -3463,19 +3347,15 @@ timezone_dst(PyObject *self, PyObject *dt) ...@@ -3463,19 +3347,15 @@ timezone_dst(PyObject *self, PyObject *dt)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject *
add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta,
int factor);
static PyObject * static PyObject *
timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt)
{ {
if (! PyDateTime_Check(dt)) { if (!PyDateTime_Check(dt)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"fromutc: argument must be a datetime"); "fromutc: argument must be a datetime");
return NULL; return NULL;
} }
if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) { if (!HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo " PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
"is not self"); "is not self");
return NULL; return NULL;
...@@ -3689,21 +3569,18 @@ time_dealloc(PyDateTime_Time *self) ...@@ -3689,21 +3569,18 @@ time_dealloc(PyDateTime_Time *self)
/* These are all METH_NOARGS, so don't need to check the arglist. */ /* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject * static PyObject *
time_utcoffset(PyDateTime_Time *self, PyObject *unused) { time_utcoffset(PyObject *self, PyObject *unused) {
return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, return call_utcoffset(GET_TIME_TZINFO(self), Py_None);
"utcoffset", Py_None);
} }
static PyObject * static PyObject *
time_dst(PyDateTime_Time *self, PyObject *unused) { time_dst(PyObject *self, PyObject *unused) {
return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, return call_dst(GET_TIME_TZINFO(self), Py_None);
"dst", Py_None);
} }
static PyObject * static PyObject *
time_tzname(PyDateTime_Time *self, PyObject *unused) { time_tzname(PyDateTime_Time *self, PyObject *unused) {
return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None, return call_tzname(GET_TIME_TZINFO(self), Py_None);
Py_None);
} }
/* /*
...@@ -3758,7 +3635,7 @@ time_isoformat(PyDateTime_Time *self, PyObject *unused) ...@@ -3758,7 +3635,7 @@ time_isoformat(PyDateTime_Time *self, PyObject *unused)
TIME_GET_MINUTE(self), TIME_GET_MINUTE(self),
TIME_GET_SECOND(self)); TIME_GET_SECOND(self));
if (result == NULL || ! HASTZINFO(self) || self->tzinfo == Py_None) if (result == NULL || !HASTZINFO(self) || self->tzinfo == Py_None)
return result; return result;
/* We need to append the UTC offset. */ /* We need to append the UTC offset. */
...@@ -3809,98 +3686,108 @@ time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw) ...@@ -3809,98 +3686,108 @@ time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
static PyObject * static PyObject *
time_richcompare(PyObject *self, PyObject *other, int op) time_richcompare(PyObject *self, PyObject *other, int op)
{ {
PyObject *result = NULL;
PyObject *offset1, *offset2;
int diff; int diff;
naivety n1, n2;
int offset1, offset2;
if (! PyTime_Check(other)) { if (! PyTime_Check(other)) {
Py_INCREF(Py_NotImplemented); Py_INCREF(Py_NotImplemented);
return Py_NotImplemented; return Py_NotImplemented;
} }
if (classify_two_utcoffsets(self, &offset1, &n1, Py_None,
other, &offset2, &n2, Py_None) < 0) if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) {
diff = memcmp(((PyDateTime_Time *)self)->data,
((PyDateTime_Time *)other)->data,
_PyDateTime_TIME_DATASIZE);
return diff_to_bool(diff, op);
}
offset1 = time_utcoffset(self, NULL);
if (offset1 == NULL)
return NULL; return NULL;
assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); offset2 = time_utcoffset(other, NULL);
if (offset2 == NULL)
goto done;
/* If they're both naive, or both aware and have the same offsets, /* If they're both naive, or both aware and have the same offsets,
* we get off cheap. Note that if they're both naive, offset1 == * we get off cheap. Note that if they're both naive, offset1 ==
* offset2 == 0 at this point. * offset2 == Py_None at this point.
*/ */
if (n1 == n2 && offset1 == offset2) { if ((offset1 == offset2) ||
(PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
delta_cmp(offset1, offset2) == 0)) {
diff = memcmp(((PyDateTime_Time *)self)->data, diff = memcmp(((PyDateTime_Time *)self)->data,
((PyDateTime_Time *)other)->data, ((PyDateTime_Time *)other)->data,
_PyDateTime_TIME_DATASIZE); _PyDateTime_TIME_DATASIZE);
return diff_to_bool(diff, op); result = diff_to_bool(diff, op);
} }
/* The hard case: both aware with different UTC offsets */
if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) { else if (offset1 != Py_None && offset2 != Py_None) {
assert(offset1 != offset2); /* else last "if" handled it */ int offsecs1, offsecs2;
/* Convert everything except microseconds to seconds. These assert(offset1 != offset2); /* else last "if" handled it */
* can't overflow (no more than the # of seconds in 2 days). offsecs1 = TIME_GET_HOUR(self) * 3600 +
*/ TIME_GET_MINUTE(self) * 60 +
offset1 = TIME_GET_HOUR(self) * 3600 + TIME_GET_SECOND(self) -
(TIME_GET_MINUTE(self) - offset1) * 60 + GET_TD_DAYS(offset1) * 86400 -
TIME_GET_SECOND(self); GET_TD_SECONDS(offset1);
offset2 = TIME_GET_HOUR(other) * 3600 + offsecs2 = TIME_GET_HOUR(other) * 3600 +
(TIME_GET_MINUTE(other) - offset2) * 60 + TIME_GET_MINUTE(other) * 60 +
TIME_GET_SECOND(other); TIME_GET_SECOND(other) -
diff = offset1 - offset2; GET_TD_DAYS(offset2) * 86400 -
GET_TD_SECONDS(offset2);
diff = offsecs1 - offsecs2;
if (diff == 0) if (diff == 0)
diff = TIME_GET_MICROSECOND(self) - diff = TIME_GET_MICROSECOND(self) -
TIME_GET_MICROSECOND(other); TIME_GET_MICROSECOND(other);
return diff_to_bool(diff, op); result = diff_to_bool(diff, op);
} }
else {
assert(n1 != n2); PyErr_SetString(PyExc_TypeError,
PyErr_SetString(PyExc_TypeError, "can't compare offset-naive and "
"can't compare offset-naive and " "offset-aware times");
"offset-aware times"); }
return NULL; done:
Py_DECREF(offset1);
Py_XDECREF(offset2);
return result;
} }
static long static long
time_hash(PyDateTime_Time *self) time_hash(PyDateTime_Time *self)
{ {
if (self->hashcode == -1) { if (self->hashcode == -1) {
naivety n; PyObject *offset;
int offset;
PyObject *temp;
n = classify_utcoffset((PyObject *)self, Py_None, &offset); offset = time_utcoffset((PyObject *)self, NULL);
assert(n != OFFSET_UNKNOWN);
if (n == OFFSET_ERROR) if (offset == NULL)
return -1; return -1;
/* Reduce this to a hash of another object. */ /* Reduce this to a hash of another object. */
if (offset == 0) { if (offset == Py_None)
self->hashcode = generic_hash( self->hashcode = generic_hash(
(unsigned char *)self->data, _PyDateTime_TIME_DATASIZE); (unsigned char *)self->data, _PyDateTime_TIME_DATASIZE);
return self->hashcode;
}
else { else {
int hour; PyObject *temp1, *temp2;
int minute; int seconds, microseconds;
assert(n == OFFSET_AWARE);
assert(HASTZINFO(self)); assert(HASTZINFO(self));
hour = divmod(TIME_GET_HOUR(self) * 60 + seconds = TIME_GET_HOUR(self) * 3600 +
TIME_GET_MINUTE(self) - offset, TIME_GET_MINUTE(self) * 60 +
60, TIME_GET_SECOND(self);
&minute); microseconds = TIME_GET_MICROSECOND(self);
if (0 <= hour && hour < 24) temp1 = new_delta(0, seconds, microseconds, 1);
temp = new_time(hour, minute, if (temp1 == NULL) {
TIME_GET_SECOND(self), Py_DECREF(offset);
TIME_GET_MICROSECOND(self), return -1;
Py_None); }
else temp2 = delta_subtract(temp1, offset);
temp = Py_BuildValue("iiii", Py_DECREF(temp1);
hour, minute, if (temp2 == NULL) {
TIME_GET_SECOND(self), Py_DECREF(offset);
TIME_GET_MICROSECOND(self)); return -1;
} }
if (temp != NULL) { self->hashcode = PyObject_Hash(temp2);
self->hashcode = PyObject_Hash(temp); Py_DECREF(temp2);
Py_DECREF(temp);
} }
Py_DECREF(offset);
} }
return self->hashcode; return self->hashcode;
} }
...@@ -3929,10 +3816,10 @@ time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw) ...@@ -3929,10 +3816,10 @@ time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw)
} }
static int static int
time_bool(PyDateTime_Time *self) time_bool(PyObject *self)
{ {
int offset; PyObject *offset, *tzinfo;
int none; int offsecs = 0;
if (TIME_GET_SECOND(self) || TIME_GET_MICROSECOND(self)) { if (TIME_GET_SECOND(self) || TIME_GET_MICROSECOND(self)) {
/* Since utcoffset is in whole minutes, nothing can /* Since utcoffset is in whole minutes, nothing can
...@@ -3940,13 +3827,15 @@ time_bool(PyDateTime_Time *self) ...@@ -3940,13 +3827,15 @@ time_bool(PyDateTime_Time *self)
*/ */
return 1; return 1;
} }
offset = 0; tzinfo = GET_TIME_TZINFO(self);
if (HASTZINFO(self) && self->tzinfo != Py_None) { if (tzinfo != Py_None) {
offset = call_utcoffset(self->tzinfo, Py_None, &none); offset = call_utcoffset(tzinfo, Py_None);
if (offset == -1 && PyErr_Occurred()) if (offset == NULL)
return -1; return -1;
offsecs = GET_TD_DAYS(offset)*86400 + GET_TD_SECONDS(offset);
Py_DECREF(offset);
} }
return (TIME_GET_MINUTE(self) - offset + TIME_GET_HOUR(self)*60) != 0; return (TIME_GET_MINUTE(self)*60 - offsecs + TIME_GET_HOUR(self)*3600) != 0;
} }
/* Pickle support, a simple use of __reduce__. */ /* Pickle support, a simple use of __reduce__. */
...@@ -4455,21 +4344,18 @@ datetime_dealloc(PyDateTime_DateTime *self) ...@@ -4455,21 +4344,18 @@ datetime_dealloc(PyDateTime_DateTime *self)
/* These are all METH_NOARGS, so don't need to check the arglist. */ /* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject * static PyObject *
datetime_utcoffset(PyDateTime_DateTime *self, PyObject *unused) { datetime_utcoffset(PyObject *self, PyObject *unused) {
return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, return call_utcoffset(GET_DT_TZINFO(self), self);
"utcoffset", (PyObject *)self);
} }
static PyObject * static PyObject *
datetime_dst(PyDateTime_DateTime *self, PyObject *unused) { datetime_dst(PyObject *self, PyObject *unused) {
return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, return call_dst(GET_DT_TZINFO(self), self);
"dst", (PyObject *)self);
} }
static PyObject * static PyObject *
datetime_tzname(PyDateTime_DateTime *self, PyObject *unused) { datetime_tzname(PyObject *self, PyObject *unused) {
return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None, return call_tzname(GET_DT_TZINFO(self), self);
(PyObject *)self);
} }
/* /*
...@@ -4536,21 +4422,43 @@ datetime_subtract(PyObject *left, PyObject *right) ...@@ -4536,21 +4422,43 @@ datetime_subtract(PyObject *left, PyObject *right)
/* datetime - ??? */ /* datetime - ??? */
if (PyDateTime_Check(right)) { if (PyDateTime_Check(right)) {
/* datetime - datetime */ /* datetime - datetime */
naivety n1, n2; PyObject *offset1, *offset2, *offdiff = NULL;
int offset1, offset2;
int delta_d, delta_s, delta_us; int delta_d, delta_s, delta_us;
if (classify_two_utcoffsets(left, &offset1, &n1, left, if (GET_DT_TZINFO(left) == GET_DT_TZINFO(right)) {
right, &offset2, &n2, offset2 = offset1 = Py_None;
right) < 0) Py_INCREF(offset1);
return NULL; Py_INCREF(offset2);
assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); }
if (n1 != n2) { else {
PyErr_SetString(PyExc_TypeError, offset1 = datetime_utcoffset(left, NULL);
"can't subtract offset-naive and " if (offset1 == NULL)
"offset-aware datetimes"); return NULL;
return NULL; offset2 = datetime_utcoffset(right, NULL);
if (offset2 == NULL) {
Py_DECREF(offset1);
return NULL;
}
if ((offset1 != Py_None) != (offset2 != Py_None)) {
PyErr_SetString(PyExc_TypeError,
"can't subtract offset-naive and "
"offset-aware datetimes");
Py_DECREF(offset1);
Py_DECREF(offset2);
return NULL;
}
}
if ((offset1 != offset2) &&
delta_cmp(offset1, offset2) != 0) {
offdiff = delta_subtract(offset1, offset2);
if (offdiff == NULL) {
Py_DECREF(offset1);
Py_DECREF(offset2);
return NULL;
}
} }
Py_DECREF(offset1);
Py_DECREF(offset2);
delta_d = ymd_to_ord(GET_YEAR(left), delta_d = ymd_to_ord(GET_YEAR(left),
GET_MONTH(left), GET_MONTH(left),
GET_DAY(left)) - GET_DAY(left)) -
...@@ -4569,11 +4477,13 @@ datetime_subtract(PyObject *left, PyObject *right) ...@@ -4569,11 +4477,13 @@ datetime_subtract(PyObject *left, PyObject *right)
DATE_GET_SECOND(right)); DATE_GET_SECOND(right));
delta_us = DATE_GET_MICROSECOND(left) - delta_us = DATE_GET_MICROSECOND(left) -
DATE_GET_MICROSECOND(right); DATE_GET_MICROSECOND(right);
/* (left - offset1) - (right - offset2) =
* (left - right) + (offset2 - offset1)
*/
delta_s += (offset2 - offset1) * 60;
result = new_delta(delta_d, delta_s, delta_us, 1); result = new_delta(delta_d, delta_s, delta_us, 1);
if (offdiff != NULL) {
PyObject *temp = result;
result = delta_subtract(result, offdiff);
Py_DECREF(temp);
Py_DECREF(offdiff);
}
} }
else if (PyDelta_Check(right)) { else if (PyDelta_Check(right)) {
/* datetime - delta */ /* datetime - delta */
...@@ -4683,9 +4593,9 @@ datetime_ctime(PyDateTime_DateTime *self) ...@@ -4683,9 +4593,9 @@ datetime_ctime(PyDateTime_DateTime *self)
static PyObject * static PyObject *
datetime_richcompare(PyObject *self, PyObject *other, int op) datetime_richcompare(PyObject *self, PyObject *other, int op)
{ {
PyObject *result = NULL;
PyObject *offset1, *offset2;
int diff; int diff;
naivety n1, n2;
int offset1, offset2;
if (! PyDateTime_Check(other)) { if (! PyDateTime_Check(other)) {
if (PyDate_Check(other)) { if (PyDate_Check(other)) {
...@@ -4706,85 +4616,99 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) ...@@ -4706,85 +4616,99 @@ datetime_richcompare(PyObject *self, PyObject *other, int op)
return Py_NotImplemented; return Py_NotImplemented;
} }
if (classify_two_utcoffsets(self, &offset1, &n1, self, if (GET_DT_TZINFO(self) == GET_DT_TZINFO(other)) {
other, &offset2, &n2, other) < 0) diff = memcmp(((PyDateTime_DateTime *)self)->data,
((PyDateTime_DateTime *)other)->data,
_PyDateTime_DATETIME_DATASIZE);
return diff_to_bool(diff, op);
}
offset1 = datetime_utcoffset(self, NULL);
if (offset1 == NULL)
return NULL; return NULL;
assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); offset2 = datetime_utcoffset(other, NULL);
if (offset2 == NULL)
goto done;
/* If they're both naive, or both aware and have the same offsets, /* If they're both naive, or both aware and have the same offsets,
* we get off cheap. Note that if they're both naive, offset1 == * we get off cheap. Note that if they're both naive, offset1 ==
* offset2 == 0 at this point. * offset2 == Py_None at this point.
*/ */
if (n1 == n2 && offset1 == offset2) { if ((offset1 == offset2) ||
(PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
delta_cmp(offset1, offset2) == 0)) {
diff = memcmp(((PyDateTime_DateTime *)self)->data, diff = memcmp(((PyDateTime_DateTime *)self)->data,
((PyDateTime_DateTime *)other)->data, ((PyDateTime_DateTime *)other)->data,
_PyDateTime_DATETIME_DATASIZE); _PyDateTime_DATETIME_DATASIZE);
return diff_to_bool(diff, op); result = diff_to_bool(diff, op);
} }
else if (offset1 != Py_None && offset2 != Py_None) {
if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) {
PyDateTime_Delta *delta; PyDateTime_Delta *delta;
assert(offset1 != offset2); /* else last "if" handled it */ assert(offset1 != offset2); /* else last "if" handled it */
delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self, delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self,
other); other);
if (delta == NULL) if (delta == NULL)
return NULL; goto done;
diff = GET_TD_DAYS(delta); diff = GET_TD_DAYS(delta);
if (diff == 0) if (diff == 0)
diff = GET_TD_SECONDS(delta) | diff = GET_TD_SECONDS(delta) |
GET_TD_MICROSECONDS(delta); GET_TD_MICROSECONDS(delta);
Py_DECREF(delta); Py_DECREF(delta);
return diff_to_bool(diff, op); result = diff_to_bool(diff, op);
} }
else {
assert(n1 != n2); PyErr_SetString(PyExc_TypeError,
PyErr_SetString(PyExc_TypeError, "can't compare offset-naive and "
"can't compare offset-naive and " "offset-aware datetimes");
"offset-aware datetimes"); }
return NULL; done:
Py_DECREF(offset1);
Py_XDECREF(offset2);
return result;
} }
static long static long
datetime_hash(PyDateTime_DateTime *self) datetime_hash(PyDateTime_DateTime *self)
{ {
if (self->hashcode == -1) { if (self->hashcode == -1) {
naivety n; PyObject *offset;
int offset;
PyObject *temp; offset = datetime_utcoffset((PyObject *)self, NULL);
n = classify_utcoffset((PyObject *)self, (PyObject *)self, if (offset == NULL)
&offset);
assert(n != OFFSET_UNKNOWN);
if (n == OFFSET_ERROR)
return -1; return -1;
/* Reduce this to a hash of another object. */ /* Reduce this to a hash of another object. */
if (n == OFFSET_NAIVE) { if (offset == Py_None)
self->hashcode = generic_hash( self->hashcode = generic_hash(
(unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE); (unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE);
return self->hashcode;
}
else { else {
int days; PyObject *temp1, *temp2;
int seconds; int days, seconds;
assert(n == OFFSET_AWARE);
assert(HASTZINFO(self)); assert(HASTZINFO(self));
days = ymd_to_ord(GET_YEAR(self), days = ymd_to_ord(GET_YEAR(self),
GET_MONTH(self), GET_MONTH(self),
GET_DAY(self)); GET_DAY(self));
seconds = DATE_GET_HOUR(self) * 3600 + seconds = DATE_GET_HOUR(self) * 3600 +
(DATE_GET_MINUTE(self) - offset) * 60 + DATE_GET_MINUTE(self) * 60 +
DATE_GET_SECOND(self); DATE_GET_SECOND(self);
temp = new_delta(days, temp1 = new_delta(days, seconds,
seconds, DATE_GET_MICROSECOND(self),
DATE_GET_MICROSECOND(self), 1);
1); if (temp1 == NULL) {
} Py_DECREF(offset);
if (temp != NULL) { return -1;
self->hashcode = PyObject_Hash(temp); }
Py_DECREF(temp); temp2 = delta_subtract(temp1, offset);
Py_DECREF(temp1);
if (temp2 == NULL) {
Py_DECREF(offset);
return -1;
}
self->hashcode = PyObject_Hash(temp2);
Py_DECREF(temp2);
} }
Py_DECREF(offset);
} }
return self->hashcode; return self->hashcode;
} }
...@@ -4819,10 +4743,9 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) ...@@ -4819,10 +4743,9 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
static PyObject * static PyObject *
datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
{ {
int y, m, d, hh, mm, ss, us;
PyObject *result; PyObject *result;
int offset, none; PyObject *offset;
PyObject *temp;
PyObject *tzinfo; PyObject *tzinfo;
static char *keywords[] = {"tz", NULL}; static char *keywords[] = {"tz", NULL};
...@@ -4840,39 +4763,35 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) ...@@ -4840,39 +4763,35 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
} }
/* Convert self to UTC. */ /* Convert self to UTC. */
offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none); offset = datetime_utcoffset((PyObject *)self, NULL);
if (offset == -1 && PyErr_Occurred()) if (offset == NULL)
return NULL; return NULL;
if (none) if (offset == Py_None) {
goto NeedAware; Py_DECREF(offset);
NeedAware:
PyErr_SetString(PyExc_ValueError, "astimezone() cannot be applied to "
"a naive datetime");
return NULL;
}
y = GET_YEAR(self); /* result = self - offset */
m = GET_MONTH(self); result = add_datetime_timedelta(self,
d = GET_DAY(self); (PyDateTime_Delta *)offset, -1);
hh = DATE_GET_HOUR(self); Py_DECREF(offset);
mm = DATE_GET_MINUTE(self); if (result == NULL)
ss = DATE_GET_SECOND(self);
us = DATE_GET_MICROSECOND(self);
mm -= offset;
if ((mm < 0 || mm >= 60) &&
normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
return NULL; return NULL;
/* Attach new tzinfo and let fromutc() do the rest. */ /* Attach new tzinfo and let fromutc() do the rest. */
result = new_datetime(y, m, d, hh, mm, ss, us, tzinfo); temp = ((PyDateTime_DateTime *)result)->tzinfo;
if (result != NULL) { ((PyDateTime_DateTime *)result)->tzinfo = tzinfo;
PyObject *temp = result; Py_INCREF(tzinfo);
Py_DECREF(temp);
result = PyObject_CallMethod(tzinfo, "fromutc", "O", temp); temp = result;
Py_DECREF(temp); result = PyObject_CallMethod(tzinfo, "fromutc", "O", temp);
} Py_DECREF(temp);
return result;
NeedAware: return result;
PyErr_SetString(PyExc_ValueError, "astimezone() cannot be applied to "
"a naive datetime");
return NULL;
} }
static PyObject * static PyObject *
...@@ -4881,17 +4800,15 @@ datetime_timetuple(PyDateTime_DateTime *self) ...@@ -4881,17 +4800,15 @@ datetime_timetuple(PyDateTime_DateTime *self)
int dstflag = -1; int dstflag = -1;
if (HASTZINFO(self) && self->tzinfo != Py_None) { if (HASTZINFO(self) && self->tzinfo != Py_None) {
int none; PyObject * dst;
dstflag = call_dst(self->tzinfo, (PyObject *)self, &none); dst = call_dst(self->tzinfo, (PyObject *)self);
if (dstflag == -1 && PyErr_Occurred()) if (dst == NULL)
return NULL; return NULL;
if (none) if (dst != Py_None)
dstflag = -1; dstflag = delta_bool((PyDateTime_Delta *)dst);
else if (dstflag != 0) Py_DECREF(dst);
dstflag = 1;
} }
return build_struct_time(GET_YEAR(self), return build_struct_time(GET_YEAR(self),
GET_MONTH(self), GET_MONTH(self),
...@@ -4927,41 +4844,47 @@ datetime_gettimetz(PyDateTime_DateTime *self) ...@@ -4927,41 +4844,47 @@ datetime_gettimetz(PyDateTime_DateTime *self)
DATE_GET_MINUTE(self), DATE_GET_MINUTE(self),
DATE_GET_SECOND(self), DATE_GET_SECOND(self),
DATE_GET_MICROSECOND(self), DATE_GET_MICROSECOND(self),
HASTZINFO(self) ? self->tzinfo : Py_None); GET_DT_TZINFO(self));
} }
static PyObject * static PyObject *
datetime_utctimetuple(PyDateTime_DateTime *self) datetime_utctimetuple(PyDateTime_DateTime *self)
{ {
int y = GET_YEAR(self); int y, m, d, hh, mm, ss;
int m = GET_MONTH(self); PyObject *tzinfo;
int d = GET_DAY(self); PyDateTime_DateTime *utcself;
int hh = DATE_GET_HOUR(self);
int mm = DATE_GET_MINUTE(self);
int ss = DATE_GET_SECOND(self);
int us = 0; /* microseconds are ignored in a timetuple */
int offset = 0;
if (HASTZINFO(self) && self->tzinfo != Py_None) {
int none;
offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none); tzinfo = GET_DT_TZINFO(self);
if (offset == -1 && PyErr_Occurred()) if (tzinfo == Py_None) {
return NULL; utcself = self;
Py_INCREF(utcself);
} }
/* Even if offset is 0, don't call timetuple() -- tm_isdst should be else {
* 0 in a UTC timetuple regardless of what dst() says. PyObject *offset;
*/ offset = call_utcoffset(tzinfo, (PyObject *)self);
if (offset) { if (offset == NULL)
/* Subtract offset minutes & normalize. */
int stat;
mm -= offset;
stat = normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us);
/* OverflowError may be raised in the edge cases. */
if (stat < 0)
return NULL; return NULL;
if (offset == Py_None) {
Py_DECREF(offset);
utcself = self;
Py_INCREF(utcself);
}
else {
utcself = (PyDateTime_DateTime *)add_datetime_timedelta(self,
(PyDateTime_Delta *)offset, -1);
Py_DECREF(offset);
if (utcself == NULL)
return NULL;
}
} }
y = GET_YEAR(utcself);
m = GET_MONTH(utcself);
d = GET_DAY(utcself);
hh = DATE_GET_HOUR(utcself);
mm = DATE_GET_MINUTE(utcself);
ss = DATE_GET_SECOND(utcself);
Py_DECREF(utcself);
return build_struct_time(y, m, d, hh, mm, ss, 0); return build_struct_time(y, m, d, hh, mm, ss, 0);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment