Skip to content

Commit ee4d1a9

Browse files
committed
updated whatsnew and simplified to_timestamp(how='end')
1 parent 3581188 commit ee4d1a9

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

doc/source/whatsnew/v0.23.0.txt

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,60 @@ If installed, we now require:
193193
| openpyxl | 2.4.0 | |
194194
+-----------------+-----------------+----------+
195195

196+
.. _whatsnew_0230.api_breaking.end_time:
197+
198+
Time values in ``dt.end_time`` and ``to_timestamp(how='end')``
199+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
200+
201+
The time values in ``Period`` and ``PeriodIndex`` objects are now adjusted
202+
appropriately when calling :attr:`Series.dt.end_time`, :attr:`Period.end_time`,
203+
:attr:`PeriodIndex.end_time`, :func:`Period.to_timestamp()` with ``how='end'``,
204+
and :func:`PeriodIndex.to_timestamp()` with ``how='end'`` (:issue:`17157`)
205+
206+
Previous Behavior:
207+
208+
.. code-block:: ipython
209+
210+
In [2]: p = pd.Period('2017-01-01', 'D')
211+
In [3]: pi = pd.PeriodIndex([p])
212+
213+
In [4]: pd.Series(p).dt.end_time[0]
214+
Out[4]: Timestamp(2017-01-01 00:00:00)
215+
216+
In [5]: pd.Series(pi).dt.end_time[0]
217+
Out[5]: Timestamp(2017-01-01 00:00:00)
218+
219+
In [6]: p.end_time
220+
Out[6]: Timestamp(2017-01-01 23:59:59.999999999)
221+
222+
In [7]: pi.end_time[0]
223+
Out[7]: Timestamp(2017-01-01 00:00:00)
224+
225+
In [8]: p.to_timestamp(how='end')
226+
Out[8]: Timestamp(2017-01-01 00:00:00)
227+
228+
In [9]: pi.to_timestamp(how='end')[0]
229+
Out[9]: Timestamp(2017-01-01 00:00:00)
230+
231+
Current Behavior
232+
233+
.. ipython:: python
234+
235+
p = pd.Period('2017-01-01', 'D')
236+
pi = pd.PeriodIndex([p])
237+
238+
pd.Series(p).dt.end_time[0]
239+
240+
pd.Series(pi).dt.end_time[0]
241+
242+
p.end_time
243+
244+
pi.end_time[0]
245+
246+
p.to_timestamp(how='end')
247+
248+
pi.to_timestamp(how='end')[0]
249+
196250

197251
Build Changes
198252
^^^^^^^^^^^^^
@@ -318,7 +372,6 @@ Documentation Changes
318372
Bug Fixes
319373
~~~~~~~~~
320374

321-
322375
Conversion
323376
^^^^^^^^^^
324377

