@@ -456,8 +456,8 @@ static char *PyDateTimeToIso(PyDateTime_Date *obj, NPY_DATETIMEUNIT base,
456
456
static char * PyDateTimeToIsoCallback (JSOBJ obj , JSONTypeContext * tc ,
457
457
size_t * len ) {
458
458
459
- if (!PyDateTime_Check (obj )) {
460
- PyErr_SetString (PyExc_TypeError , "Expected datetime object" );
459
+ if (!PyDate_Check (obj )) {
460
+ PyErr_SetString (PyExc_TypeError , "Expected date object" );
461
461
return NULL ;
462
462
}
463
463
@@ -469,7 +469,7 @@ static npy_datetime PyDateTimeToEpoch(PyObject *obj, NPY_DATETIMEUNIT base) {
469
469
npy_datetimestruct dts ;
470
470
int ret ;
471
471
472
- if (!PyDateTime_Check (obj )) {
472
+ if (!PyDate_Check (obj )) {
473
473
// TODO: raise TypeError
474
474
}
475
475
PyDateTime_Date * dt = (PyDateTime_Date * )obj ;
@@ -1504,6 +1504,7 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1504
1504
char * * ret ;
1505
1505
char * dataptr , * cLabel ;
1506
1506
int type_num ;
1507
+ NPY_DATETIMEUNIT base = enc -> datetimeUnit ;
1507
1508
PRINTMARK ();
1508
1509
1509
1510
if (!labels ) {
@@ -1541,60 +1542,85 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1541
1542
break ;
1542
1543
}
1543
1544
1544
- // TODO: vectorized timedelta solution
1545
- if (enc -> datetimeIso &&
1546
- (type_num == NPY_TIMEDELTA || PyDelta_Check (item ))) {
1547
- PyObject * td = PyObject_CallFunction (cls_timedelta , "(O)" , item );
1548
- if (td == NULL ) {
1549
- Py_DECREF (item );
1550
- NpyArr_freeLabels (ret , num );
1551
- ret = 0 ;
1552
- break ;
1553
- }
1554
-
1555
- PyObject * iso = PyObject_CallMethod (td , "isoformat" , NULL );
1556
- Py_DECREF (td );
1557
- if (iso == NULL ) {
1558
- Py_DECREF (item );
1559
- NpyArr_freeLabels (ret , num );
1560
- ret = 0 ;
1561
- break ;
1562
- }
1563
-
1564
- cLabel = (char * )PyUnicode_AsUTF8 (iso );
1565
- Py_DECREF (iso );
1566
- len = strlen (cLabel );
1567
- } else if (PyTypeNum_ISDATETIME (type_num )) {
1568
- NPY_DATETIMEUNIT base = enc -> datetimeUnit ;
1569
- npy_int64 longVal ;
1545
+ int is_datetimelike = 0 ;
1546
+ npy_int64 nanosecVal ;
1547
+ if (PyTypeNum_ISDATETIME (type_num )) {
1548
+ is_datetimelike = 1 ;
1570
1549
PyArray_VectorUnaryFunc * castfunc =
1571
1550
PyArray_GetCastFunc (PyArray_DescrFromType (type_num ), NPY_INT64 );
1572
1551
if (!castfunc ) {
1573
1552
PyErr_Format (PyExc_ValueError ,
1574
1553
"Cannot cast numpy dtype %d to long" ,
1575
1554
enc -> npyType );
1576
1555
}
1577
- castfunc (dataptr , & longVal , 1 , NULL , NULL );
1578
- if (enc -> datetimeIso ) {
1579
- cLabel = int64ToIso (longVal , base , & len );
1556
+ castfunc (dataptr , & nanosecVal , 1 , NULL , NULL );
1557
+ } else if (PyDate_Check (item ) || PyDelta_Check (item )) {
1558
+ is_datetimelike = 1 ;
1559
+ if (PyObject_HasAttrString (item , "value" )) {
1560
+ nanosecVal = get_long_attr (item , "value" );
1580
1561
} else {
1581
- if (!scaleNanosecToUnit (& longVal , base )) {
1582
- // TODO: This gets hit but somehow doesn't cause errors
1583
- // need to clean up (elsewhere in module as well)
1562
+ if (PyDelta_Check (item )) {
1563
+ nanosecVal = total_seconds (item ) *
1564
+ 1000000000LL ; // nanoseconds per second
1565
+ } else {
1566
+ // datetime.* objects don't follow above rules
1567
+ nanosecVal = PyDateTimeToEpoch (item , NPY_FR_ns );
1584
1568
}
1585
- cLabel = PyObject_Malloc (21 ); // 21 chars for int64
1586
- sprintf (cLabel , "%" NPY_INT64_FMT , longVal );
1587
- len = strlen (cLabel );
1588
1569
}
1589
- } else if (PyDateTime_Check (item ) || PyDate_Check (item )) {
1590
- NPY_DATETIMEUNIT base = enc -> datetimeUnit ;
1591
- if (enc -> datetimeIso ) {
1592
- cLabel = PyDateTimeToIso ((PyDateTime_Date * )item , base , & len );
1570
+ }
1571
+
1572
+ if (is_datetimelike ) {
1573
+ if (nanosecVal == get_nat ()) {
1574
+ len = 5 ; // TODO: shouldn't require extra space for terminator
1575
+ cLabel = PyObject_Malloc (len );
1576
+ strncpy (cLabel , "null" , len );
1593
1577
} else {
1594
- cLabel = PyObject_Malloc (21 ); // 21 chars for int64
1595
- sprintf (cLabel , "%" NPY_DATETIME_FMT ,
1596
- PyDateTimeToEpoch (item , base ));
1597
- len = strlen (cLabel );
1578
+ if (enc -> datetimeIso ) {
1579
+ // TODO: Vectorized Timedelta function
1580
+ if ((type_num == NPY_TIMEDELTA ) || (PyDelta_Check (item ))) {
1581
+ PyObject * td =
1582
+ PyObject_CallFunction (cls_timedelta , "(O)" , item );
1583
+ if (td == NULL ) {
1584
+ Py_DECREF (item );
1585
+ NpyArr_freeLabels (ret , num );
1586
+ ret = 0 ;
1587
+ break ;
1588
+ }
1589
+
1590
+ PyObject * iso =
1591
+ PyObject_CallMethod (td , "isoformat" , NULL );
1592
+ Py_DECREF (td );
1593
+ if (iso == NULL ) {
1594
+ Py_DECREF (item );
1595
+ NpyArr_freeLabels (ret , num );
1596
+ ret = 0 ;
1597
+ break ;
1598
+ }
1599
+
1600
+ len = strlen (PyUnicode_AsUTF8 (iso ));
1601
+ cLabel = PyObject_Malloc (len + 1 );
1602
+ memcpy (cLabel , PyUnicode_AsUTF8 (iso ), len + 1 );
1603
+ Py_DECREF (iso );
1604
+ } else {
1605
+ if (type_num == NPY_DATETIME ) {
1606
+ cLabel = int64ToIso (nanosecVal , base , & len );
1607
+ } else {
1608
+ cLabel = PyDateTimeToIso ((PyDateTime_Date * )item ,
1609
+ base , & len );
1610
+ }
1611
+ }
1612
+ if (cLabel == NULL ) {
1613
+ Py_DECREF (item );
1614
+ NpyArr_freeLabels (ret , num );
1615
+ ret = 0 ;
1616
+ break ;
1617
+ }
1618
+ } else {
1619
+ cLabel = PyObject_Malloc (21 ); // 21 chars for int64
1620
+ sprintf (cLabel , "%" NPY_DATETIME_FMT ,
1621
+ NpyDateTimeToEpoch (nanosecVal , base ));
1622
+ len = strlen (cLabel );
1623
+ }
1598
1624
}
1599
1625
} else { // Fallback to string representation
1600
1626
PyObject * str = PyObject_Str (item );
@@ -1615,6 +1641,10 @@ char **NpyArr_encodeLabels(PyArrayObject *labels, PyObjectEncoder *enc,
1615
1641
ret [i ] = PyObject_Malloc (len + 1 );
1616
1642
memcpy (ret [i ], cLabel , len + 1 );
1617
1643
1644
+ if (is_datetimelike ) {
1645
+ PyObject_Free (cLabel );
1646
+ }
1647
+
1618
1648
if (PyErr_Occurred ()) {
1619
1649
NpyArr_freeLabels (ret , num );
1620
1650
ret = 0 ;
0 commit comments