ENH: Timestamp.replace support non-nano (#47312) · pandas-dev/pandas@87da500 (original) (raw)
`@@ -31,6 +31,7 @@ from cpython.datetime cimport (
`
31
31
`import_datetime()
`
32
32
``
33
33
`from pandas._libs.tslibs.base cimport ABCTimestamp
`
``
34
`+
from pandas._libs.tslibs.dtypes cimport periods_per_second
`
34
35
`from pandas._libs.tslibs.np_datetime cimport (
`
35
36
` NPY_DATETIMEUNIT,
`
36
37
` NPY_FR_ns,
`
`@@ -40,11 +41,14 @@ from pandas._libs.tslibs.np_datetime cimport (
`
40
41
` dtstruct_to_dt64,
`
41
42
` get_datetime64_unit,
`
42
43
` get_datetime64_value,
`
``
44
`+
get_implementation_bounds,
`
43
45
` get_unit_from_dtype,
`
44
46
` npy_datetime,
`
45
47
` npy_datetimestruct,
`
``
48
`+
npy_datetimestruct_to_datetime,
`
46
49
` pandas_datetime_to_datetimestruct,
`
47
50
` pydatetime_to_dt64,
`
``
51
`+
pydatetime_to_dtstruct,
`
48
52
` string_to_dts,
`
49
53
`)
`
50
54
``
`@@ -307,11 +311,15 @@ cdef maybe_localize_tso(_TSObject obj, tzinfo tz, NPY_DATETIMEUNIT reso):
`
307
311
`if obj.value != NPY_NAT:
`
308
312
`# check_overflows needs to run after _localize_tso
`
309
313
` check_dts_bounds(&obj.dts, reso)
`
310
``
`-
check_overflows(obj)
`
``
314
`+
check_overflows(obj, reso)
`
311
315
``
312
316
``
313
``
`-
cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
`
314
``
`-
int32_t nanos=0):
`
``
317
`+
cdef _TSObject convert_datetime_to_tsobject(
`
``
318
`+
datetime ts,
`
``
319
`+
tzinfo tz,
`
``
320
`+
int32_t nanos=0,
`
``
321
`+
NPY_DATETIMEUNIT reso=NPY_FR_ns,
`
``
322
`+
):
`
315
323
`"""
`
316
324
`` Convert a datetime (or Timestamp) input ts
, along with optional timezone
``
317
325
`` object tz
to a _TSObject.
``
`@@ -327,13 +335,15 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
`
327
335
` timezone for the timezone-aware output
`
328
336
` nanos : int32_t, default is 0
`
329
337
` nanoseconds supplement the precision of the datetime input ts
`
``
338
`+
reso : NPY_DATETIMEUNIT, default NPY_FR_ns
`
330
339
``
331
340
` Returns
`
332
341
` -------
`
333
342
` obj : _TSObject
`
334
343
`"""
`
335
344
` cdef:
`
336
345
` _TSObject obj = _TSObject()
`
``
346
`+
int64_t pps
`
337
347
``
338
348
` obj.fold = ts.fold
`
339
349
`if tz is not None:
`
`@@ -342,34 +352,35 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
`
342
352
`if ts.tzinfo is not None:
`
343
353
`# Convert the current timezone to the passed timezone
`
344
354
` ts = ts.astimezone(tz)
`
345
``
`-
obj.value = pydatetime_to_dt64(ts, &obj.dts)
`
``
355
`+
pydatetime_to_dtstruct(ts, &obj.dts)
`
346
356
` obj.tzinfo = ts.tzinfo
`
347
357
`elif not is_utc(tz):
`
348
358
` ts = _localize_pydatetime(ts, tz)
`
349
``
`-
obj.value = pydatetime_to_dt64(ts, &obj.dts)
`
``
359
`+
pydatetime_to_dtstruct(ts, &obj.dts)
`
350
360
` obj.tzinfo = ts.tzinfo
`
351
361
`else:
`
352
362
`# UTC
`
353
``
`-
obj.value = pydatetime_to_dt64(ts, &obj.dts)
`
``
363
`+
pydatetime_to_dtstruct(ts, &obj.dts)
`
354
364
` obj.tzinfo = tz
`
355
365
`else:
`
356
``
`-
obj.value = pydatetime_to_dt64(ts, &obj.dts)
`
``
366
`+
pydatetime_to_dtstruct(ts, &obj.dts)
`
357
367
` obj.tzinfo = ts.tzinfo
`
358
368
``
359
``
`-
if obj.tzinfo is not None and not is_utc(obj.tzinfo):
`
360
``
`-
offset = get_utcoffset(obj.tzinfo, ts)
`
361
``
`-
obj.value -= int(offset.total_seconds() * 1e9)
`
362
``
-
363
369
`if isinstance(ts, ABCTimestamp):
`
364
``
`-
obj.value += ts.nanosecond
`
365
370
` obj.dts.ps = ts.nanosecond * 1000
`
366
371
``
367
372
`if nanos:
`
368
``
`-
obj.value += nanos
`
369
373
` obj.dts.ps = nanos * 1000
`
370
374
``
371
``
`-
check_dts_bounds(&obj.dts)
`
372
``
`-
check_overflows(obj)
`
``
375
`+
obj.value = npy_datetimestruct_to_datetime(reso, &obj.dts)
`
``
376
+
``
377
`+
if obj.tzinfo is not None and not is_utc(obj.tzinfo):
`
``
378
`+
offset = get_utcoffset(obj.tzinfo, ts)
`
``
379
`+
pps = periods_per_second(reso)
`
``
380
`+
obj.value -= int(offset.total_seconds() * pps)
`
``
381
+
``
382
`+
check_dts_bounds(&obj.dts, reso)
`
``
383
`+
check_overflows(obj, reso)
`
373
384
`return obj
`
374
385
``
375
386
``
`@@ -401,7 +412,7 @@ cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
`
401
412
` obj.tzinfo = pytz.FixedOffset(tzoffset)
`
402
413
` obj.value = tz_localize_to_utc_single(value, obj.tzinfo)
`
403
414
`if tz is None:
`
404
``
`-
check_overflows(obj)
`
``
415
`+
check_overflows(obj, NPY_FR_ns)
`
405
416
`return obj
`
406
417
``
407
418
` cdef:
`
`@@ -515,13 +526,14 @@ cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, str unit,
`
515
526
`return convert_datetime_to_tsobject(dt, tz)
`
516
527
``
517
528
``
518
``
`-
cdef inline check_overflows(_TSObject obj):
`
``
529
`+
cdef inline check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso=NPY_FR_ns):
`
519
530
`"""
`
520
531
` Check that we haven't silently overflowed in timezone conversion
`
521
532
``
522
533
` Parameters
`
523
534
` ----------
`
524
535
` obj : _TSObject
`
``
536
`+
reso : NPY_DATETIMEUNIT, default NPY_FR_ns
`
525
537
``
526
538
` Returns
`
527
539
` -------
`
`@@ -532,15 +544,20 @@ cdef inline check_overflows(_TSObject obj):
`
532
544
` OutOfBoundsDatetime
`
533
545
`"""
`
534
546
`# GH#12677
`
535
``
`-
if obj.dts.year == 1677:
`
``
547
`+
cdef:
`
``
548
`+
npy_datetimestruct lb, ub
`
``
549
+
``
550
`+
get_implementation_bounds(reso, &lb, &ub)
`
``
551
+
``
552
`+
if obj.dts.year == lb.year:
`
536
553
`if not (obj.value < 0):
`
537
554
`from pandas._libs.tslibs.timestamps import Timestamp
`
538
555
` fmt = (f"{obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
`
539
556
` f"{obj.dts.hour:02d}:{obj.dts.min:02d}:{obj.dts.sec:02d}")
`
540
557
`raise OutOfBoundsDatetime(
`
541
558
` f"Converting {fmt} underflows past {Timestamp.min}"
`
542
559
` )
`
543
``
`-
elif obj.dts.year == 2262:
`
``
560
`+
elif obj.dts.year == ub.year:
`
544
561
`if not (obj.value > 0):
`
545
562
`from pandas._libs.tslibs.timestamps import Timestamp
`
546
563
` fmt = (f"{obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
`