@@ -338,7 +391,6 @@ Conversion
338391
- Bug in :class:`Series` floor-division where operating on a scalar ``timedelta`` raises an exception (:issue:`18846`)
339392
- Bug in :class:`FY5253Quarter`, :class:`LastWeekOfMonth` where rollback and rollforward behavior was inconsistent with addition and subtraction behavior (:issue:`18854`)
340393
- Bug in :class:`Index` constructor with ``dtype=CategoricalDtype(...)`` where ``categories`` and ``ordered`` are not maintained (issue:`19032`)
341-
- Bug in :class:`Index` constructor with ``dtype=CategoricalDtype(...)`` where ``categories`` and ``ordered`` are not maintained (issue:`19032`)
342394
- Bug in :class:`Series`` with ``dtype='timedelta64[ns]`` where addition or subtraction of ``TimedeltaIndex`` had results cast to ``dtype='int64'`` (:issue:`17250`)
343395
- Bug in :class:`TimedeltaIndex` where division by a ``Series`` would return a ``TimedeltaIndex`` instead of a ``Series`` (issue:`19042`)
344396
- Bug in :class:`Series` with ``dtype='timedelta64[ns]`` where addition or subtraction of ``TimedeltaIndex`` could return a ``Series`` with an incorrect name (issue:`19043`)

pandas/_libs/tslibs/period.pyx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22
# cython: profile=False
3+
import pandas as pd
34
from datetime import datetime, date, timedelta
45

56
from cpython cimport (
@@ -756,6 +757,10 @@ cdef class _Period(object):
756757
freq = self._maybe_convert_freq(freq)
757758
how = _validate_end_alias(how)
758759

760+
end = how == 'E'
761+
if end:
762+
return (self + 1).to_timestamp(how='start') - pd.Timedelta(1, 'ns')
763+
759764
if freq is None:
760765
base, mult = get_freq_code(self.freq)
761766
freq = get_to_timestamp_base(base)

pandas/core/indexes/period.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from datetime import datetime, timedelta
33
import numpy as np
44
import warnings
5+
import pandas as pd
56

67
from pandas.core import common as com
78
from pandas.core.dtypes.common import (
@@ -656,6 +657,10 @@ def to_timestamp(self, freq=None, how='start'):
656657
"""
657658
how = _validate_end_alias(how)
658659

660+
end = how == 'E'
661+
if end:
662+
return (self + 1).to_timestamp(how='start') - pd.Timedelta(1, 'ns')
663+
659664
if freq is None:
660665
base, mult = _gfc(self.freq)
661666
freq = frequencies.get_to_timestamp_base(base)
@@ -665,16 +670,7 @@ def to_timestamp(self, freq=None, how='start'):
665670
base, mult = _gfc(freq)
666671
new_data = self.asfreq(freq, how)
667672

668-
end = how == 'E'
669-
if end:
670-
indexer = np.where(new_data.notnull())
671-
# move forward one period
672-
new_data._values[indexer] += 1
673-
new_data = period.periodarr_to_dt64arr(new_data._values, base)
674-
# subtract one nanosecond
675-
new_data[indexer] -= 1
676-
else:
677-
new_data = period.periodarr_to_dt64arr(new_data._values, base)
673+
new_data = period.periodarr_to_dt64arr(new_data._values, base)
678674
return DatetimeIndex(new_data, freq='infer', name=self.name)
679675

680676
def _maybe_convert_timedelta(self, other):

pandas/tests/indexes/period/test_tools.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def test_to_timestamp(self):
173173

174174
exp_index = date_range('1/1/2001', end='12/31/2009', freq='A-DEC')
175175
result = series.to_timestamp(how='end')
176+
exp_index = exp_index + pd.Timedelta(1, 'D') - pd.Timedelta(1, 'ns')
176177
tm.assert_index_equal(result.index, exp_index)
177178
assert result.name == 'foo'
178179

@@ -187,16 +188,19 @@ def _get_with_delta(delta, freq='A-DEC'):
187188
delta = timedelta(hours=23)
188189
result = series.to_timestamp('H', 'end')
189190
exp_index = _get_with_delta(delta)
191+
exp_index = exp_index + pd.Timedelta(1, 'h') - pd.Timedelta(1, 'ns')
190192
tm.assert_index_equal(result.index, exp_index)
191193

192194
delta = timedelta(hours=23, minutes=59)
193195
result = series.to_timestamp('T', 'end')
194196
exp_index = _get_with_delta(delta)
197+
exp_index = exp_index + pd.Timedelta(1, 'm') - pd.Timedelta(1, 'ns')
195198
tm.assert_index_equal(result.index, exp_index)
196199

197200
result = series.to_timestamp('S', 'end')
198201
delta = timedelta(hours=23, minutes=59, seconds=59)
199202
exp_index = _get_with_delta(delta)
203+
exp_index = exp_index + pd.Timedelta(1, 's') - pd.Timedelta(1, 'ns')
200204
tm.assert_index_equal(result.index, exp_index)
201205

202206
index = PeriodIndex(freq='H', start='1/1/2001', end='1/2/2001')
@@ -205,6 +209,7 @@ def _get_with_delta(delta, freq='A-DEC'):
205209
exp_index = date_range('1/1/2001 00:59:59', end='1/2/2001 00:59:59',
206210
freq='H')
207211
result = series.to_timestamp(how='end')
212+
exp_index = exp_index + pd.Timedelta(1, 's') - pd.Timedelta(1, 'ns')
208213
tm.assert_index_equal(result.index, exp_index)
209214
assert result.name == 'foo'
210215

@@ -268,6 +273,7 @@ def test_to_timestamp_pi_mult(self):
268273
result = idx.to_timestamp(how='E')
269274
expected = DatetimeIndex(
270275
['2011-02-28', 'NaT', '2011-03-31'], name='idx')
276+
expected = expected + pd.Timedelta(1, 'D') - pd.Timedelta(1, 'ns')
271277
tm.assert_index_equal(result, expected)
272278

273279
def test_to_timestamp_pi_combined(self):
@@ -278,11 +284,13 @@ def test_to_timestamp_pi_combined(self):
278284
tm.assert_index_equal(result, expected)
279285
result = idx.to_timestamp(how='E')
280286
expected = DatetimeIndex(
281-
['2011-01-02 00:59:59', '2011-01-03 01:59:59'], name='idx')
287+
['2011-01-02 01:00:00', '2011-01-03 02:00:00'], name='idx')
288+
expected = expected - pd.Timedelta(1, 'ns')
282289
tm.assert_index_equal(result, expected)
283290
result = idx.to_timestamp(how='E', freq='H')
284291
expected = DatetimeIndex(
285-
['2011-01-02 00:00', '2011-01-03 01:00'], name='idx')
292+
['2011-01-02 01:00', '2011-01-03 02:00'], name='idx')
293+
expected = expected - pd.Timedelta(1, 'ns')
286294
tm.assert_index_equal(result, expected)
287295

288296
def test_to_timestamp_to_period_astype(self):
@@ -324,6 +332,7 @@ def test_period_astype_to_timestamp(self):
324332
tm.assert_index_equal(pi.astype('datetime64[ns]'), exp)
325333

326334
exp = pd.DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'])
335+
exp = exp + pd.Timedelta(1, 'D') - pd.Timedelta(1, 'ns')
327336
tm.assert_index_equal(pi.astype('datetime64[ns]', how='end'), exp)
328337

329338
exp = pd.DatetimeIndex(['2011-01-01', '2011-02-01', '2011-03-01'],
@@ -333,6 +342,7 @@ def test_period_astype_to_timestamp(self):
333342

334343
exp = pd.DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'],
335344
tz='US/Eastern')
345+
exp = exp + pd.Timedelta(1, 'D') - pd.Timedelta(1, 'ns')
336346
res = pi.astype('datetime64[ns, US/Eastern]', how='end')
337347
tm.assert_index_equal(res, exp)
338348

0 commit comments

Comments
 (0)