Skip to content

BUG: pandas.DataFrame.last doesn't respect time zone #52131 #52769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
853fed4
components function for datetime data type
jgarba Apr 6, 2023
637b83b
test case - not final
jgarba Apr 6, 2023
eec29ee
datetime components
jgarba Apr 11, 2023
62fa43a
fixes
jgarba Apr 11, 2023
46c4802
more fixes
jgarba Apr 12, 2023
bfa793c
merge
jgarba Apr 12, 2023
5da1cac
more fixes
jgarba Apr 12, 2023
21f2cc1
fixing some errors from pre-commit failure
jgarba Apr 12, 2023
abb5eaa
pre-commit check fixes
jgarba Apr 13, 2023
38d8a19
gerge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 13, 2023
2ace080
ran pre-commit
jgarba Apr 13, 2023
ba44e0e
removing test for now
jgarba Apr 13, 2023
b6dde2d
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 13, 2023
5cf16ae
Merge branch 'main' into jg/44933
jgarba Apr 13, 2023
c7654f1
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 14, 2023
a882650
test cases
jgarba Apr 14, 2023
20f3805
pre-commit run
jgarba Apr 14, 2023
6d7d581
Merge branch 'jg/44933' of https://github.com/jgarba/pandas into jg/4…
jgarba Apr 14, 2023
896e388
whatsnew
jgarba Apr 14, 2023
f325edf
invoke function
jgarba Apr 14, 2023
bdcbdab
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 14, 2023
e88f5e7
making some more fixes
jgarba Apr 16, 2023
d4a5c95
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 16, 2023
b4d5288
pre-commit
jgarba Apr 16, 2023
b64176d
datetimes
jgarba Apr 16, 2023
e966b5e
deleting from datetimes.py
jgarba Apr 18, 2023
6c0cf19
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 18, 2023
78c5ac8
tests passed locally
jgarba Apr 18, 2023
22ef157
fix
jgarba Apr 18, 2023
25f1232
Merge remote-tracking branch 'upstream/main' into jg/44933
jgarba Apr 18, 2023
c00d5a3
pre-commit
jgarba Apr 18, 2023
c60b7fe
Merge remote-tracking branch 'upstream/main' into jg/52131
jgarba Apr 18, 2023
eab110f
jg/52131
jgarba Apr 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.0.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Other
- :class:`DataFrame` created from empty dicts had :attr:`~DataFrame.columns` of dtype ``object``. It is now a :class:`RangeIndex` (:issue:`52404`)
- :class:`Series` created from empty dicts had :attr:`~Series.index` of dtype ``object``. It is now a :class:`RangeIndex` (:issue:`52404`)
- Implemented :meth:`Series.str.split` and :meth:`Series.str.rsplit` for :class:`ArrowDtype` with ``pyarrow.string`` (:issue:`52401`)
- Implemented :meth:`_Timestamp._ensure_components` and :meth:`_Timestamp.components` timestamps.pyx and :meth:`Series.dt.components` in datetimes.py

.. ---------------------------------------------------------------------------
.. _whatsnew_201.contributors:
Expand Down
3 changes: 3 additions & 0 deletions pandas/_libs/tslibs/timestamps.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ cdef class _Timestamp(ABCTimestamp):
cdef readonly:
int64_t _value, nanosecond, year
NPY_DATETIMEUNIT _creso
bint _is_populated # are my components populated
int64_t _y, _month, _d, _h, _m, _s, _us, _ns

