Skip to content

Commit fb25c21

Browse files
committed
new test cases for GH#22591
demonstrates loss of precision and requires no UserWarning is raised for non standard frequencies
1 parent 3ab9dbd commit fb25c21

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

pandas/tests/indexes/datetimes/test_scalar_compat.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pandas as pd
1212

1313
from pandas import date_range, Timestamp, DatetimeIndex
14+
from pandas.tseries.frequencies import to_offset
1415

1516

1617
class TestDatetimeIndexOps(object):
@@ -124,7 +125,7 @@ def test_round(self, tz_naive_fixture):
124125
expected = DatetimeIndex(['2016-10-17 12:00:00.001501030'])
125126
tm.assert_index_equal(result, expected)
126127

127-
with tm.assert_produces_warning():
128+
with tm.assert_produces_warning(False):
128129
ts = '2016-10-17 12:00:00.001501031'
129130
DatetimeIndex([ts]).round('1010ns')
130131

@@ -169,6 +170,43 @@ def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
169170
expected = DatetimeIndex(list(expected))
170171
assert expected.equals(result)
171172

173+
@pytest.mark.parametrize('start, index_freq, periods', [
174+
('2018-01-01', '12H', 25),
175+
('2018-01-01 0:0:0.124999', '1ns', 1000),
176+
])
177+
@pytest.mark.parametrize('rounding_freq', [
178+
'2ns', '3ns', '4ns', '5ns', '6ns', '7ns',
179+
'250ns', '500ns', '750ns',
180+
'1us', '19us', '250us', '500us', '750us',
181+
'1s', '2s', '3s',
182+
'12H', '1D',
183+
])
184+
def test_round_int64(self, start, index_freq, periods, rounding_freq):
185+
dt = DatetimeIndex(start=start, freq=index_freq, periods=periods)
186+
unit = to_offset(rounding_freq).nanos
187+
# test floor
188+
result = dt.floor(rounding_freq).asi8
189+
diff = dt.asi8 - result
190+
mod = result % unit
191+
assert (mod == 0).all(), "floor not a %s multiple" % (rounding_freq, )
192+
assert (0 <= diff).all() and (diff < unit).all(), "floor error"
193+
# test ceil
194+
result = dt.ceil(rounding_freq).asi8
195+
diff = result - dt.asi8
196+
mod = result % unit
197+
assert (mod == 0).all(), "ceil not a %s multiple" % (rounding_freq, )
198+
assert (0 <= diff).all() and (diff < unit).all(), "ceil error"
199+
# test round
200+
result = dt.round(rounding_freq).asi8
201+
diff = abs(result - dt.asi8)
202+
mod = result % unit
203+
assert (mod == 0).all(), "round not a %s multiple" % (rounding_freq, )
204+
assert (diff <= unit // 2).all(), "round error"
205+
if unit % 2 == 0:
206+
assert (
207+
result[diff == unit // 2] % 2 == 0
208+
).all(), "round half to even error"
209+
172210
# ----------------------------------------------------------------
173211
# DatetimeIndex.normalize
174212

pandas/tests/scalar/timestamp/test_unary_ops.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from pandas._libs.tslibs import conversion
1414
from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG
1515
from pandas import Timestamp, NaT
16+
from pandas.tseries.frequencies import to_offset
1617

1718

1819
class TestTimestampUnaryOps(object):
@@ -70,7 +71,7 @@ def test_round_subsecond(self):
7071
assert result == expected
7172

7273
def test_round_nonstandard_freq(self):
73-
with tm.assert_produces_warning():
74+
with tm.assert_produces_warning(False):
7475
Timestamp('2016-10-17 12:00:00.001501031').round('1010ns')
7576

7677
def test_round_invalid_arg(self):
@@ -154,6 +155,43 @@ def test_round_dst_border(self, method):
154155
with pytest.raises(pytz.AmbiguousTimeError):
155156
getattr(ts, method)('H', ambiguous='raise')
156157

158+
@pytest.mark.parametrize('timestamp', [
159+
'2018-01-01 0:0:0.124999360',
160+
'2018-01-01 0:0:0.125000367',
161+
'2018-01-01 0:0:0.125500',
162+
'2018-01-01 0:0:0.126500',
163+
'2018-01-01 12:00:00',
164+
'2019-01-01 12:00:00',
165+
])
166+
@pytest.mark.parametrize('freq', [
167+
'2ns', '3ns', '4ns', '5ns', '6ns', '7ns',
168+
'250ns', '500ns', '750ns',
169+
'1us', '19us', '250us', '500us', '750us',
170+
'1s', '2s', '3s',
171+
'1D',
172+
])
173+
def test_round_int64(self, timestamp, freq):
174+
"""check that all rounding modes are accurate to int64 precision
175+
see GH#22591
176+
"""
177+
dt = Timestamp(timestamp)
178+
unit = to_offset(freq).nanos
179+
# test floor
180+
result = dt.floor(freq)
181+
assert result.value % unit == 0, "floor not a %s multiple" % (freq, )
182+
assert 0 <= dt.value - result.value < unit, "floor error"
183+
# test ceil
184+
result = dt.ceil(freq)
185+
assert result.value % unit == 0, "ceil not a %s multiple" % (freq, )
186+
assert 0 <= result.value - dt.value < unit, "ceil error"
187+
# test round
188+
result = dt.round(freq)
189+
assert result.value % unit == 0, "round not a %s multiple" % (freq, )
190+
assert abs(result.value - dt.value) <= unit // 2, "round error"
191+
if unit % 2 == 0 and abs(result.value - dt.value) == unit // 2:
192+
# round half to even
193+
assert result.value // unit % 2 == 0, "round half to even error"
194+
157195
# --------------------------------------------------------------
158196
# Timestamp.replace
159197

0 commit comments

Comments
 (0)