Skip to content

Commit

Permalink
Migrate from pytz to zoneinfo
Browse files Browse the repository at this point in the history
Migrates code base and tests from `pytz` to `zoneinfo`.
  • Loading branch information
maread99 committed Sep 7, 2023
1 parent 543227c commit 4ca2677
Show file tree
Hide file tree
Showing 69 changed files with 231 additions and 213 deletions.
2 changes: 1 addition & 1 deletion etc/bench.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import itertools

import pandas as pd
from pytz import UTC

from exchange_calendars import get_calendar
from exchange_calendars.calendar_helpers import UTC
from exchange_calendars.calendar_utils import (
ExchangeCalendarDispatcher,
_default_calendar_aliases,
Expand Down
4 changes: 2 additions & 2 deletions etc/check_holidays.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pandas as pd

from exchange_calendars import get_calendar
from exchange_calendars.calendar_helpers import UTC
from exchange_calendars.calendar_utils import default_calendar_names


Expand Down Expand Up @@ -136,7 +137,7 @@ def _check_range(start, end, holidays, cal, calendar_name):
@click.option(
"--min-date",
default=pd.Timestamp("2002-01-01"),
type=TimestampType(tz="UTC"),
type=TimestampType(tz=UTC),
help="Start the holiday comparison at this date.",
show_default=True,
)
Expand Down Expand Up @@ -190,7 +191,6 @@ def main(
strip_x_from_cal_name,
answer_key_calendar_name,
):

check_holidays(
holiday_key_path,
calendar_column,
Expand Down
18 changes: 10 additions & 8 deletions etc/factory_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
"""

from __future__ import annotations
import dataclasses

import abc
import dataclasses
import pathlib
import pickle
from typing import Literal, Type

import pandas as pd

import exchange_calendars as xcals
from exchange_calendars.calendar_helpers import UTC


@dataclasses.dataclass
Expand Down Expand Up @@ -72,7 +74,7 @@ def calendar_name(self) -> str:

@property
def today(self) -> pd.Timestamp:
return pd.Timestamp.now(tz="UTC").floor("D")
return pd.Timestamp.now(tz=UTC).floor("D")

def _get_calendar(
self,
Expand Down Expand Up @@ -173,13 +175,13 @@ def _try_short_cut(
"""
if bound == "start":
likely_bounds = [
pd.Timestamp("1678-01-01", tz="UTC"),
pd.Timestamp("1679-01-01", tz="UTC"),
pd.Timestamp("1678-01-01", tz=UTC),
pd.Timestamp("1679-01-01", tz=UTC),
]
else:
likely_bounds = [
pd.Timestamp("2260-12-31", tz="UTC"),
pd.Timestamp("2262-04-10", tz="UTC"),
pd.Timestamp("2260-12-31", tz=UTC),
pd.Timestamp("2262-04-10", tz=UTC),
]

for likely_bound in likely_bounds:
Expand All @@ -191,9 +193,9 @@ def _try_short_cut(
@staticmethod
def _initial_value(bound: Literal["start", "end"]) -> pd.Timestamp:
if bound == "start":
return pd.Timestamp.min.ceil("D").tz_localize("UTC")
return pd.Timestamp.min.ceil("D").tz_localize(UTC)
else:
return pd.Timestamp.max.floor("D").tz_localize("UTC")
return pd.Timestamp.max.floor("D").tz_localize(UTC)

def _get_bound(
self, bound: Literal["start", "end"]
Expand Down
4 changes: 3 additions & 1 deletion etc/lunisolar
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import pandas as pd
import toolz
from novas.compat import eph_manager

from exchange_calendars.calendar_helpers import UTC


@contextlib.contextmanager
def ephemeris():
Expand Down Expand Up @@ -1089,7 +1091,7 @@ def utc_to_chinese_date(arr):
An array of datetime64[ns] at midnight corresponding the date in China
of the input.
"""
ts = pd.to_datetime(arr).tz_localize("UTC").tz_convert("Asia/Shanghai")
ts = pd.to_datetime(arr).tz_localize(UTC).tz_convert("Asia/Shanghai")
out = ts.normalize().tz_localize(None)
if isinstance(out, pd.Timestamp):
return out.asm8
Expand Down
3 changes: 1 addition & 2 deletions exchange_calendars/always_open.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from datetime import time

from pytz import UTC

from .exchange_calendar import ExchangeCalendar
from exchange_calendars.calendar_helpers import UTC


class AlwaysOpenCalendar(ExchangeCalendar):
Expand Down
27 changes: 13 additions & 14 deletions exchange_calendars/calendar_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import datetime
import typing
from typing import Literal
from zoneinfo import ZoneInfo

import numpy as np
import pandas as pd
import pytz

from exchange_calendars import errors

if typing.TYPE_CHECKING:
from exchange_calendars import ExchangeCalendar

UTC = ZoneInfo("UTC")

NANOSECONDS_PER_MINUTE = int(6e10)

NP_NAT = pd.NaT.value
Expand All @@ -36,7 +38,6 @@


def next_divider_idx(dividers: np.ndarray, minute_val: int) -> int:

divider_idx = np.searchsorted(dividers, minute_val, side="right")
target = dividers[divider_idx]

Expand All @@ -48,7 +49,6 @@ def next_divider_idx(dividers: np.ndarray, minute_val: int) -> int:


def previous_divider_idx(dividers: np.ndarray, minute_val: int) -> int:

divider_idx = np.searchsorted(dividers, minute_val)

if divider_idx == 0:
Expand Down Expand Up @@ -146,9 +146,9 @@ def to_utc(ts: pd.Timestamp) -> pd.Timestamp:
Timestamp to return a copy of with timezone set to UTC.
"""
try:
return ts.tz_convert(pytz.UTC)
return ts.tz_convert(UTC)
except TypeError:
return ts.tz_localize(pytz.UTC)
return ts.tz_localize(UTC)


def parse_timestamp(
Expand Down Expand Up @@ -189,9 +189,9 @@ def parse_timestamp(
to minute resolution.
utc : default: True
True - convert / set timezone to "UTC".
True - convert / set timezone to UTC.
False - leave any timezone unchanged. Note, if timezone of
`timestamp` is "UTC" then will remain as "UTC".
`timestamp` is UTC then will remain as UTC.
Raises
------
Expand Down Expand Up @@ -224,8 +224,8 @@ def parse_timestamp(
else:
raise ValueError(msg) from e

if utc and ts.tz is not pytz.UTC:
ts = ts.tz_localize("UTC") if ts.tz is None else ts.tz_convert("UTC")
if utc and ts.tz is not UTC:
ts = ts.tz_localize(UTC) if ts.tz is None else ts.tz_convert(UTC)

if ts.second or ts.microsecond or ts.nanosecond:
if side is None and calendar is None:
Expand Down Expand Up @@ -376,7 +376,7 @@ def parse_date(

if ts.tz is not None:
raise ValueError(
f"Parameter `{param_name}` received with timezone defined as '{ts.tz.zone}'"
f"Parameter `{param_name}` received with timezone defined as '{ts.tz.key}'"
f" although a Date must be timezone naive."
)

Expand Down Expand Up @@ -630,7 +630,6 @@ def _trading_index(self) -> np.ndarray:
becoming unsynced with the corresponding left indices.
"""
if self.has_break:

# sessions with breaks
index_am = self._create_index_for_sessions(
self.opens[self.mask],
Expand Down Expand Up @@ -680,7 +679,7 @@ def trading_index(self) -> pd.DatetimeIndex:
index = self._trading_index()
if self.has_break:
index.sort()
index = pd.DatetimeIndex(index, tz=pytz.UTC)
index = pd.DatetimeIndex(index, tz=UTC)
return self.curtail_for_times(index)

@contextlib.contextmanager
Expand Down Expand Up @@ -718,7 +717,7 @@ def trading_index_intervals(self) -> pd.IntervalIndex:
else:
raise errors.IntervalsOverlapError()

left = pd.DatetimeIndex(left, tz=pytz.UTC)
right = pd.DatetimeIndex(right, tz=pytz.UTC)
left = pd.DatetimeIndex(left, tz=UTC)
right = pd.DatetimeIndex(right, tz=UTC)
index = pd.IntervalIndex.from_arrays(left, right, self.closed)
return self.curtail_for_times(index)
15 changes: 8 additions & 7 deletions exchange_calendars/exchange_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
import operator
from typing import TYPE_CHECKING, Literal, Any
import warnings
from zoneinfo import ZoneInfo

import numpy as np
import pandas as pd
import toolz
from pandas.tseries.holiday import AbstractHolidayCalendar
from pandas.tseries.offsets import CustomBusinessDay
import pytz
from pytz import UTC

from exchange_calendars import errors
from .calendar_helpers import (
UTC,
NANOSECONDS_PER_MINUTE,
NP_NAT,
Date,
Expand All @@ -55,6 +55,7 @@
if TYPE_CHECKING:
from pandas._libs.tslibs.nattype import NaTType


GLOBAL_DEFAULT_START = pd.Timestamp.now().floor("D") - pd.DateOffset(years=20)
# Give an aggressive buffer for logic that needs to use the next trading
# day or minute.
Expand Down Expand Up @@ -84,7 +85,7 @@ def selection(
def _group_times(
sessions: pd.DatetimeIndex,
times: None | Sequence[tuple[pd.Timestamp | None, datetime.time]],
tz: pytz.tzinfo.BaseTzInfo,
tz: ZoneInfo,
offset: int = 0,
) -> pd.DatetimeIndex | None:
"""Evaluate standard times for a specific session bound.
Expand Down Expand Up @@ -424,7 +425,7 @@ def _bound_max_error_msg(self, end: pd.Timestamp) -> str:

@property
@abstractmethod
def tz(self) -> pytz.tzinfo.BaseTzInfo:
def tz(self) -> ZoneInfo:
"""Calendar timezone."""
raise NotImplementedError()

Expand Down Expand Up @@ -1475,8 +1476,8 @@ def is_open_at_time(
f" got type {type(ts)}."
)

if ts.tz is not pytz.UTC:
ts = ts.tz_localize("UTC") if ts.tz is None else ts.tz_convert("UTC")
if ts.tz is not UTC:
ts = ts.tz_localize(UTC) if ts.tz is None else ts.tz_convert(UTC)

if self._minute_oob(ts):
raise errors.MinuteOutOfBounds(self, ts, "timestamp")
Expand Down Expand Up @@ -2820,7 +2821,7 @@ def scheduled_special_times(
start: pd.Timestamp,
end: pd.Timestamp,
time: datetime.time,
tz: pytz.tzinfo.BaseTzInfo,
tz: ZoneInfo,
) -> pd.Series:
"""Return map of dates to special times.
Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_aixk.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import time
from itertools import chain
from zoneinfo import ZoneInfo

import pandas as pd
from pandas.tseries.holiday import (
Expand All @@ -8,7 +9,6 @@
nearest_workday,
next_workday,
)
import pytz

from .common_holidays import new_years_day, eid_al_adha_first_day
from .exchange_calendar import (
Expand Down Expand Up @@ -172,7 +172,7 @@ class AIXKExchangeCalendar(ExchangeCalendar):

name = "AIXK"

tz = pytz.timezone("Asia/Almaty")
tz = ZoneInfo("Asia/Almaty")

open_times = ((None, time(11)),)

Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_asex.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

from datetime import time, timedelta
from itertools import chain
from zoneinfo import ZoneInfo

import pandas as pd
from pandas.tseries.holiday import EasterMonday, GoodFriday, Holiday
from pytz import timezone

from .common_holidays import (
assumption_day,
Expand Down Expand Up @@ -102,7 +102,7 @@ class ASEXExchangeCalendar(ExchangeCalendar):

name = "ASEX"

tz = timezone("Europe/Athens")
tz = ZoneInfo("Europe/Athens")

open_times = ((None, time(10)),)

Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_bvmf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.

from datetime import time
from zoneinfo import ZoneInfo

import pandas as pd
from pandas.tseries.holiday import (
Expand All @@ -23,7 +24,6 @@
Holiday,
previous_friday,
)
from pytz import timezone

from .common_holidays import corpus_christi
from .exchange_calendar import HolidayCalendar, ExchangeCalendar
Expand Down Expand Up @@ -174,7 +174,7 @@ class BVMFExchangeCalendar(ExchangeCalendar):

name = "BVMF"

tz = timezone("America/Sao_Paulo")
tz = ZoneInfo("America/Sao_Paulo")

regular_late_open = time(13)

Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_cmes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
# limitations under the License.

from datetime import time
from zoneinfo import ZoneInfo

from pandas.tseries.holiday import (
GoodFriday,
USLaborDay,
USPresidentsDay,
USThanksgivingDay,
)
from pytz import timezone

from .exchange_calendar import HolidayCalendar, ExchangeCalendar
from .us_holidays import (
Expand Down Expand Up @@ -55,7 +55,7 @@ class CMESExchangeCalendar(ExchangeCalendar):

name = "CMES"

tz = timezone("America/Chicago")
tz = ZoneInfo("America/Chicago")

open_times = ((None, time(17)),)

Expand Down
Loading

0 comments on commit 4ca2677

Please sign in to comment.