Skip to content

Commit edebde8

Browse files
fix: replace function now also replaces unit parameter if applicable
1 parent 73fd026 commit edebde8

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

pandas/_libs/tslibs/timestamps.pyx

+22-5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ from pandas._libs.tslibs.conversion cimport (
5959
maybe_localize_tso,
6060
)
6161
from pandas._libs.tslibs.dtypes cimport (
62+
abbrev_to_npy_unit,
6263
npy_unit_to_abbrev,
6364
npy_unit_to_attrname,
6465
periods_per_day,
@@ -2439,10 +2440,12 @@ default 'raise'
24392440
datetime ts_input
24402441
tzinfo_type tzobj
24412442
_TSObject ts
2443+
NPY_DATETIMEUNIT rep_reso
24422444

24432445
# set to naive if needed
24442446
tzobj = self.tzinfo
24452447
value = self._value
2448+
rep_reso = self._creso
24462449

24472450
# GH 37610. Preserve fold when replacing.
24482451
if fold is None:
@@ -2466,40 +2469,54 @@ default 'raise'
24662469

24672470
if year is not None:
24682471
dts.year = validate("year", year)
2472+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_Y
24692473
if month is not None:
24702474
dts.month = validate("month", month)
2475+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_M
24712476
if day is not None:
24722477
dts.day = validate("day", day)
2478+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_D
24732479
if hour is not None:
24742480
dts.hour = validate("hour", hour)
2481+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_h
24752482
if minute is not None:
24762483
dts.min = validate("minute", minute)
2484+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_m
24772485
if second is not None:
24782486
dts.sec = validate("second", second)
2487+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_s
24792488
if microsecond is not None:
24802489
dts.us = validate("microsecond", microsecond)
2490+
if microsecond > 999:
2491+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_us
2492+
else:
2493+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ms
24812494
if nanosecond is not None:
24822495
dts.ps = validate("nanosecond", nanosecond) * 1000
2496+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ns
24832497
if tzinfo is not object:
24842498
tzobj = tzinfo
2499+
2500+
if rep_reso < self._creso:
2501+
rep_reso = self._creso
24852502

24862503
# reconstruct & check bounds
24872504
if tzobj is None:
24882505
# We can avoid going through pydatetime paths, which is robust
24892506
# to datetimes outside of pydatetime range.
24902507
ts = _TSObject()
24912508
try:
2492-
ts.value = npy_datetimestruct_to_datetime(self._creso, &dts)
2509+
ts.value = npy_datetimestruct_to_datetime(rep_reso, &dts)
24932510
except OverflowError as err:
24942511
fmt = dts_to_iso_string(&dts)
24952512
raise OutOfBoundsDatetime(
24962513
f"Out of bounds timestamp: {fmt} with frequency '{self.unit}'"
24972514
) from err
24982515
ts.dts = dts
2499-
ts.creso = self._creso
2516+
ts.creso = rep_reso
25002517
ts.fold = fold
25012518
return create_timestamp_from_ts(
2502-
ts.value, dts, tzobj, fold, reso=self._creso
2519+
ts.value, dts, tzobj, fold, reso=rep_reso
25032520
)
25042521

25052522
elif tzobj is not None and treat_tz_as_pytz(tzobj):
@@ -2518,10 +2535,10 @@ default 'raise'
25182535
ts_input = datetime(**kwargs)
25192536

25202537
ts = convert_datetime_to_tsobject(
2521-
ts_input, tzobj, nanos=dts.ps // 1000, reso=self._creso
2538+
ts_input, tzobj, nanos=dts.ps // 1000, reso=rep_reso
25222539
)
25232540
return create_timestamp_from_ts(
2524-
ts.value, dts, tzobj, fold, reso=self._creso
2541+
ts.value, dts, tzobj, fold, reso=rep_reso
25252542
)
25262543

25272544
def to_julian_date(self) -> np.float64:

pandas/tests/scalar/timestamp/methods/test_replace.py

+14
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,17 @@ def test_replace_preserves_fold(self, fold):
189189
ts_replaced = ts.replace(second=1)
190190

191191
assert ts_replaced.fold == fold
192+
193+
def test_replace_unit(self):
194+
#GH#57749
195+
ts = Timestamp("2023-07-15 23:08:12")
196+
ts1 = Timestamp("2023-07-15 23:08:12.134567")
197+
ts2 = Timestamp("2023-07-15 23:08:12.134567123")
198+
199+
ts = ts.replace(microsecond=ts1.microsecond)
200+
201+
assert ts == ts1
202+
203+
ts = ts.replace(nanosecond=ts2.nanosecond)
204+
205+
assert ts == ts2

0 commit comments

Comments
 (0)