mirror of
https://github.com/msgpack/msgpack-python.git
synced 2025-10-19 20:03:16 +00:00
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:
parent
772c830841
commit
8fb709f2e0
3 changed files with 30 additions and 13 deletions
1
Makefile
1
Makefile
|
@ -25,6 +25,7 @@ clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
rm -f msgpack/_cmsgpack.cpp
|
rm -f msgpack/_cmsgpack.cpp
|
||||||
rm -f msgpack/_cmsgpack.*.so
|
rm -f msgpack/_cmsgpack.*.so
|
||||||
|
rm -f msgpack/_cmsgpack.*.pyd
|
||||||
rm -rf msgpack/__pycache__
|
rm -rf msgpack/__pycache__
|
||||||
rm -rf test/__pycache__
|
rm -rf test/__pycache__
|
||||||
|
|
||||||
|
|
|
@ -341,7 +341,26 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
|
||||||
else if (u->timestamp == 0) { // Timestamp
|
else if (u->timestamp == 0) { // Timestamp
|
||||||
py = PyObject_CallFunction(u->timestamp_t, "(Lk)", ts.tv_sec, ts.tv_nsec);
|
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);
|
PyObject *a = PyFloat_FromDouble((double)ts.tv_nsec);
|
||||||
if (a == NULL) return -1;
|
if (a == NULL) return -1;
|
||||||
|
|
||||||
|
@ -358,18 +377,7 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
|
||||||
a = PyNumber_Add(b, c);
|
a = PyNumber_Add(b, c);
|
||||||
Py_DECREF(b);
|
Py_DECREF(b);
|
||||||
Py_DECREF(c);
|
Py_DECREF(c);
|
||||||
|
py = a;
|
||||||
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 {
|
} else {
|
||||||
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
|
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
|
||||||
|
|
|
@ -99,6 +99,14 @@ def test_unpack_datetime():
|
||||||
assert unpacked == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=_utc)
|
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")
|
@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
|
||||||
def test_pack_datetime():
|
def test_pack_datetime():
|
||||||
t = Timestamp(42, 14000)
|
t = Timestamp(42, 14000)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue