-
-
Notifications
You must be signed in to change notification settings - Fork 18.7k
implement constructors for TimedeltaArray, DatetimeArray #21803
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 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,13 +6,21 @@ | |
|
||
from pandas._libs import tslib | ||
from pandas._libs.tslib import Timestamp, NaT, iNaT | ||
from pandas._libs.tslibs import conversion, fields, timezones | ||
from pandas._libs.tslibs import ( | ||
conversion, fields, timezones, | ||
resolution as libresolution) | ||
|
||
from pandas.util._decorators import cache_readonly | ||
|
||
from pandas.core.dtypes.common import _NS_DTYPE, is_datetime64tz_dtype | ||
from pandas.core.dtypes.common import ( | ||
_NS_DTYPE, | ||
is_datetime64tz_dtype, | ||
is_datetime64_dtype, | ||
_ensure_int64) | ||
from pandas.core.dtypes.dtypes import DatetimeTZDtype | ||
|
||
from pandas.tseries.frequencies import to_offset, DateOffset | ||
|
||
from .datetimelike import DatetimeLikeArrayMixin | ||
|
||
|
||
|
@@ -66,6 +74,50 @@ class DatetimeArrayMixin(DatetimeLikeArrayMixin): | |
'is_year_end', 'is_leap_year'] | ||
_object_ops = ['weekday_name', 'freq', 'tz'] | ||
|
||
# ----------------------------------------------------------------- | ||
# Constructors | ||
|
||
_attributes = ["freq", "tz"] | ||
|
||
@classmethod | ||
def _simple_new(cls, values, freq=None, tz=None, **kwargs): | ||
""" | ||
we require the we have a dtype compat for the values | ||
if we are passed a non-dtype compat, then coerce using the constructor | ||
""" | ||
|
||
if getattr(values, 'dtype', None) is None: | ||
# empty, but with dtype compat | ||
if values is None: | ||
values = np.empty(0, dtype=_NS_DTYPE) | ||
return cls(values, freq=freq, tz=tz, **kwargs) | ||
values = np.array(values, copy=False) | ||
|
||
if not is_datetime64_dtype(values): | ||
values = _ensure_int64(values).view(_NS_DTYPE) | ||
|
||
result = object.__new__(cls) | ||
result._data = values | ||
result._freq = freq | ||
tz = timezones.maybe_get_tz(tz) | ||
result._tz = timezones.tz_standardize(tz) | ||
return result | ||
|
||
def __new__(cls, values, freq=None, tz=None): | ||
if (freq is not None and not isinstance(freq, DateOffset) and | ||
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. this condition could actually be handled to by 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. Yah, I anticipate most of the existing constructors will be moved from the Index subclasses to the Array subclasses; for now I just need something that works for the cases that are called internally. |
||
freq != 'infer'): | ||
freq = to_offset(freq) | ||
|
||
result = cls._simple_new(values, freq=freq, tz=tz) | ||
if freq == 'infer': | ||
inferred = result.inferred_freq | ||
if inferred: | ||
result.freq = to_offset(inferred) | ||
|
||
# NB: Among other things not yet ported from the DatetimeIndex | ||
# constructor, this does not call _deepcopy_if_needed | ||
return result | ||
|
||
# ----------------------------------------------------------------- | ||
# Descriptive Properties | ||
|
||
|
@@ -116,6 +168,10 @@ def is_normalized(self): | |
""" | ||
return conversion.is_date_array_normalized(self.asi8, self.tz) | ||
|
||
@property # NB: override with cache_readonly in immutable subclasses | ||
def _resolution(self): | ||
return libresolution.resolution(self.asi8, self.tz) | ||
|
||
# ---------------------------------------------------------------- | ||
# Array-like Methods | ||
|
||
|
@@ -207,6 +263,170 @@ def _local_timestamps(self): | |
reverse.put(indexer, np.arange(n)) | ||
return result.take(reverse) | ||
|
||
def tz_convert(self, tz): | ||
""" | ||
Convert tz-aware Datetime Array/Index from one time zone to another. | ||
|
||
Parameters | ||
---------- | ||
tz : string, pytz.timezone, dateutil.tz.tzfile or None | ||
Time zone for time. Corresponding timestamps would be converted | ||
to this time zone of the Datetime Array/Index. A `tz` of None will | ||
convert to UTC and remove the timezone information. | ||
|
||
Returns | ||
------- | ||
normalized : same type as self | ||
|
||
Raises | ||
------ | ||
TypeError | ||
If Datetime Array/Index is tz-naive. | ||
|
||
See Also | ||
-------- | ||
DatetimeIndex.tz : A timezone that has a variable offset from UTC | ||
DatetimeIndex.tz_localize : Localize tz-naive DatetimeIndex to a | ||
given time zone, or remove timezone from a tz-aware DatetimeIndex. | ||
|
||
Examples | ||
-------- | ||
With the `tz` parameter, we can change the DatetimeIndex | ||
to other time zones: | ||
|
||
>>> dti = pd.DatetimeIndex(start='2014-08-01 09:00', | ||
... freq='H', periods=3, tz='Europe/Berlin') | ||
|
||
>>> dti | ||
DatetimeIndex(['2014-08-01 09:00:00+02:00', | ||
'2014-08-01 10:00:00+02:00', | ||
'2014-08-01 11:00:00+02:00'], | ||
dtype='datetime64[ns, Europe/Berlin]', freq='H') | ||
|
||
>>> dti.tz_convert('US/Central') | ||
DatetimeIndex(['2014-08-01 02:00:00-05:00', | ||
'2014-08-01 03:00:00-05:00', | ||
'2014-08-01 04:00:00-05:00'], | ||
dtype='datetime64[ns, US/Central]', freq='H') | ||
|
||
With the ``tz=None``, we can remove the timezone (after converting | ||
to UTC if necessary): | ||
|
||
>>> dti = pd.DatetimeIndex(start='2014-08-01 09:00',freq='H', | ||
... periods=3, tz='Europe/Berlin') | ||
|
||
>>> dti | ||
DatetimeIndex(['2014-08-01 09:00:00+02:00', | ||
'2014-08-01 10:00:00+02:00', | ||
'2014-08-01 11:00:00+02:00'], | ||
dtype='datetime64[ns, Europe/Berlin]', freq='H') | ||
|
||
>>> dti.tz_convert(None) | ||
DatetimeIndex(['2014-08-01 07:00:00', | ||
'2014-08-01 08:00:00', | ||
'2014-08-01 09:00:00'], | ||
dtype='datetime64[ns]', freq='H') | ||
""" | ||
tz = timezones.maybe_get_tz(tz) | ||
|
||
if self.tz is None: | ||
# tz naive, use tz_localize | ||
raise TypeError('Cannot convert tz-naive timestamps, use ' | ||
'tz_localize to localize') | ||
|
||
# No conversion since timestamps are all UTC to begin with | ||
return self._shallow_copy(tz=tz) | ||
|
||
def tz_localize(self, tz, ambiguous='raise', errors='raise'): | ||
""" | ||
Localize tz-naive Datetime Array/Index to tz-aware | ||
Datetime Array/Index. | ||
|
||
This method takes a time zone (tz) naive Datetime Array/Index object | ||
and makes this time zone aware. It does not move the time to another | ||
time zone. | ||
Time zone localization helps to switch from time zone aware to time | ||
zone unaware objects. | ||
|
||
Parameters | ||
---------- | ||
tz : string, pytz.timezone, dateutil.tz.tzfile or None | ||
Time zone to convert timestamps to. Passing ``None`` will | ||
remove the time zone information preserving local time. | ||
ambiguous : str {'infer', 'NaT', 'raise'} or bool array, | ||
default 'raise' | ||
- 'infer' will attempt to infer fall dst-transition hours based on | ||
order | ||
- bool-ndarray where True signifies a DST time, False signifies a | ||
non-DST time (note that this flag is only applicable for | ||
ambiguous times) | ||
- 'NaT' will return NaT where there are ambiguous times | ||
- 'raise' will raise an AmbiguousTimeError if there are ambiguous | ||
times | ||
errors : {'raise', 'coerce'}, default 'raise' | ||
- 'raise' will raise a NonExistentTimeError if a timestamp is not | ||
valid in the specified time zone (e.g. due to a transition from | ||
or to DST time) | ||
- 'coerce' will return NaT if the timestamp can not be converted | ||
to the specified time zone | ||
|
||
.. versionadded:: 0.19.0 | ||
|
||
Returns | ||
------- | ||
result : same type as self | ||
Array/Index converted to the specified time zone. | ||
|
||
Raises | ||
------ | ||
TypeError | ||
If the Datetime Array/Index is tz-aware and tz is not None. | ||
|
||
See Also | ||
-------- | ||
DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from | ||
one time zone to another. | ||
|
||
Examples | ||
-------- | ||
>>> tz_naive = pd.date_range('2018-03-01 09:00', periods=3) | ||
>>> tz_naive | ||
DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00', | ||
'2018-03-03 09:00:00'], | ||
dtype='datetime64[ns]', freq='D') | ||
|
||
Localize DatetimeIndex in US/Eastern time zone: | ||
|
||
>>> tz_aware = tz_naive.tz_localize(tz='US/Eastern') | ||
>>> tz_aware | ||
DatetimeIndex(['2018-03-01 09:00:00-05:00', | ||
'2018-03-02 09:00:00-05:00', | ||
'2018-03-03 09:00:00-05:00'], | ||
dtype='datetime64[ns, US/Eastern]', freq='D') | ||
|
||
With the ``tz=None``, we can remove the time zone information | ||
while keeping the local time (not converted to UTC): | ||
|
||
>>> tz_aware.tz_localize(None) | ||
DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00', | ||
'2018-03-03 09:00:00'], | ||
dtype='datetime64[ns]', freq='D') | ||
""" | ||
if self.tz is not None: | ||
if tz is None: | ||
new_dates = conversion.tz_convert(self.asi8, 'UTC', self.tz) | ||
else: | ||
raise TypeError("Already tz-aware, use tz_convert to convert.") | ||
else: | ||
tz = timezones.maybe_get_tz(tz) | ||
# Convert to UTC | ||
|
||
new_dates = conversion.tz_localize_to_utc(self.asi8, tz, | ||
ambiguous=ambiguous, | ||
errors=errors) | ||
new_dates = new_dates.view(_NS_DTYPE) | ||
return self._shallow_copy(new_dates, tz=tz) | ||
|
||
# ---------------------------------------------------------------- | ||
# Conversion Methods - Vectorized analogues of Timestamp methods | ||
|
||
|
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.
don't need the else here
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.
This is the existing
__getitem__
moved verbatim. Can de-indent in the next pass.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.
thanks!