cdef bint _get_start_end_field(self, str field, freq)
cdef _get_date_name_field(self, str field, object locale)
Expand All @@ -34,3 +36,4 @@ cdef class _Timestamp(ABCTimestamp):
int op) except -1
cdef bint _compare_mismatched_resos(_Timestamp self, _Timestamp other, int op)
cdef _Timestamp _as_creso(_Timestamp self, NPY_DATETIMEUNIT creso, bint round_ok=*)
cdef _ensure_components(_Timestamp self)
48 changes: 47 additions & 1 deletion pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ construction requirements, we need to do object instantiation in python
shadows the python class, where we do any heavy lifting.
"""

import collections
import warnings

cimport cython
Expand Down Expand Up @@ -128,6 +129,21 @@ from pandas._libs.tslibs.tzconversion cimport (
_zero_time = dt_time(0, 0)
_no_input = object()

# components named tuple
Components = collections.namedtuple(
"Components",
[
"year",
"month",
"day",
"hour",
"minute",
"second",
"microsecond",
"nanosecond",
]
)

# ----------------------------------------------------------------------


Expand Down Expand Up @@ -227,7 +243,6 @@ class MinMaxReso:
# ----------------------------------------------------------------------

cdef class _Timestamp(ABCTimestamp):

# higher than np.ndarray and np.matrix
__array_priority__ = 100
dayofweek = _Timestamp.day_of_week
Expand Down Expand Up @@ -568,6 +583,37 @@ cdef class _Timestamp(ABCTimestamp):
return type(self)(other) - self
return NotImplemented

cdef _ensure_components(_Timestamp self):
"""
compute the components
"""

if self._is_populated:
return

cdef:
npy_datetimestruct dts

pandas_datetime_to_datetimestruct(self._value, self._creso, &dts)
self._y = dts.year
self._month = dts.month
self._d = dts.day
self._h = dts.hour
self._m = dts.min
self._s = dts.sec
self._us = dts.us
self._ns = dts.ps // 1000

self._is_populated = 1

@property
def components(self):
self._ensure_components()
return Components(
self._y, self._month, self._d,
self._h, self._m, self._s, self._us, self._ns
)

# -----------------------------------------------------------------

cdef int64_t _maybe_convert_value_to_local(self):
Expand Down
16 changes: 13 additions & 3 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -9145,9 +9145,19 @@ def last(self, offset) -> Self:

offset = to_offset(offset)

start_date = self.index[-1] - offset
start = self.index.searchsorted(start_date, side="right")
return self.iloc[start:]
if not isinstance(offset, Tick) and offset.is_on_offset(self.index[-1]):
# GH#29623 if first value is end of period, remove offset with n = 1
# before adding the real offset
start_date = start = self.index[-1] - offset.base - offset
else:
start_date = start = self.index[-1] - offset

# Tick-like, e.g. 3 weeks
if isinstance(offset, Tick) and start_date in self.index:
start = self.index.searchsorted(start_date, side="right")
return self.iloc[:start]

return self.loc[:start]

@final
def rank(
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/scalar/test_nat.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def test_nat_iso_format(get_nat):
@pytest.mark.parametrize(
"klass,expected",
[
(Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]),
(Timestamp, ["components", "normalize", "to_julian_date", "to_period", "unit"]),
(
Timedelta,
[
Expand Down
34 changes: 34 additions & 0 deletions pandas/tests/scalar/timestamp/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,40 @@ def test_fields(self, attr, expected, tz):
assert isinstance(result, int)
assert result == expected

# components
tzstr = "dateutil/usr/share/zoneinfo/America/Chicago"
ts = Timestamp(
year=2013,
month=11,
day=3,
hour=1,
minute=0,
fold=1,
second=32,
microsecond=3,
nanosecond=7,
tz=tzstr,
).components

assert ts.year == 2013
assert ts.month == 11
assert ts.day == 3
assert ts.hour == 1
assert ts.minute == 0
assert ts.second == 32
assert ts.microsecond == 3
assert ts.nanosecond == 7

tzstr = "dateutil/usr/share/zoneinfo/America/Detroit"
ts = Timestamp(
year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr
).components
assert ts.year == 2023
assert ts.month == 4
assert ts.day == 14
assert ts.hour == 9
assert ts.minute == 53

@pytest.mark.parametrize("tz", [None, "US/Eastern"])
def test_millisecond_raises(self, tz):
ts = Timestamp("2014-12-31 23:59:00", tz=tz)
Expand Down