Skip to content

Commit 0f8ecf6

Browse files
committed
refactor: deprecate pytz in favor of builtin zoneinfo
1 parent 603ede0 commit 0f8ecf6

17 files changed

Lines changed: 545 additions & 411 deletions

ci/requirements-py3.10-min.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ dependencies:
99
- pytest-mock
1010
- pytest-timeout
1111
- python=3.10
12-
- pytz
1312
- requests
1413
- pip:
1514
- h5py==3.6.0

ci/requirements-py3.10.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ dependencies:
1919
- pytest-rerunfailures
2020
- conda-forge::pytest-remotedata # version in default channel is old
2121
- python=3.10
22-
- pytz
2322
- requests
2423
- scipy >= 1.7.2
2524
- statsmodels

ci/requirements-py3.11.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ dependencies:
1919
- pytest-rerunfailures
2020
- conda-forge::pytest-remotedata # version in default channel is old
2121
- python=3.11
22-
- pytz
2322
- requests
2423
- scipy >= 1.7.2
2524
- statsmodels

ci/requirements-py3.12.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ dependencies:
1919
- pytest-rerunfailures
2020
- conda-forge::pytest-remotedata # version in default channel is old
2121
- python=3.12
22-
- pytz
2322
- requests
2423
- scipy >= 1.7.2
2524
- statsmodels

ci/requirements-py3.13.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ dependencies:
1919
- pytest-rerunfailures
2020
- conda-forge::pytest-remotedata # version in default channel is old
2121
- python=3.13
22-
- pytz
2322
- requests
2423
- scipy >= 1.7.2
2524
- statsmodels

ci/requirements-py3.14.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ dependencies:
1919
- pytest-rerunfailures
2020
- conda-forge::pytest-remotedata # version in default channel is old
2121
- python=3.14
22-
- pytz
2322
- requests
2423
- scipy >= 1.7.2
2524
- statsmodels

docs/sphinx/source/whatsnew/v0.15.2.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Breaking Changes
1010

1111
Deprecations
1212
~~~~~~~~~~~~
13+
* Deprecated ``Location.pytz`` attribute. Use ``Location.tz`` property instead.
14+
(:ghuser:`JoLo90`, :pull:`2757`)
1315

1416

1517
Bug fixes
@@ -43,6 +45,8 @@ Benchmarking
4345

4446
Requirements
4547
~~~~~~~~~~~~
48+
* Removed ``pytz`` from core dependencies. ``zoneinfo`` (built into Python 3.9+)
49+
is now used instead. (:ghuser:`JoLo90`, :pull:`2757`)
4650

4751

4852
Maintenance

docs/tutorials/solarposition.ipynb

Lines changed: 483 additions & 340 deletions
Large diffs are not rendered by default.

pvlib/iotools/pvgis.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import requests
2121
import numpy as np
2222
import pandas as pd
23-
import pytz
23+
import zoneinfo
2424
from pvlib.iotools import read_epw
2525

2626
URL = 'https://re.jrc.ec.europa.eu/api/'
@@ -413,10 +413,10 @@ def _coerce_and_roll_tmy(tmy_data, tz, year):
413413
re-interpreted as zero / UTC.
414414
"""
415415
if tz:
416-
tzname = pytz.timezone(f'Etc/GMT{-tz:+d}')
416+
tzname = zoneinfo.ZoneInfo(f'Etc/GMT{-tz:+d}') # noqa: E231
417417
else:
418418
tz = 0
419-
tzname = pytz.timezone('UTC')
419+
tzname = zoneinfo.ZoneInfo('UTC')
420420
new_index = pd.DatetimeIndex([
421421
timestamp.replace(year=year, tzinfo=tzname)
422422
for timestamp in tmy_data.index],

pvlib/location.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import zoneinfo
1010

1111
import pandas as pd
12-
import pytz
1312
import h5py
1413

1514
from pvlib import solarposition, clearsky, atmosphere, irradiance
@@ -22,13 +21,11 @@ class Location:
2221
time zone, and altitude data associated with a particular geographic
2322
location. You can also assign a name to a location object.
2423
25-
Location objects have two time-zone attributes:
24+
Location objects have a time-zone attribute:
2625
2726
* ``tz`` is an IANA time-zone string.
28-
* ``pytz`` is a pytz-based time-zone object (read only).
2927
30-
The read-only ``pytz`` attribute will stay in sync with any changes made
31-
using ``tz``.
28+
The ``pytz`` attribute was removed. Use ``tz`` property instead.
3229
3330
Location objects support the print method.
3431
@@ -47,8 +44,8 @@ class Location:
4744
list of valid name strings. An `int` or `float` must be a whole-number
4845
hour offsets from UTC that can be converted to the IANA-supported
4946
'Etc/GMT-N' format. (Note the limited range of the offset N and its
50-
sign-change convention.) Time zones from the pytz and zoneinfo packages
51-
may also be passed here, as they are subclasses of datetime.tzinfo.
47+
sign-change convention.) Time zones from the zoneinfo packages may also
48+
be passed here.
5249
5350
The `tz` attribute is represented as a valid IANA time zone name
5451
string.
@@ -108,17 +105,19 @@ def tz(self, tz_):
108105
if isinstance(tz_, str):
109106
self._zoneinfo = zoneinfo.ZoneInfo(tz_)
110107
elif isinstance(tz_, int):
111-
self._zoneinfo = zoneinfo.ZoneInfo(f"Etc/GMT{-tz_:+d}")
108+
tz_str = f"Etc/GMT{-tz_:+d}" # noqa: E231
109+
self._zoneinfo = zoneinfo.ZoneInfo(tz_str)
112110
elif isinstance(tz_, float):
113111
if tz_ % 1 != 0:
114112
raise TypeError(
115113
"Floating-point tz has non-zero fractional part: "
116114
f"{tz_}. Only whole-number offsets are supported."
117115
)
118116

119-
self._zoneinfo = zoneinfo.ZoneInfo(f"Etc/GMT{-int(tz_):+d}")
117+
tz_str = f"Etc/GMT{-int(tz_):+d}" # noqa: E231
118+
self._zoneinfo = zoneinfo.ZoneInfo(tz_str)
120119
elif isinstance(tz_, datetime.tzinfo):
121-
# Includes time zones generated by pytz and zoneinfo packages.
120+
# Includes time zones generated by zoneinfo packages.
122121
self._zoneinfo = zoneinfo.ZoneInfo(str(tz_))
123122
else:
124123
raise TypeError(
@@ -127,11 +126,6 @@ def tz(self, tz_):
127126
"datetime.tzinfo object (including subclasses)"
128127
)
129128

130-
@property
131-
def pytz(self):
132-
"""The location's pytz time zone (read only)."""
133-
return pytz.timezone(str(self._zoneinfo))
134-
135129
@classmethod
136130
def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs):
137131
"""

0 commit comments

Comments
 (0)