Skip to content

Commit

Permalink
Adjust docs and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mroeschke committed Jan 24, 2024
1 parent a972881 commit 4b6ab5d
Show file tree
Hide file tree
Showing 65 changed files with 563 additions and 408 deletions.
15 changes: 9 additions & 6 deletions asv_bench/benchmarks/tslibs/timestamp.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from datetime import datetime
from datetime import (
datetime,
timezone,
)
import zoneinfo

import numpy as np
import pytz

from pandas import Timestamp

Expand All @@ -12,7 +15,7 @@ class TimestampConstruction:
def setup(self):
self.npdatetime64 = np.datetime64("2020-01-01 00:00:00")
self.dttime_unaware = datetime(2020, 1, 1, 0, 0, 0)
self.dttime_aware = datetime(2020, 1, 1, 0, 0, 0, 0, pytz.UTC)
self.dttime_aware = datetime(2020, 1, 1, 0, 0, 0, 0, timezone.utc)
self.ts = Timestamp("2020-01-01 00:00:00")

def time_parse_iso8601_no_tz(self):
Expand Down Expand Up @@ -113,7 +116,7 @@ def setup(self, tz):
self.ts = Timestamp("2017-08-25 08:16:14", tz=tz)

def time_replace_tz(self, tz):
self.ts.replace(tzinfo=pytz.timezone("US/Eastern"))
self.ts.replace(tzinfo=zoneinfo.ZoneInfo("US/Eastern"))

def time_replace_None(self, tz):
self.ts.replace(tzinfo=None)
Expand Down Expand Up @@ -144,8 +147,8 @@ def time_ceil(self, tz):

class TimestampAcrossDst:
def setup(self):
dt = datetime(2016, 3, 27, 1)
self.tzinfo = pytz.timezone("CET").localize(dt, is_dst=False).tzinfo
dt = datetime(2016, 3, 27, 1, fold=0)
self.tzinfo = dt.astimezone(zoneinfo.ZoneInfo("Europe/Berlin")).tzinfo
self.ts2 = Timestamp(dt)

def time_replace_across_dst(self):
Expand Down
4 changes: 2 additions & 2 deletions asv_bench/benchmarks/tslibs/tslib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
timedelta,
timezone,
)
import zoneinfo

from dateutil.tz import (
gettz,
tzlocal,
)
import numpy as np
import pytz

try:
from pandas._libs.tslibs import ints_to_pydatetime
Expand All @@ -37,7 +37,7 @@
None,
timezone.utc,
timezone(timedelta(minutes=60)),
pytz.timezone("US/Pacific"),
zoneinfo.ZoneInfo("US/Pacific"),
gettz("Asia/Tokyo"),
tzlocal_obj,
]
Expand Down
5 changes: 3 additions & 2 deletions asv_bench/benchmarks/tslibs/tz_convert.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import timezone

import numpy as np
from pytz import UTC

from pandas._libs.tslibs.tzconversion import tz_localize_to_utc

Expand Down Expand Up @@ -41,7 +42,7 @@ def time_tz_convert_from_utc(self, size, tz):
# dti = DatetimeIndex(self.i8data, tz=tz)
# dti.tz_localize(None)
if old_sig:
tz_convert_from_utc(self.i8data, UTC, tz)
tz_convert_from_utc(self.i8data, timezone.utc, tz)
else:
tz_convert_from_utc(self.i8data, tz)

Expand Down
2 changes: 2 additions & 0 deletions doc/source/reference/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Exceptions and warnings
.. autosummary::
:toctree: api/

errors.AmbiguousTimeError
errors.AbstractMethodError
errors.AttributeConflictWarning
errors.CategoricalConversionWarning
Expand All @@ -45,6 +46,7 @@ Exceptions and warnings
errors.LossySetitemError
errors.MergeError
errors.NoBufferPresent
errors.NonExistentTimeError
errors.NullFrequencyError
errors.NumbaUtilError
errors.NumExprClobberingError
Expand Down
12 changes: 7 additions & 5 deletions doc/source/user_guide/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2326,7 +2326,7 @@ Time zone handling
------------------

