@@ -310,6 +310,13 @@ def _parse_structured_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> Any:
310
310
raise ValueError (f"Fill_value { fill_value } is not valid for dtype { dtype } ." ) from e
311
311
312
312
313
+ def _parse_timedelta (td : dict [str , Any ]) -> np .timedelta64 :
314
+ if td ["value" ] is None :
315
+ return np .timedelta64 ("NaT" )
316
+ else :
317
+ return np .timedelta64 (int (td ["value" ]), td ["unit" ])
318
+
319
+
313
320
def parse_fill_value (fill_value : Any , dtype : np .dtype [Any ]) -> Any :
314
321
"""
315
322
Parse a potential fill value into a value that is compatible with the provided dtype.
@@ -329,13 +336,18 @@ def parse_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> Any:
329
336
if fill_value is None or dtype .hasobject :
330
337
# Pass through None or if dtype is object
331
338
pass
332
- elif dtype .kind in "M" :
339
+ elif dtype .kind == "M" : # datetime
333
340
# Check for both string "NaT" and the int64 representation of NaT
334
341
if fill_value == "NaT" or fill_value == np .iinfo (np .int64 ).min :
335
342
fill_value = dtype .type ("NaT" )
336
343
else :
337
344
fill_value = np .array (fill_value , dtype = dtype )[()]
338
345
# Fall through for non-NaT datetime/timedelta values (handled below)
346
+ elif dtype .kind == "m" : # timedelta
347
+ if isinstance (fill_value , dict ):
348
+ return _parse_timedelta (fill_value )
349
+ else : # if raw value is passed rather than unit-based serialization
350
+ return np .timedelta64 (fill_value )
339
351
elif dtype .fields is not None :
340
352
# the dtype is structured (has multiple fields), so the fill_value might be a
341
353
# compound value (e.g., a tuple or dict) that needs field-wise processing.
@@ -373,6 +385,14 @@ def parse_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> Any:
373
385
return fill_value
374
386
375
387
388
+ def _serialize_timedelta (td : np .timedelta64 ) -> JSON :
389
+ if np .isnat (td ):
390
+ return {"value" : None , "unit" : None }
391
+ else :
392
+ val , unit = int (td .astype (int )), td .dtype .name .split ("[" )[- 1 ][:- 1 ]
393
+ return {"value" : val , "unit" : unit }
394
+
395
+
376
396
def _serialize_fill_value (fill_value : Any , dtype : np .dtype [Any ]) -> JSON :
377
397
serialized : JSON
378
398
@@ -383,7 +403,9 @@ def _serialize_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> JSON:
383
403
# that mypy isn't aware of. The fact that we have S or V dtype here
384
404
# means we should have a bytes-type fill_value.
385
405
serialized = base64 .standard_b64encode (cast (bytes , fill_value )).decode ("ascii" )
386
- elif isinstance (fill_value , np .datetime64 ):
406
+ elif dtype .kind == "m" :
407
+ serialized = _serialize_timedelta (fill_value )
408
+ elif dtype .kind == "M" :
387
409
serialized = np .datetime_as_string (fill_value )
388
410
elif isinstance (fill_value , numbers .Integral ):
389
411
serialized = int (fill_value )
@@ -423,7 +445,9 @@ def _default_fill_value(dtype: np.dtype[Any]) -> Any:
423
445
return b""
424
446
elif dtype .kind in "UO" :
425
447
return ""
426
- elif dtype .kind in "Mm" :
448
+ elif dtype .kind in "m" : # timedelta64
449
+ return np .timedelta64 ("NaT" )
450
+ elif dtype .kind in "M" : # datetime64
427
451
return dtype .type ("nat" )
428
452
elif dtype .kind == "V" :
429
453
if dtype .fields is not None :
0 commit comments