@@ -54,7 +54,6 @@ static PyTypeObject *cls_dataframe;
54
54
static PyTypeObject * cls_series ;
55
55
static PyTypeObject * cls_index ;
56
56
static PyTypeObject * cls_nat ;
57
- PyObject * cls_timestamp ;
58
57
PyObject * cls_timedelta ;
59
58
60
59
npy_int64 get_nat (void ) { return NPY_MIN_INT64 ; }
@@ -166,7 +165,6 @@ void *initObjToJSON(void) {
166
165
cls_index = (PyTypeObject * )PyObject_GetAttrString (mod_pandas , "Index" );
167
166
cls_series =
168
167
(PyTypeObject * )PyObject_GetAttrString (mod_pandas , "Series" );
169
- cls_timestamp = PyObject_GetAttrString (mod_pandas , "Timestamp" );
170
168
cls_timedelta = PyObject_GetAttrString (mod_pandas , "Timedelta" );
171
169
Py_DECREF (mod_pandas );
172
170
}
@@ -408,30 +406,25 @@ static char *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *Py_UNUSED(tc),
408
406
return (char * )PyUnicode_AsUTF8AndSize (_obj , (Py_ssize_t * )_outLen );
409
407
}
410
408
411
- /* returns a char* and mutates the pointer to *len */
412
- static char * NpyDateTimeToIso (JSOBJ Py_UNUSED (obj ), JSONTypeContext * tc ,
413
- size_t * len ) {
409
+ /* Converts the int64_t representation of a datetime to ISO; mutates len */
410
+ static char * int64ToIso (int64_t value , NPY_DATETIMEUNIT base , size_t * len ) {
414
411
npy_datetimestruct dts ;
415
412
int ret_code ;
416
- int64_t longVal = GET_TC (tc )-> longValue ;
417
413
418
- pandas_datetime_to_datetimestruct (longVal , NPY_FR_ns , & dts );
414
+ pandas_datetime_to_datetimestruct (value , NPY_FR_ns , & dts );
419
415
420
- NPY_DATETIMEUNIT base = ((PyObjectEncoder * )tc -> encoder )-> datetimeUnit ;
421
416
* len = (size_t )get_datetime_iso_8601_strlen (0 , base );
422
417
char * result = PyObject_Malloc (* len );
423
418
424
419
if (result == NULL ) {
425
420
PyErr_NoMemory ();
426
- ((JSONObjectEncoder * )tc -> encoder )-> errorMsg = "" ;
427
421
return NULL ;
428
422
}
429
423
430
424
ret_code = make_iso_8601_datetime (& dts , result , * len , base );
431
425
if (ret_code != 0 ) {
432
426
PyErr_SetString (PyExc_ValueError ,
433
427
"Could not convert datetime value to string" );
434
- ((JSONObjectEncoder * )tc -> encoder )-> errorMsg = "" ;
435
428
PyObject_Free (result );
436
429
}
437
430
@@ -441,30 +434,33 @@ static char *NpyDateTimeToIso(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc,
441
434
return result ;
442
435
}
443
436
437
+ /* JSON callback. returns a char* and mutates the pointer to *len */
438
+ static char * NpyDateTimeToIsoCallback (JSOBJ Py_UNUSED (unused ), JSONTypeContext * tc ,
439
+ size_t * len ) {
440
+ NPY_DATETIMEUNIT base = ((PyObjectEncoder * )tc -> encoder )-> datetimeUnit ;
441
+ return int64ToIso (GET_TC (tc )-> longValue , base , len );
442
+ }
443
+
444
444
static npy_datetime NpyDateTimeToEpoch (npy_datetime dt , NPY_DATETIMEUNIT base ) {
445
445
scaleNanosecToUnit (& dt , base );
446
446
return dt ;
447
447
}
448
448
449
- static char * PyDateTimeToIso (JSOBJ obj , JSONTypeContext * tc , size_t * len ) {
449
+ /* Convert PyDatetime To ISO C-string. mutates len */
450
+ static char * PyDateTimeToIso (PyDateTime_Date * obj , NPY_DATETIMEUNIT base ,
451
+ size_t * len ) {
450
452
npy_datetimestruct dts ;
451
453
int ret ;
452
454
453
- if (!PyDateTime_Check (obj )) {
454
- // TODO: raise TypeError
455
- }
456
-
457
455
ret = convert_pydatetime_to_datetimestruct (obj , & dts );
458
456
if (ret != 0 ) {
459
457
if (!PyErr_Occurred ()) {
460
458
PyErr_SetString (PyExc_ValueError ,
461
459
"Could not convert PyDateTime to numpy datetime" );
462
460
}
463
- ((JSONObjectEncoder * )tc -> encoder )-> errorMsg = "" ;
464
461
return NULL ;
465
462
}
466
463
467
- NPY_DATETIMEUNIT base = ((PyObjectEncoder * )tc -> encoder )-> datetimeUnit ;
468
464
* len = (size_t )get_datetime_iso_8601_strlen (0 , base );
469
465
char * result = PyObject_Malloc (* len );
470
466
ret = make_iso_8601_datetime (& dts , result , * len , base );
@@ -473,7 +469,6 @@ static char *PyDateTimeToIso(JSOBJ obj, JSONTypeContext *tc, size_t *len) {
473
469
PRINTMARK ();
474
470
PyErr_SetString (PyExc_ValueError ,
475
471
"Could not convert datetime value to string" );
476
- ((JSONObjectEncoder * )tc -> encoder )-> errorMsg = "" ;
477
472
PyObject_Free (result );
478
473
return NULL ;
479
474
}
@@ -484,6 +479,19 @@ static char *PyDateTimeToIso(JSOBJ obj, JSONTypeContext *tc, size_t *len) {
484
479
return result ;
485
480
}
486
481
482
+ /* JSON callback */
483
+ static char * PyDateTimeToIsoCallback (JSOBJ obj , JSONTypeContext * tc ,
484
+ size_t * len ) {
485
+
486
+ if (!PyDateTime_Check (obj )) {
487
+ PyErr_SetString (PyExc_TypeError , "Expected datetime object" );
488
+ return NULL ;
489
+ }
490
+
491
+ NPY_DATETIMEUNIT base = ((PyObjectEncoder * )tc -> encoder )-> datetimeUnit ;
492
+ return PyDateTimeToIso (obj , base , len );
493
+ }
494
+
487
495
static npy_datetime PyDateTimeToEpoch (PyObject * obj , NPY_DATETIMEUNIT base ) {
488
496
npy_datetimestruct dts ;
489
497
int ret ;
@@ -1518,7 +1526,8 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1518
1526
npy_intp num ) {
1519
1527
// NOTE this function steals a reference to labels.
1520
1528
PyObject * item = NULL ;
1521
- npy_intp i , stride , len ;
1529
+ size_t len ;
1530
+ npy_intp i , stride ;
1522
1531
char * * ret ;
1523
1532
char * dataptr , * cLabel ;
1524
1533
int type_num ;
@@ -1559,8 +1568,7 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1559
1568
break ;
1560
1569
}
1561
1570
1562
- // TODO: for any matches on type_num (date and timedeltas) should use a
1563
- // vectorized solution to convert to epoch or iso formats
1571
+ // TODO: vectorized timedelta solution
1564
1572
if (enc -> datetimeIso &&
1565
1573
(type_num == NPY_TIMEDELTA || PyDelta_Check (item ))) {
1566
1574
PyObject * td = PyObject_CallFunction (cls_timedelta , "(O)" , item );
@@ -1583,54 +1591,36 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1583
1591
cLabel = (char * )PyUnicode_AsUTF8 (iso );
1584
1592
Py_DECREF (iso );
1585
1593
len = strlen (cLabel );
1586
- } else if (PyTypeNum_ISDATETIME (type_num ) || PyDateTime_Check (item ) ||
1587
- PyDate_Check (item )) {
1588
- PyObject * ts = PyObject_CallFunction (cls_timestamp , "(O)" , item );
1589
- if (ts == NULL ) {
1590
- Py_DECREF (item );
1591
- NpyArr_freeLabels (ret , num );
1592
- ret = 0 ;
1593
- break ;
1594
+ } else if (PyTypeNum_ISDATETIME (type_num )) {
1595
+ NPY_DATETIMEUNIT base = enc -> datetimeUnit ;
1596
+ npy_int64 longVal ;
1597
+ PyArray_VectorUnaryFunc * castfunc =
1598
+ PyArray_GetCastFunc (PyArray_DescrFromType (type_num ), NPY_INT64 );
1599
+ if (!castfunc ) {
1600
+ PyErr_Format (PyExc_ValueError ,
1601
+ "Cannot cast numpy dtype %d to long" ,
1602
+ enc -> npyType );
1594
1603
}
1595
-
1604
+ castfunc ( dataptr , & longVal , 1 , NULL , NULL );
1596
1605
if (enc -> datetimeIso ) {
1597
- PyObject * iso = PyObject_CallMethod (ts , "isoformat" , NULL );
1598
- Py_DECREF (ts );
1599
- if (iso == NULL ) {
1600
- Py_DECREF (item );
1601
- NpyArr_freeLabels (ret , num );
1602
- ret = 0 ;
1603
- break ;
1606
+ cLabel = int64ToIso (longVal , base , & len );
1607
+ } else {
1608
+ if (!scaleNanosecToUnit (& longVal , base )) {
1609
+ // TODO: This gets hit but somehow doesn't cause errors
1610
+ // need to clean up (elsewhere in module as well)
1604
1611
}
1605
-
1606
- cLabel = (char * )PyUnicode_AsUTF8 (iso );
1607
- Py_DECREF (iso );
1612
+ cLabel = PyObject_Malloc (21 ); // 21 chars for int64
1613
+ sprintf (cLabel , "%" NPY_INT64_FMT , longVal );
1608
1614
len = strlen (cLabel );
1615
+ }
1616
+ } else if (PyDateTime_Check (item ) || PyDate_Check (item )) {
1617
+ NPY_DATETIMEUNIT base = enc -> datetimeUnit ;
1618
+ if (enc -> datetimeIso ) {
1619
+ cLabel = PyDateTimeToIso ((PyDateTime_Date * )item , base , & len );
1609
1620
} else {
1610
- npy_int64 value ;
1611
- // TODO: refactor to not duplicate what goes on in
1612
- // beginTypeContext
1613
- if (PyObject_HasAttrString (ts , "value" )) {
1614
- PRINTMARK ();
1615
- value = get_long_attr (ts , "value" );
1616
- } else {
1617
- PRINTMARK ();
1618
- value = total_seconds (ts ) *
1619
- 1000000000LL ; // nanoseconds per second
1620
- }
1621
- Py_DECREF (ts );
1622
-
1623
- NPY_DATETIMEUNIT unit = enc -> datetimeUnit ;
1624
- if (scaleNanosecToUnit (& value , unit ) != 0 ) {
1625
- Py_DECREF (item );
1626
- NpyArr_freeLabels (ret , num );
1627
- ret = 0 ;
1628
- break ;
1629
- }
1630
-
1631
- char buf [21 ] = {0 }; // 21 chars for 2**63 as string
1632
- cLabel = buf ;
1633
- sprintf (buf , "%" NPY_INT64_FMT , value );
1621
+ cLabel = PyObject_Malloc (21 ); // 21 chars for int64
1622
+ sprintf (cLabel , "%" NPY_DATETIME_FMT ,
1623
+ PyDateTimeToEpoch (item , base ));
1634
1624
len = strlen (cLabel );
1635
1625
}
1636
1626
} else { // Fallback to string representation
@@ -1740,7 +1730,7 @@ void Object_beginTypeContext(JSOBJ _obj, JSONTypeContext *tc) {
1740
1730
1741
1731
if (enc -> datetimeIso ) {
1742
1732
PRINTMARK ();
1743
- pc -> PyTypeToUTF8 = NpyDateTimeToIso ;
1733
+ pc -> PyTypeToUTF8 = NpyDateTimeToIsoCallback ;
1744
1734
// Currently no way to pass longVal to iso function, so use
1745
1735
// state management
1746
1736
GET_TC (tc )-> longValue = longVal ;
@@ -1815,7 +1805,7 @@ void Object_beginTypeContext(JSOBJ _obj, JSONTypeContext *tc) {
1815
1805
PRINTMARK ();
1816
1806
if (enc -> datetimeIso ) {
1817
1807
PRINTMARK ();
1818
- pc -> PyTypeToUTF8 = PyDateTimeToIso ;
1808
+ pc -> PyTypeToUTF8 = PyDateTimeToIsoCallback ;
1819
1809
tc -> type = JT_UTF8 ;
1820
1810
} else {
1821
1811
PRINTMARK ();
@@ -1841,7 +1831,7 @@ void Object_beginTypeContext(JSOBJ _obj, JSONTypeContext *tc) {
1841
1831
PRINTMARK ();
1842
1832
if (enc -> datetimeIso ) {
1843
1833
PRINTMARK ();
1844
- pc -> PyTypeToUTF8 = PyDateTimeToIso ;
1834
+ pc -> PyTypeToUTF8 = PyDateTimeToIsoCallback ;
1845
1835
tc -> type = JT_UTF8 ;
1846
1836
} else {
1847
1837
PRINTMARK ();
0 commit comments