pandas provides rich support for working with timestamps in different time
zones using the ``pytz`` and ``dateutil`` libraries or :class:`datetime.timezone`
zones using the ``zoneinfo``, ``pytz``, ``dateutil`` libraries or :class:`datetime.timezone`
objects from the standard library.


Expand All @@ -2343,10 +2343,12 @@ By default, pandas objects are time zone unaware:
To localize these dates to a time zone (assign a particular time zone to a naive date),
you can use the ``tz_localize`` method or the ``tz`` keyword argument in
:func:`date_range`, :class:`Timestamp`, or :class:`DatetimeIndex`.
You can either pass ``pytz`` or ``dateutil`` time zone objects or Olson time zone database strings.
Olson time zone strings will return ``pytz`` time zone objects by default.
You can either pass ``zoneinfo``, ``pytz`` or ``dateutil`` time zone objects or Olson time zone database strings.
Olson time zone strings will return ``zoneinfo.ZoneInfo`` time zone objects by default.
To return ``dateutil`` time zone objects, append ``dateutil/`` before the string.

* For ``zoneinfo``, a list of available timezones are available from
``zoneinfo.available_timezones()``.
* In ``pytz`` you can find a list of common (and less common) time zones using
``from pytz import common_timezones, all_timezones``.
* ``dateutil`` uses the OS time zones so there isn't a fixed list available. For
Expand Down Expand Up @@ -2556,7 +2558,7 @@ Ambiguous times when localizing
because daylight savings time (DST) in a local time zone causes some times to occur
twice within one day ("clocks fall back"). The following options are available:

* ``'raise'``: Raises a ``pytz.AmbiguousTimeError`` (the default behavior)
* ``'raise'``: Raises an :class:`errors.AmbiguousTimeError` (the default behavior)
* ``'infer'``: Attempt to determine the correct offset base on the monotonicity of the timestamps
* ``'NaT'``: Replaces ambiguous times with ``NaT``
* ``bool``: ``True`` represents a DST time, ``False`` represents non-DST time. An array-like of ``bool`` values is supported for a sequence of times.
Expand Down Expand Up @@ -2591,7 +2593,7 @@ A DST transition may also shift the local time ahead by 1 hour creating nonexist
local times ("clocks spring forward"). The behavior of localizing a timeseries with nonexistent times
can be controlled by the ``nonexistent`` argument. The following options are available:

