Skip to content

Commit f2aa1d8

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

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

pandas/_libs/tslibs/timestamps.pyx

+30-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:
@@ -2465,41 +2468,63 @@ default 'raise'
24652468
return v
24662469

24672470
if year is not None:
2471+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_Y
24682472
dts.year = validate("year", year)
2473+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_Y
24692474
if month is not None:
2475+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_M
24702476
dts.month = validate("month", month)
2477+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_M
24712478
if day is not None:
2479+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_D
24722480
dts.day = validate("day", day)
2481+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_D
24732482
if hour is not None:
2483+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_h
24742484
dts.hour = validate("hour", hour)
2485+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_h
24752486
if minute is not None:
2487+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_m
24762488
dts.min = validate("minute", minute)
2489+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_m
24772490
if second is not None:
2491+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_s
24782492
dts.sec = validate("second", second)
2493+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_s
24792494
if microsecond is not None:
2495+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ms
24802496
dts.us = validate("microsecond", microsecond)
2497+
if microsecond > 999:
2498+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_us
2499+
else:
2500+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ms
24812501
if nanosecond is not None:
2502+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ns
24822503
dts.ps = validate("nanosecond", nanosecond) * 1000
2504+
rep_reso = NPY_DATETIMEUNIT.NPY_FR_ns
24832505
if tzinfo is not object:
24842506
tzobj = tzinfo
2507+
2508+
if rep_reso < self._creso:
2509+
rep_reso = self._creso
24852510

24862511
# reconstruct & check bounds
24872512
if tzobj is None:
24882513
# We can avoid going through pydatetime paths, which is robust
24892514
# to datetimes outside of pydatetime range.
24902515
ts = _TSObject()
24912516
try:
2492-
ts.value = npy_datetimestruct_to_datetime(self._creso, &dts)
2517+
ts.value = npy_datetimestruct_to_datetime(rep_reso, &dts)
24932518
except OverflowError as err:
24942519
fmt = dts_to_iso_string(&dts)
24952520
raise OutOfBoundsDatetime(
24962521
f"Out of bounds timestamp: {fmt} with frequency '{self.unit}'"
24972522
) from err
24982523
ts.dts = dts
2499-
ts.creso = self._creso
2524+
ts.creso = rep_reso
25002525
ts.fold = fold
25012526
return create_timestamp_from_ts(
2502-
ts.value, dts, tzobj, fold, reso=self._creso
2527+
ts.value, dts, tzobj, fold, reso=rep_reso
25032528
)
25042529

25052530
elif tzobj is not None and treat_tz_as_pytz(tzobj):
@@ -2518,10 +2543,10 @@ default 'raise'
25182543
ts_input = datetime(**kwargs)
25192544

25202545
ts = convert_datetime_to_tsobject(
2521-
ts_input, tzobj, nanos=dts.ps // 1000, reso=self._creso
2546+
ts_input, tzobj, nanos=dts.ps // 1000, reso=rep_reso
25222547
)
25232548
return create_timestamp_from_ts(
2524-
ts.value, dts, tzobj, fold, reso=self._creso
2549+
ts.value, dts, tzobj, fold, reso=rep_reso
25252550
)
25262551

25272552
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)