Fix datetime before epoch on windows in cython implementation (#436)

Cython implementation still used datetime.from_timestamp method, which does not work on windows.
Update the cython implementation to use utc time and delta and add a regression test to highlight the issue.
This commit is contained in:
Peter Fischer 2020-07-30 16:48:51 +02:00 committed by GitHub
parent 772c830841
commit 8fb709f2e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 13 deletions

View file

@ -25,6 +25,7 @@ clean:
rm -rf build
rm -f msgpack/_cmsgpack.cpp
rm -f msgpack/_cmsgpack.*.so
rm -f msgpack/_cmsgpack.*.pyd
rm -rf msgpack/__pycache__
rm -rf test/__pycache__

View file

@ -341,7 +341,26 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
else if (u->timestamp == 0) { // Timestamp
py = PyObject_CallFunction(u->timestamp_t, "(Lk)", ts.tv_sec, ts.tv_nsec);
}
else { // float or datetime
else if (u->timestamp == 3) { // datetime
// Calculate datetime using epoch + delta
// due to limitations PyDateTime_FromTimestamp on Windows with negative timestamps
PyObject *epoch = PyDateTimeAPI->DateTime_FromDateAndTime(1970, 1, 1, 0, 0, 0, 0, u->utc, PyDateTimeAPI->DateTimeType);
if (epoch == NULL) {
return -1;
}
PyObject* d = PyDelta_FromDSU(0, ts.tv_sec, ts.tv_nsec / 1000);
if (d == NULL) {
Py_DECREF(epoch);
return -1;
}
py = PyNumber_Add(epoch, d);
Py_DECREF(epoch);
Py_DECREF(d);
}
else { // float
PyObject *a = PyFloat_FromDouble((double)ts.tv_nsec);
if (a == NULL) return -1;
@ -358,19 +377,8 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
a = PyNumber_Add(b, c);
Py_DECREF(b);
Py_DECREF(c);
if (u->timestamp == 3) { // datetime
PyObject *t = PyTuple_Pack(2, a, u->utc);
Py_DECREF(a);
if (t == NULL) {
return -1;
}
py = PyDateTime_FromTimestamp(t);
Py_DECREF(t);
} else { // float
py = a;
}
}
} else {
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
}

View file

@ -99,6 +99,14 @@ def test_unpack_datetime():
assert unpacked == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=_utc)
@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_unpack_before_epoch():
t_in = datetime.datetime(1960, 1, 1, tzinfo=_utc)
packed = msgpack.packb(t_in, datetime=True)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == t_in
@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_datetime():
t = Timestamp(42, 14000)