* ``'raise'``: Raises a ``pytz.NonExistentTimeError`` (the default behavior)
* ``'raise'``: Raises a :class:`errors.NonExistentTimeError` (the default behavior)
* ``'NaT'``: Replaces nonexistent times with ``NaT``
* ``'shift_forward'``: Shifts nonexistent times forward to the closest real time
* ``'shift_backward'``: Shifts nonexistent times backward to the closest real time
Expand Down
8 changes: 7 additions & 1 deletion pandas/_libs/tslibs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
__all__ = [
"AmbiguousTimeError",
"NonExistentTimeError",
"dtypes",
"localize_pydatetime",
"NaT",
Expand Down Expand Up @@ -76,7 +78,11 @@
)
from pandas._libs.tslibs.timestamps import Timestamp
from pandas._libs.tslibs.timezones import tz_compare
from pandas._libs.tslibs.tzconversion import tz_convert_from_utc_single
from pandas._libs.tslibs.tzconversion import (
AmbiguousTimeError,
NonExistentTimeError,
tz_convert_from_utc_single,
)
from pandas._libs.tslibs.vectorized import (
dt64arr_to_periodarr,
get_resolution,
Expand Down
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/nattype.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1345,13 +1345,13 @@ default 'raise'
Replace timezone (not a conversion):
>>> import pytz
>>> ts.replace(tzinfo=pytz.timezone('US/Pacific'))
>>> import zoneinfo
>>> ts.replace(tzinfo=zoneinfo.Zoneinfo('US/Pacific'))
Timestamp('2020-03-14 15:32:52.192548651-0700', tz='US/Pacific')
Analogous for ``pd.NaT``:
>>> pd.NaT.replace(tzinfo=pytz.timezone('US/Pacific'))
>>> pd.NaT.replace(tzinfo=zoneinfo.Zoneinfo('US/Pacific'))
NaT
""",
)
Expand Down
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/strptime.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ FUNCTIONS:
strptime -- Calculates the time struct represented by the passed-in string
"""
from datetime import timezone
import zoneinfo

from cpython.datetime cimport (
PyDate_Check,
Expand All @@ -38,7 +39,6 @@ from _thread import allocate_lock as _thread_allocate_lock
import re

import numpy as np
import pytz

cimport numpy as cnp
from numpy cimport (
Expand Down Expand Up @@ -725,7 +725,7 @@ cdef tzinfo _parse_with_format(
week_of_year_start = 0
elif parse_code == 17:
# e.g. val='2011-12-30T00:00:00.000000UTC'; fmt='%Y-%m-%dT%H:%M:%S.%f%Z'
tz = pytz.timezone(found_dict["Z"])
tz = zoneinfo.ZoneInfo(found_dict["Z"])
elif parse_code == 19:
# e.g. val='March 1, 2018 12:00:00+0400'; fmt='%B %d, %Y %H:%M:%S%z'
tz = parse_timezone_directive(found_dict["z"])
Expand Down Expand Up @@ -966,7 +966,7 @@ class TimeRE(_TimeRE):
if key == "Z":
# lazy computation
if self._Z is None:
self._Z = self.__seqToRE(pytz.all_timezones, "Z")
self._Z = self.__seqToRE(zoneinfo.available_timezones(), "Z")
# Note: handling Z is the key difference vs using the stdlib
# _strptime.TimeRE. test_to_datetime_parse_tzname_or_tzoffset with
# fmt='%Y-%m-%d %H:%M:%S %Z' fails with the stdlib version.
Expand Down
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2422,13 +2422,13 @@ default 'raise'
Replace timezone (not a conversion):
>>> import pytz
>>> ts.replace(tzinfo=pytz.timezone('US/Pacific'))
>>> import zoneinfo
>>> ts.replace(tzinfo=zoneinfo.Zoneinfo('US/Pacific'))
Timestamp('2020-03-14 15:32:52.192548651-0700', tz='US/Pacific')
Analogous for ``pd.NaT``:
>>> pd.NaT.replace(tzinfo=pytz.timezone('US/Pacific'))
>>> pd.NaT.replace(tzinfo=zoneinfo.Zoneinfo('US/Pacific'))
NaT
"""

Expand Down
78 changes: 45 additions & 33 deletions pandas/_libs/tslibs/timezones.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ from datetime import (
timezone,
)
import zoneinfo
from zoneinfo import ZoneInfo

from pandas.compat._optional import import_optional_dependency

Expand All @@ -22,8 +21,11 @@ from dateutil.tz import (
tzutc as _dateutil_tzutc,
)
import numpy as np
import pytz
from pytz.tzinfo import BaseTzInfo as _pytz_BaseTzInfo

try:
import pytz
except ImportError:
pytz = None

cimport numpy as cnp
from numpy cimport int64_t
Expand All @@ -39,7 +41,7 @@ from pandas._libs.tslibs.util cimport (

cdef int64_t NPY_NAT = get_nat()
cdef tzinfo utc_stdlib = timezone.utc
cdef tzinfo utc_pytz = pytz.utc
cdef tzinfo utc_pytz = None
cdef tzinfo utc_dateutil_str = dateutil_gettz("UTC") # NB: *not* the same as tzutc()

cdef tzinfo utc_zoneinfo = None
Expand All @@ -50,13 +52,13 @@ cdef tzinfo utc_zoneinfo = None
cdef bint is_utc_zoneinfo(tzinfo tz):
# Workaround for cases with missing tzdata
# https://github.com/pandas-dev/pandas/pull/46425#discussion_r830633025
if tz is None or zoneinfo is None:
if tz is None:
return False

global utc_zoneinfo
if utc_zoneinfo is None:
try:
utc_zoneinfo = ZoneInfo("UTC")
utc_zoneinfo = zoneinfo.ZoneInfo("UTC")
except zoneinfo.ZoneInfoNotFoundError:
return False
# Warn if tzdata is too old, even if there is a system tzdata to alert
Expand All @@ -66,20 +68,31 @@ cdef bint is_utc_zoneinfo(tzinfo tz):
return tz is utc_zoneinfo


cdef bint is_utc_pytz(tzinfo tz):
if tz is None:
return False

global utc_pytz
if utc_pytz is None:
if pytz is None:
return False
utc_pytz = pytz.UTC

return tz is utc_pytz


cpdef inline bint is_utc(tzinfo tz):
return (
tz is utc_pytz
or tz is utc_stdlib
tz is utc_stdlib
or isinstance(tz, _dateutil_tzutc)
or tz is utc_dateutil_str
or is_utc_zoneinfo(tz)
or is_utc_pytz(tz)
)


cdef bint is_zoneinfo(tzinfo tz):
if ZoneInfo is None:
return False
return isinstance(tz, ZoneInfo)
return isinstance(tz, zoneinfo.ZoneInfo)


cdef bint is_tzlocal(tzinfo tz):
Expand Down Expand Up @@ -113,27 +126,26 @@ cpdef inline object get_timezone(tzinfo tz):
raise TypeError("tz argument cannot be None")
if is_utc(tz):
return tz
elif is_zoneinfo(tz):
return tz.key
elif treat_tz_as_dateutil(tz):
if ".tar.gz" in tz._filename:
raise ValueError(
"Bad tz filename. Dateutil on python 3 on windows has a "
"bug which causes tzfile._filename to be the same for all "
"timezone files. Please construct dateutil timezones "
'implicitly by passing a string like "dateutil/Europe'
'/London" when you construct your pandas objects instead '
"of passing a timezone object. See "
"https://github.com/pandas-dev/pandas/pull/7362")
return "dateutil/" + tz._filename
elif treat_tz_as_pytz(tz):
zone = tz.zone
if zone is None:
return tz
return zone
else:
if treat_tz_as_dateutil(tz):
if ".tar.gz" in tz._filename:
raise ValueError(
"Bad tz filename. Dateutil on python 3 on windows has a "
"bug which causes tzfile._filename to be the same for all "
"timezone files. Please construct dateutil timezones "
'implicitly by passing a string like "dateutil/Europe'
'/London" when you construct your pandas objects instead '
"of passing a timezone object. See "
"https://github.com/pandas-dev/pandas/pull/7362")
return "dateutil/" + tz._filename
else:
# tz is a pytz timezone or unknown.
try:
zone = tz.zone
if zone is None:
return tz
return zone
except AttributeError:
return tz
return tz


cpdef inline tzinfo maybe_get_tz(object tz):
Expand Down Expand Up @@ -200,7 +212,7 @@ cdef object tz_cache_key(tzinfo tz):
the same tz file). Also, pytz objects are not always hashable so we use
str(tz) instead.
"""
if isinstance(tz, _pytz_BaseTzInfo):
if pytz is not None and isinstance(tz, pytz.tzinfo.BaseTzInfo):
return tz.zone
elif isinstance(tz, _dateutil_tzfile):
if ".tar.gz" in tz._filename:
Expand Down Expand Up @@ -438,6 +450,6 @@ def tz_standardize(tz: tzinfo) -> tzinfo:
>>> tz_standardize(tz)
<DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>
"""
if treat_tz_as_pytz(tz):
if treat_tz_as_pytz(tz) and pytz is not None:
return pytz.timezone(str(tz))
return tz

0 comments on commit 4b6ab5d

Please sign in to comment.