Skip to content

Commit 3691cac

Browse files
committed
Merge branch 'Xmader/perf/convert-date-by-timestamp' into Xmader/fix/readline-windows
2 parents 1fb3ae2 + fb8b578 commit 3691cac

File tree

6 files changed

+26
-18
lines changed

6 files changed

+26
-18
lines changed

src/DateType.cc

+17-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
DateType::DateType(PyObject *object) : PyType(object) {}
1313

1414
DateType::DateType(JSContext *cx, JS::HandleObject dateObj) {
15+
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } // for PyDateTime_FromTimestamp
16+
1517
JS::Rooted<JS::ValueArray<0>> args(cx);
1618
JS::Rooted<JS::Value> year(cx);
1719
JS::Rooted<JS::Value> month(cx);
@@ -20,19 +22,23 @@ DateType::DateType(JSContext *cx, JS::HandleObject dateObj) {
2022
JS::Rooted<JS::Value> minute(cx);
2123
JS::Rooted<JS::Value> second(cx);
2224
JS::Rooted<JS::Value> usecond(cx);
23-
JS_CallFunctionName(cx, dateObj, "getFullYear", args, &year);
24-
JS_CallFunctionName(cx, dateObj, "getMonth", args, &month);
25-
JS_CallFunctionName(cx, dateObj, "getDate", args, &day);
26-
JS_CallFunctionName(cx, dateObj, "getHours", args, &hour);
27-
JS_CallFunctionName(cx, dateObj, "getMinutes", args, &minute);
28-
JS_CallFunctionName(cx, dateObj, "getSeconds", args, &second);
29-
JS_CallFunctionName(cx, dateObj, "getMilliseconds", args, &usecond);
30-
31-
PyDateTime_IMPORT;
32-
pyObject = PyDateTime_FromDateAndTime(
25+
JS_CallFunctionName(cx, dateObj, "getUTCFullYear", args, &year);
26+
JS_CallFunctionName(cx, dateObj, "getUTCMonth", args, &month);
27+
JS_CallFunctionName(cx, dateObj, "getUTCDate", args, &day);
28+
JS_CallFunctionName(cx, dateObj, "getUTCHours", args, &hour);
29+
JS_CallFunctionName(cx, dateObj, "getUTCMinutes", args, &minute);
30+
JS_CallFunctionName(cx, dateObj, "getUTCSeconds", args, &second);
31+
JS_CallFunctionName(cx, dateObj, "getUTCMilliseconds", args, &usecond);
32+
33+
pyObject = PyDateTimeAPI->DateTime_FromDateAndTime(
3334
year.toNumber(), month.toNumber() + 1, day.toNumber(),
3435
hour.toNumber(), minute.toNumber(), second.toNumber(),
35-
usecond.toNumber() * 1000);
36+
usecond.toNumber() * 1000,
37+
PyDateTime_TimeZone_UTC, // Make the resulting Python datetime object timezone-aware
38+
// See https://docs.python.org/3/library/datetime.html#aware-and-naive-objects
39+
PyDateTimeAPI->DateTimeType
40+
);
41+
Py_INCREF(PyDateTime_TimeZone_UTC);
3642
}
3743

3844
JSObject *DateType::toJsDate(JSContext *cx) {

src/jsTypeFactory.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ size_t UCS4ToUTF16(const uint32_t *chars, size_t length, uint16_t **outStr) {
6767
}
6868

6969
JS::Value jsTypeFactory(JSContext *cx, PyObject *object) {
70-
PyDateTime_IMPORT; // for PyDateTime_Check
70+
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } // for PyDateTime_Check
7171

7272
JS::RootedValue returnType(cx);
7373

src/modules/pythonmonkey/pythonmonkey.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ static JSFunctionSpec jsGlobalFunctions[] = {
391391

392392
PyMODINIT_FUNC PyInit_pythonmonkey(void)
393393
{
394-
PyDateTime_IMPORT;
394+
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
395395

396396
SpiderMonkeyError = PyErr_NewException("pythonmonkey.SpiderMonkeyError", NULL, NULL);
397397
if (!JS_Init()) {

tests/js/js2py/datetime2.simple

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def eq(date1, date2):
5252
def eqUnixTime(date, timet):
5353
import datetime
5454
global eq
55-
return eq(date, datetime.datetime.utcfromtimestamp(timet))
55+
return eq(date, datetime.datetime.fromtimestamp(timet, tz=datetime.timezone.utc))
5656
`);
5757

5858
const eq = python.eval('eq');

tests/js/py2js/datetime.simple.failing renamed to tests/js/py2js/datetime.simple

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
const throughJS = x => x;
1212
var expectedJsTimestamp;
13+
var actualJsTimestamp;
1314
var jsDate;
1415
var pyDate;
1516

@@ -35,9 +36,10 @@ python.exec(`import datetime`);
3536
pyDate = python.eval(`datetime.datetime.fromtimestamp(${expectedJsTimestamp}, tz=timezone.utc)`);
3637
jsDate = throughJS(pyDate);
3738

38-
if (jsDate.getTime() !== expectedJsTimestamp)
39+
actualJsTimestamp = jsDate.getTime() / 1000; // JS timestamp is in milliseconds, convert it to seconds
40+
if (actualJsTimestamp !== expectedJsTimestamp)
3941
{
40-
console.error('expected', expectedJsTimestamp, 'but got',jsDate.getTime());
42+
console.error('expected', expectedJsTimestamp, 'but got', actualJsTimestamp);
4143
throw new Error('test failed');
4244
}
4345

tests/python/test_pythonmonkey_eval.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import pythonmonkey as pm
33
import random
4-
from datetime import datetime, timedelta
4+
from datetime import datetime, timedelta, timezone
55
import math
66

77
def test_passes():
@@ -47,7 +47,7 @@ def test_eval_booleans():
4747
def test_eval_dates():
4848
MIN_YEAR = 1 # https://docs.python.org/3/library/datetime.html#datetime.MINYEAR
4949
MAX_YEAR = 2023
50-
start = datetime(MIN_YEAR, 1, 1, 00, 00, 00)
50+
start = datetime(MIN_YEAR, 1, 1, 00, 00, 00, tzinfo=timezone.utc)
5151
years = MAX_YEAR - MIN_YEAR + 1
5252
end = start + timedelta(days=365 * years)
5353
for _ in range(10):

0 commit comments

Comments
 (0)