Skip to content

Commit 7d2eae9

Browse files
Jonathan EliashivJonathan Eliashiv
authored andcommitted
fix up compile. This was more complicated than I thought
1 parent 4b12c10 commit 7d2eae9

File tree

8 files changed

+182
-62
lines changed

8 files changed

+182
-62
lines changed

pandas/_libs/src/ujson/python/date_conversions.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ int scaleNanosecToUnit(npy_int64 *value, NPY_DATETIMEUNIT unit) {
4141
}
4242

4343
/* Converts the int64_t representation of a datetime to ISO; mutates len */
44-
char *int64ToIso(int64_t value, NPY_DATETIMEUNIT base, size_t *len) {
44+
char *int64ToIso(int64_t value, NPY_DATETIMEUNIT base, size_t *len, int local, int utc, PyObject *timezone_obj) {
4545
npy_datetimestruct dts;
4646
int ret_code;
4747

4848
pandas_datetime_to_datetimestruct(value, NPY_FR_ns, &dts);
4949

50-
*len = (size_t)get_datetime_iso_8601_strlen(0, base);
50+
*len = (size_t)get_datetime_iso_8601_strlen(local, base);
5151
char *result = PyObject_Malloc(*len);
5252

5353
if (result == NULL) {
5454
PyErr_NoMemory();
5555
return NULL;
5656
}
5757

58-
ret_code = make_iso_8601_datetime(&dts, result, *len, base);
58+
ret_code = make_iso_8601_datetime(&dts, result, *len, base, local, utc, timezone_obj);
5959
if (ret_code != 0) {
6060
PyErr_SetString(PyExc_ValueError,
6161
"Could not convert datetime value to string");
@@ -75,7 +75,7 @@ npy_datetime NpyDateTimeToEpoch(npy_datetime dt, NPY_DATETIMEUNIT base) {
7575

7676
/* Convert PyDatetime To ISO C-string. mutates len */
7777
char *PyDateTimeToIso(PyObject *obj, NPY_DATETIMEUNIT base,
78-
size_t *len) {
78+
size_t *len, int local, int utc, PyObject *timezone_obj) {
7979
npy_datetimestruct dts;
8080
int ret;
8181

@@ -88,9 +88,9 @@ char *PyDateTimeToIso(PyObject *obj, NPY_DATETIMEUNIT base,
8888
return NULL;
8989
}
9090

91-
*len = (size_t)get_datetime_iso_8601_strlen(0, base);
91+
*len = (size_t)get_datetime_iso_8601_strlen(local, base);
9292
char *result = PyObject_Malloc(*len);
93-
ret = make_iso_8601_datetime(&dts, result, *len, base);
93+
ret = make_iso_8601_datetime(&dts, result, *len, base, local, utc, timezone_obj);
9494

9595
if (ret != 0) {
9696
PyErr_SetString(PyExc_ValueError,

pandas/_libs/src/ujson/python/date_conversions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ int scaleNanosecToUnit(npy_int64 *value, NPY_DATETIMEUNIT unit);
1919
// up to precision `base` e.g. base="s" yields 2020-01-03T00:00:00Z
2020
// while base="ns" yields "2020-01-01T00:00:00.000000000Z"
2121
// len is mutated to save the length of the returned string
22-
char *int64ToIso(int64_t value, NPY_DATETIMEUNIT base, size_t *len);
22+
char *int64ToIso(int64_t value, NPY_DATETIMEUNIT base, size_t *len, int local, int utc, PyObject *timezone_obj);
2323

2424
// TODO(username): this function doesn't do a lot; should augment or
2525
// replace with scaleNanosecToUnit
@@ -29,7 +29,7 @@ npy_datetime NpyDateTimeToEpoch(npy_datetime dt, NPY_DATETIMEUNIT base);
2929
// up to precision `base` e.g. base="s" yields 2020-01-03T00:00:00Z
3030
// while base="ns" yields "2020-01-01T00:00:00.000000000Z"
3131
// len is mutated to save the length of the returned string
32-
char *PyDateTimeToIso(PyObject *obj, NPY_DATETIMEUNIT base, size_t *len);
32+
char *PyDateTimeToIso(PyObject *obj, NPY_DATETIMEUNIT base, size_t *len, int local, int utc, PyObject *timezone_obj);
3333

3434
// Convert a Python Date/Datetime to Unix epoch with resolution base
3535
npy_datetime PyDateTimeToEpoch(PyObject *dt, NPY_DATETIMEUNIT base);

pandas/_libs/src/ujson/python/objToJSON.c

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ typedef struct __PyObjectEncoder {
129129
void *npyValue;
130130

131131
int datetimeIso;
132+
int local;
133+
int utc;
134+
PyObject *timezone_obj;
132135
NPY_DATETIMEUNIT datetimeUnit;
133136

134137
// output format style for pandas data types
@@ -336,8 +339,12 @@ static char *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *Py_UNUSED(tc),
336339
/* JSON callback. returns a char* and mutates the pointer to *len */
337340
static char *NpyDateTimeToIsoCallback(JSOBJ Py_UNUSED(unused),
338341
JSONTypeContext *tc, size_t *len) {
342+
339343
NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit;
340-
return int64ToIso(GET_TC(tc)->longValue, base, len);
344+
int local = ((PyObjectEncoder *)tc->encoder)->local;
345+
int utc = ((PyObjectEncoder *)tc->encoder)->utc;
346+
int timezone_obj = ((PyObjectEncoder *)tc->encoder)->timezone_obj;
347+
return int64ToIso(GET_TC(tc)->longValue, base, len, local, utc, timezone_obj);
341348
}
342349

343350
/* JSON callback. returns a char* and mutates the pointer to *len */
@@ -355,7 +362,10 @@ static char *PyDateTimeToIsoCallback(JSOBJ obj, JSONTypeContext *tc,
355362
}
356363

357364
NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit;
358-
return PyDateTimeToIso(obj, base, len);
365+
int local = ((PyObjectEncoder *)tc->encoder)->local;
366+
int utc = ((PyObjectEncoder *)tc->encoder)->utc;
367+
int timezone_obj = ((PyObjectEncoder *)tc->encoder)->timezone_obj;
368+
return PyDateTimeToIso(obj, base, len, local, utc, timezone_obj);
359369
}
360370

361371
static char *PyTimeToJSON(JSOBJ _obj, JSONTypeContext *tc, size_t *outLen) {
@@ -1316,9 +1326,9 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
13161326
cLabel = int64ToIsoDuration(nanosecVal, &len);
13171327
} else {
13181328
if (type_num == NPY_DATETIME) {
1319-
cLabel = int64ToIso(nanosecVal, base, &len);
1329+
cLabel = int64ToIso(nanosecVal, base, &len, 0, 0, NULL);
13201330
} else {
1321-
cLabel = PyDateTimeToIso(item, base, &len);
1331+
cLabel = PyDateTimeToIso(item, base, &len, 0, 0, NULL);
13221332
}
13231333
}
13241334
if (cLabel == NULL) {
@@ -1909,6 +1919,7 @@ void Object_endTypeContext(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc) {
19091919

19101920
const char *Object_getStringValue(JSOBJ obj, JSONTypeContext *tc,
19111921
size_t *_outLen) {
1922+
19121923
return GET_TC(tc)->PyTypeToUTF8(obj, tc, _outLen);
19131924
}
19141925

@@ -1966,7 +1977,7 @@ PyObject *objToJSON(PyObject *Py_UNUSED(self), PyObject *args,
19661977
"iso_dates",
19671978
"default_handler",
19681979
"indent",
1969-
NULL};
1980+
"timezone"};
19701981

19711982
char buffer[65536];
19721983
char *ret;
@@ -1979,6 +1990,7 @@ PyObject *objToJSON(PyObject *Py_UNUSED(self), PyObject *args,
19791990
char *sdateFormat = NULL;
19801991
PyObject *oisoDates = 0;
19811992
PyObject *odefHandler = 0;
1993+
PyObject *timezone_obj = NULL;
19821994
int indent = 0;
19831995

19841996
PyObjectEncoder pyEncoder = {{
@@ -2011,16 +2023,20 @@ PyObject *objToJSON(PyObject *Py_UNUSED(self), PyObject *args,
20112023
pyEncoder.npyType = -1;
20122024
pyEncoder.npyValue = NULL;
20132025
pyEncoder.datetimeIso = 0;
2026+
pyEncoder.local = 0;
2027+
pyEncoder.utc = 0;
2028+
pyEncoder.timezone_obj = NULL;
20142029
pyEncoder.datetimeUnit = NPY_FR_ms;
20152030
pyEncoder.outputFormat = COLUMNS;
20162031
pyEncoder.defaultHandler = 0;
20172032

20182033
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiOssOOi", kwlist,
20192034
&oinput, &oensureAscii, &idoublePrecision,
20202035
&oencodeHTMLChars, &sOrient, &sdateFormat,
2021-
&oisoDates, &odefHandler, &indent)) {
2036+
&oisoDates, &odefHandler, &indent, &timezone_obj)) {
20222037
return NULL;
20232038
}
2039+
Py_XINCREF(timezone_obj);
20242040

20252041
if (oensureAscii != NULL && !PyObject_IsTrue(oensureAscii)) {
20262042
encoder->forceASCII = 0;
@@ -2074,6 +2090,57 @@ PyObject *objToJSON(PyObject *Py_UNUSED(self), PyObject *args,
20742090

20752091
if (oisoDates != NULL && PyObject_IsTrue(oisoDates)) {
20762092
pyEncoder.datetimeIso = 1;
2093+
if (timezone_obj != NULL) {
2094+
PyObject *strobj;
2095+
if (PyBytes_Check(timezone_obj)) {
2096+
/* accept bytes input */
2097+
PyObject *obj_str = PyUnicode_FromEncodedObject(timezone_obj, NULL, NULL);
2098+
if (obj_str == NULL) {
2099+
goto fail;
2100+
}
2101+
strobj = obj_str;
2102+
}
2103+
else {
2104+
Py_INCREF(timezone_obj);
2105+
strobj = timezone_obj;
2106+
}
2107+
2108+
Py_SETREF(timezone_obj, strobj);
2109+
2110+
/* Check for the supported string inputs */
2111+
if (PyUnicode_Check(timezone_obj)) {
2112+
Py_ssize_t len;
2113+
char const *str = PyUnicode_AsUTF8AndSize(timezone_obj, &len);
2114+
if (str == NULL) {
2115+
goto fail;
2116+
}
2117+
2118+
if (strcmp(str, "local") == 0) {
2119+
pyEncoder.local = 1;
2120+
pyEncoder.utc = 0;
2121+
pyEncoder.timezone_obj = NULL;
2122+
}
2123+
else if (strcmp(str, "UTC") == 0) {
2124+
pyEncoder.local = 0;
2125+
pyEncoder.utc = 1;
2126+
pyEncoder.timezone_obj = NULL;
2127+
}
2128+
else if (strcmp(str, "naive") == 0) {
2129+
pyEncoder.local = 0;
2130+
pyEncoder.utc = 0;
2131+
pyEncoder.timezone_obj = NULL;
2132+
}
2133+
else {
2134+
PyErr_Format(PyExc_ValueError, "Unsupported timezone "
2135+
"input string \"%s\"", str);
2136+
}
2137+
}
2138+
/* Otherwise assume it's a Python TZInfo, or acts like one */
2139+
else {
2140+
pyEncoder.local = 1;
2141+
pyEncoder.timezone_obj = &timezone_obj;
2142+
}
2143+
}
20772144
}
20782145

20792146
if (odefHandler != NULL && odefHandler != Py_None) {
@@ -2107,4 +2174,8 @@ PyObject *objToJSON(PyObject *Py_UNUSED(self), PyObject *args,
21072174
}
21082175

21092176
return newobj;
2177+
2178+
fail:
2179+
Py_XDECREF(timezone_obj);
2180+
return NULL;
21102181
}

pandas/_libs/tslibs/src/datetime/np_datetime.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This file is derived from NumPy 1.7. See NUMPY_LICENSE.txt
2121
#endif // NPY_NO_DEPRECATED_API
2222

2323
#include <Python.h>
24+
#include <datetime.h>
2425

2526
#include <numpy/arrayobject.h>
2627
#include <numpy/arrayscalars.h>
@@ -158,6 +159,18 @@ npy_int64 get_datetimestruct_days(const npy_datetimestruct *dts) {
158159
return days;
159160
}
160161

162+
/*
163+
* Calculates the minutes offset from the 1970 epoch.
164+
*/
165+
npy_int64 get_datetimestruct_minutes(const npy_datetimestruct *dts)
166+
{
167+
npy_int64 days = get_datetimestruct_days(dts) * 24 * 60;
168+
days += dts->hour * 60;
169+
days += dts->min;
170+
171+
return days;
172+
}
173+
161174
/*
162175
* Modifies '*days_' to be the day offset within the year,
163176
* and returns the year.
@@ -306,6 +319,42 @@ int cmp_npy_datetimestruct(const npy_datetimestruct *a,
306319
return 0;
307320
}
308321

322+
/*
323+
* Gets a tzoffset in minutes by calling the fromutc() function on
324+
* the Python datetime.tzinfo object.
325+
*/
326+
int get_tzoffset_from_pytzinfo(PyObject *timezone_obj, npy_datetimestruct *dts)
327+
{
328+
PyObject *dt, *loc_dt;
329+
npy_datetimestruct loc_dts;
330+
331+
/* Create a Python datetime to give to the timezone object */
332+
dt = PyDateTime_FromDateAndTime((int)dts->year, dts->month, dts->day,
333+
dts->hour, dts->min, 0, 0);
334+
if (dt == NULL) {
335+
return -1;
336+
}
337+
338+
/* Convert the datetime from UTC to local time */
339+
loc_dt = PyObject_CallMethod(timezone_obj, "fromutc", "O", dt);
340+
Py_DECREF(dt);
341+
if (loc_dt == NULL) {
342+
return -1;
343+
}
344+
345+
/* Convert the local datetime into a datetimestruct */
346+
if (convert_pydatetime_to_datetimestruct(loc_dt, &loc_dts) < 0) {
347+
Py_DECREF(loc_dt);
348+
return -1;
349+
}
350+
351+
Py_DECREF(loc_dt);
352+
353+
/* Calculate the tzoffset as the difference between the datetimes */
354+
return (int)(get_datetimestruct_minutes(&loc_dts) -
355+
get_datetimestruct_minutes(dts));
356+
}
357+
309358
/*
310359
*
311360
* Converts a Python datetime.datetime or datetime.date

pandas/_libs/tslibs/src/datetime/np_datetime.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ int is_leapyear(npy_int64 year);
6262
npy_int64
6363
get_datetimestruct_days(const npy_datetimestruct *dts);
6464

65+
/*
66+
* Gets a tzoffset in minutes by calling the fromutc() function on
67+
* the Python datetime.tzinfo object.
68+
*/
69+
int get_tzoffset_from_pytzinfo(PyObject *timezone_obj, npy_datetimestruct *dts);
6570

6671
/*
6772
* Compares two npy_datetimestruct objects chronologically

0 commit comments

Comments
 (0)