Skip to content

Commit 4fa44b7

Browse files
authored
BUG: Period[us] start_time off by 1 nanosecond (#31475)
1 parent 9853d0e commit 4fa44b7

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Datetimelike
109109
- :meth:`DatetimeArray.searchsorted`, :meth:`TimedeltaArray.searchsorted`, :meth:`PeriodArray.searchsorted` not recognizing non-pandas scalars and incorrectly raising ``ValueError`` instead of ``TypeError`` (:issue:`30950`)
110110
- Bug in :class:`Timestamp` where constructing :class:`Timestamp` with dateutil timezone less than 128 nanoseconds before daylight saving time switch from winter to summer would result in nonexistent time (:issue:`31043`)
111111
- Bug in :meth:`DataFrame.reindex` and :meth:`Series.reindex` when reindexing with a tz-aware index (:issue:`26683`)
112+
- Bug in :meth:`Period.to_timestamp`, :meth:`Period.start_time` with microsecond frequency returning a timestamp one nanosecond earlier than the correct time (:issue:`31475`)
112113

113114
Timedelta
114115
^^^^^^^^^

pandas/_libs/tslibs/period.pyx

+7-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ PyDateTime_IMPORT
2222
from pandas._libs.tslibs.np_datetime cimport (
2323
npy_datetimestruct, dtstruct_to_dt64, dt64_to_dtstruct,
2424
pandas_datetime_to_datetimestruct, check_dts_bounds,
25-
NPY_DATETIMEUNIT, NPY_FR_D)
25+
NPY_DATETIMEUNIT, NPY_FR_D, NPY_FR_us)
2626

2727
cdef extern from "src/datetime/np_datetime.h":
2828
int64_t npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr,
@@ -1169,7 +1169,12 @@ cdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq) except? -1:
11691169
if ordinal == NPY_NAT:
11701170
return NPY_NAT
11711171

1172-
get_date_info(ordinal, freq, &dts)
1172+
if freq == 11000:
1173+
# Microsecond, avoid get_date_info to prevent floating point errors
1174+
pandas_datetime_to_datetimestruct(ordinal, NPY_FR_us, &dts)
1175+
else:
1176+
get_date_info(ordinal, freq, &dts)
1177+
11731178
check_dts_bounds(&dts)
11741179
return dtstruct_to_dt64(&dts)
11751180

pandas/tests/scalar/period/test_asfreq.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG, _period_code_map
44
from pandas.errors import OutOfBoundsDatetime
55

6-
from pandas import Period, offsets
6+
from pandas import Period, Timestamp, offsets
77

88

99
class TestFreqConversion:
@@ -656,6 +656,23 @@ def test_conv_secondly(self):
656656

657657
assert ival_S.asfreq("S") == ival_S
658658

659+
def test_conv_microsecond(self):
660+
# GH#31475 Avoid floating point errors dropping the start_time to
661+
# before the beginning of the Period
662+
per = Period("2020-01-30 15:57:27.576166", freq="U")
663+
assert per.ordinal == 1580399847576166
664+
665+
start = per.start_time
666+
expected = Timestamp("2020-01-30 15:57:27.576166")
667+
assert start == expected
668+
assert start.value == per.ordinal * 1000
669+
670+
per2 = Period("2300-01-01", "us")
671+
with pytest.raises(OutOfBoundsDatetime, match="2300-01-01"):
672+
per2.start_time
673+
with pytest.raises(OutOfBoundsDatetime, match="2300-01-01"):
674+
per2.end_time
675+
659676
def test_asfreq_mult(self):
660677
# normal freq to mult freq
661678
p = Period(freq="A", year=2007)

0 commit comments

Comments
 (0)