-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
ENH/BUG: Add is_dst method to DatetimeIndex and Timestamp to solve AmbiguousTimeError #22560
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
Changes from 9 commits
be2b6dd
18f8611
442f888
254b5a2
7c73d0a
b0938d5
641e295
820df35
9304b50
83109eb
627bb19
c03792e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,7 +108,8 @@ def _p_tz_cache_key(tz): | |
return tz_cache_key(tz) | ||
|
||
|
||
# Timezone data caches, key is the pytz string or dateutil file name. | ||
# Timezone data (UTC offset) caches | ||
# key is the pytz string or dateutil file name. | ||
dst_cache = {} | ||
|
||
|
||
|
@@ -186,16 +187,28 @@ cdef object get_utc_trans_times_from_dateutil_tz(object tz): | |
return new_trans | ||
|
||
|
||
cdef int64_t[:] unbox_utcoffsets(object transinfo): | ||
cdef int64_t[:] unbox_utcoffsets(object transinfo, dst): | ||
""" | ||
Unpack the offset information from pytz timezone objects | ||
|
||
Parameters | ||
---------- | ||
transinfo : list of tuples | ||
Each tuple contains (UTC offset, DST offset, tz abbreviation) | ||
dst : boolean | ||
True returns an array of the DST offsets | ||
False returns an array of UTC offsets | ||
""" | ||
cdef: | ||
Py_ssize_t i, sz | ||
int64_t[:] arr | ||
int key | ||
|
||
sz = len(transinfo) | ||
arr = np.empty(sz, dtype='i8') | ||
|
||
key = int(dst) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you may not need this if typing bint |
||
for i in range(sz): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a comment about what you are extracting |
||
arr[i] = int(transinfo[i][0].total_seconds()) * 1000000000 | ||
arr[i] = int(transinfo[i][key].total_seconds()) * 1000000000 | ||
|
||
return arr | ||
|
||
|
@@ -204,9 +217,23 @@ cdef int64_t[:] unbox_utcoffsets(object transinfo): | |
# Daylight Savings | ||
|
||
|
||
cdef object get_dst_info(object tz): | ||
cdef object get_dst_info(object tz, dst): | ||
""" | ||
return a tuple of : | ||
Return DST info from a timezone | ||
|
||
Parameters | ||
---------- | ||
tz : object | ||
timezone | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we accept only timezone objects and not strings here? |
||
dst : bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type with bitn |
||
True returns the DST specific offset and will NOT store the results in | ||
dst_cache. dst_cache is reserved for caching UTC offsets. | ||
False returns the UTC offset | ||
Specific for pytz timezones only | ||
|
||
Returns | ||
------- | ||
tuple | ||
(UTC times of DST transitions, | ||
UTC offsets in microseconds corresponding to DST transitions, | ||
string of type of transitions) | ||
|
@@ -221,7 +248,7 @@ cdef object get_dst_info(object tz): | |
np.array([num], dtype=np.int64), | ||
None) | ||
|
||
if cache_key not in dst_cache: | ||
if cache_key not in dst_cache or dst: | ||
if treat_tz_as_pytz(tz): | ||
trans = np.array(tz._utc_transition_times, dtype='M8[ns]') | ||
trans = trans.view('i8') | ||
|
@@ -230,7 +257,7 @@ cdef object get_dst_info(object tz): | |
trans[0] = NPY_NAT + 1 | ||
except Exception: | ||
pass | ||
deltas = unbox_utcoffsets(tz._transition_info) | ||
deltas = unbox_utcoffsets(tz._transition_info, dst) | ||
typ = 'pytz' | ||
|
||
elif treat_tz_as_dateutil(tz): | ||
|
@@ -273,11 +300,48 @@ cdef object get_dst_info(object tz): | |
deltas = np.array([num], dtype=np.int64) | ||
typ = 'static' | ||
|
||
if dst: | ||
return trans, deltas, typ | ||
dst_cache[cache_key] = (trans, deltas, typ) | ||
|
||
return dst_cache[cache_key] | ||
|
||
|
||
def is_dst(int64_t[:] values, object tz): | ||
""" | ||
Return a boolean array indicating whether each epoch timestamp is in | ||
daylight savings time with respect with the passed timezone. | ||
|
||
Parameters | ||
---------- | ||
values : ndarray | ||
i8 representation of the datetimes | ||
tz : object | ||
timezone | ||
|
||
Returns | ||
------- | ||
ndarray of booleans | ||
True indicates daylight savings time | ||
""" | ||
cdef: | ||
Py_ssize_t n = len(values) | ||
object typ | ||
|
||
result = np.zeros(n, dtype=bool) | ||
if tz is None: | ||
return result | ||
transitions, offsets, typ = get_dst_info(tz, True) | ||
offsets = np.array(offsets) | ||
# Fixed timezone offsets do not have DST transitions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new line here |
||
if typ not in {'pytz', 'dateutil'}: | ||
return result | ||
positions = transitions.searchsorted(values, side='right') - 1 | ||
# DST has nonzero offset | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
result = offsets[positions] != 0 | ||
return result | ||
|
||
|
||
def infer_tzinfo(start, end): | ||
if start is not None and end is not None: | ||
tz = start.tzinfo | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1012,6 +1012,22 @@ def test_iteration_preserves_nanoseconds(self, tz): | |
for i, ts in enumerate(index): | ||
assert ts == index[i] | ||
|
||
@pytest.mark.parametrize('arg, expected_arg', [ | ||
[[], []], | ||
[date_range('2018-11-04', periods=4, freq='H', tz='US/Pacific'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you try with a dateutil as well (I think we default to pytz) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pganssle Do dateutil timezones provide a way to access the possible daylight savings time shifts like
Context: I am trying to create a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mroeschke Yes and no. Yes in the sense that this is Python and obviously That said:
|
||
[True, True, False, False]], | ||
[date_range('2018-11-04', periods=4, freq='H'), | ||
[False] * 4], | ||
[date_range('2018-11-04', periods=4, freq='H', tz=pytz.FixedOffset(3)), | ||
[False] * 4], | ||
[[pd.NaT], [False]] | ||
]) | ||
def test_is_dst(self, arg, expected_arg): | ||
dti = DatetimeIndex(arg) | ||
result = dti.is_dst() | ||
expected = Index(expected_arg) | ||
tm.assert_index_equal(result, expected) | ||
|
||
|
||
class TestDateRange(object): | ||
"""Tests for date_range with timezones""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add bint to type dst