Skip to content

Commit c791681

Browse files
committed
Merge remote-tracking branch 'origin/replace-pytz' into slim
2 parents ce24ea3 + 82651fe commit c791681

File tree

9 files changed

+2858
-2840
lines changed

9 files changed

+2858
-2840
lines changed

examples/fastapi/_tests.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
import anyio
1212
import pytest
13-
import pytz
1413
from asgi_lifespan import LifespanManager
1514
from httpx import ASGITransport, AsyncClient
1615

1716
from tortoise.contrib.test import MEMORY_SQLITE
1817
from tortoise.fields.data import JSON_LOADS
18+
from tortoise.timezone import UTC, parse_timezone
1919

2020
os.environ["DB_URL"] = MEMORY_SQLITE
2121
try:
@@ -75,7 +75,7 @@ async def create_user(self, async_client: AsyncClient) -> Users:
7575
return user_obj
7676

7777
async def user_list(self, async_client: AsyncClient) -> tuple[datetime, Users, User_Pydantic]:
78-
utc_now = datetime.now(pytz.utc)
78+
utc_now = datetime.now(UTC)
7979
user_obj = await Users.create(username="test")
8080
response = await async_client.get("/users")
8181
assert response.status_code == 200, response.text
@@ -123,13 +123,13 @@ async def test_create_user_east(self, client_east: AsyncClient) -> None: # nose
123123
created_at = user_obj.created_at
124124

125125
# Verify time zone
126-
asia_tz = pytz.timezone(self.timezone)
127-
asia_now = datetime.now(pytz.utc).astimezone(asia_tz)
126+
asia_tz = parse_timezone(self.timezone)
127+
asia_now = datetime.now(UTC).astimezone(asia_tz)
128128
assert created_at.hour - asia_now.hour == 0
129129

130130
# UTC timezone
131-
utc_tz = pytz.timezone("UTC")
132-
utc_now = datetime.now(pytz.utc).astimezone(utc_tz)
131+
utc_tz = parse_timezone("UTC")
132+
utc_now = datetime.now(UTC).astimezone(utc_tz)
133133
assert (created_at.hour - utc_now.hour) in [self.delta_hours, self.delta_hours - 24]
134134

135135
@pytest.mark.anyio

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ dependencies = [
1111
"pypika-tortoise (>=0.6.3,<1.0.0)",
1212
"aiosqlite (>=0.16.0,<1.0.0)",
1313
"anyio",
14-
"pytz",
1514
"typing-extensions (>= 4.1.0); python_version < '3.11'",
1615
]
1716
classifiers = [

tests/fields/test_time.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
from time import sleep
55
from unittest.mock import patch
66

7-
import pytz
8-
97
from tests import testmodels
108
from tortoise import Model, fields, timezone
119
from tortoise.contrib import test
1210
from tortoise.contrib.test.condition import NotIn
1311
from tortoise.exceptions import ConfigurationError, IntegrityError
1412
from tortoise.expressions import F
15-
from tortoise.timezone import get_default_timezone
13+
from tortoise.timezone import get_default_timezone, parse_timezone
1614

1715

1816
class TestEmpty(test.TestCase):
@@ -125,7 +123,7 @@ async def test_set_timezone(self):
125123
old_tz = os.environ["TIMEZONE"]
126124
tz = "Asia/Shanghai"
127125
os.environ["TIMEZONE"] = tz
128-
now = datetime.now(pytz.timezone(tz))
126+
now = datetime.now(parse_timezone(tz))
129127
obj = await self.model.create(datetime=now)
130128
self.assertEqual(obj.datetime.tzinfo.zone, tz)
131129

@@ -142,7 +140,7 @@ async def test_timezone(self):
142140
os.environ["TIMEZONE"] = tz
143141
os.environ["USE_TZ"] = "True"
144142

145-
now = datetime.now(pytz.timezone(tz))
143+
now = datetime.now(parse_timezone(tz))
146144
obj = await self.model.create(datetime=now)
147145
self.assertEqual(obj.datetime.tzinfo.zone, tz)
148146
obj_get = await self.model.get(pk=obj.pk)

tests/test_default.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import datetime
22
from decimal import Decimal
33

4-
import pytz
5-
64
from tests.testmodels import DefaultModel
75
from tortoise import connections
86
from tortoise.backends.asyncpg import AsyncpgDBClient
@@ -12,6 +10,7 @@
1210
from tortoise.backends.psycopg import PsycopgClient
1311
from tortoise.backends.sqlite import SqliteClient
1412
from tortoise.contrib import test
13+
from tortoise.timezone import UTC
1514

1615

1716
class TestDefault(test.TestCase):
@@ -45,5 +44,5 @@ async def test_default(self):
4544
self.assertEqual(default_model.date_default, datetime.date(year=2020, month=5, day=21))
4645
self.assertEqual(
4746
default_model.datetime_default,
48-
datetime.datetime(year=2020, month=5, day=20, tzinfo=pytz.utc),
47+
datetime.datetime(year=2020, month=5, day=20, tzinfo=UTC),
4948
)

tests/test_update.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from datetime import datetime, timedelta
55
from typing import Any
66

7-
import pytz
87
from pypika_tortoise.terms import Function as PupikaFunction
98

109
from tests.testmodels import (
@@ -26,6 +25,7 @@
2625
from tortoise.contrib.test.condition import In, NotEQ
2726
from tortoise.expressions import Case, F, Q, Subquery, When
2827
from tortoise.functions import Function, Upper
28+
from tortoise.timezone import UTC
2929

3030

3131
class TestUpdate(test.TestCase):
@@ -49,11 +49,11 @@ async def test_bulk_update(self):
4949

5050
async def test_bulk_update_datetime(self):
5151
objs = [
52-
await DatetimeFields.create(datetime=datetime(2021, 1, 1, tzinfo=pytz.utc)),
53-
await DatetimeFields.create(datetime=datetime(2021, 1, 1, tzinfo=pytz.utc)),
52+
await DatetimeFields.create(datetime=datetime(2021, 1, 1, tzinfo=UTC)),
53+
await DatetimeFields.create(datetime=datetime(2021, 1, 1, tzinfo=UTC)),
5454
]
55-
t0 = datetime(2021, 1, 2, tzinfo=pytz.utc)
56-
t1 = datetime(2021, 1, 3, tzinfo=pytz.utc)
55+
t0 = datetime(2021, 1, 2, tzinfo=UTC)
56+
t1 = datetime(2021, 1, 3, tzinfo=UTC)
5757
objs[0].datetime = t0
5858
objs[1].datetime = t1
5959
rows_affected = await DatetimeFields.bulk_update(objs, fields=["datetime"])

tests/testmodels.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from enum import Enum, IntEnum
1414
from typing import Union
1515

16-
import pytz
1716
from pydantic import BaseModel, ConfigDict
1817

1918
from tortoise import fields
@@ -23,6 +22,7 @@
2322
from tortoise.manager import Manager
2423
from tortoise.models import Model
2524
from tortoise.queryset import QuerySet
25+
from tortoise.timezone import UTC
2626
from tortoise.validators import (
2727
CommaSeparatedIntegerListValidator,
2828
MaxValueValidator,
@@ -845,7 +845,7 @@ class DefaultModel(Model):
845845
char_default = fields.CharField(max_length=20, default="tortoise")
846846
date_default = fields.DateField(default=datetime.date(year=2020, month=5, day=21))
847847
datetime_default = fields.DatetimeField(
848-
default=datetime.datetime(year=2020, month=5, day=20, tzinfo=pytz.utc)
848+
default=datetime.datetime(year=2020, month=5, day=20, tzinfo=UTC)
849849
)
850850

851851

tortoise/backends/oracle/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import TYPE_CHECKING, Any, SupportsInt, cast
55

66
import pyodbc
7-
import pytz
87
from pypika_tortoise import OracleQuery
98

109
from tortoise.backends.base.client import (
@@ -22,6 +21,7 @@
2221
from tortoise.backends.oracle.executor import OracleExecutor
2322
from tortoise.backends.oracle.schema_generator import OracleSchemaGenerator
2423
from tortoise.fields.data import parse_datetime
24+
from tortoise.timezone import UTC
2525

2626
if TYPE_CHECKING: # pragma: nocoverage
2727
import asyncodbc # pylint: disable=W0611
@@ -95,7 +95,7 @@ def _timestamp_convert(self, value: bytes) -> datetime.date:
9595
try:
9696
return parse_datetime(value.decode()).date()
9797
except ValueError:
98-
return parse_datetime(value.decode()[:-32]).astimezone(tz=pytz.utc)
98+
return parse_datetime(value.decode()[:-32]).astimezone(tz=UTC)
9999

100100
async def __aenter__(self) -> asyncodbc.Connection:
101101
connection = await super().__aenter__()

tortoise/timezone.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
import functools
44
import os
5+
import sys
56
from datetime import datetime, time, tzinfo
7+
from zoneinfo import ZoneInfo as _ZoneInfo
8+
from zoneinfo import ZoneInfoNotFoundError
69

7-
import pytz
10+
if sys.version_info >= (3, 12):
11+
from datetime import UTC
12+
else:
13+
from datetime import timezone
14+
15+
UTC = timezone.utc
816

917

1018
@functools.cache
@@ -28,7 +36,7 @@ def now() -> datetime:
2836
Return an aware datetime.datetime, depending on use_tz and timezone.
2937
"""
3038
if get_use_tz():
31-
return datetime.now(tz=pytz.utc)
39+
return datetime.now(tz=UTC)
3240
else:
3341
return datetime.now(get_default_timezone())
3442

@@ -40,7 +48,32 @@ def get_default_timezone() -> tzinfo:
4048
4149
This is the time zone defined by Tortoise config.
4250
"""
43-
return pytz.timezone(get_timezone())
51+
return parse_timezone(get_timezone())
52+
53+
54+
class ZoneInfo(_ZoneInfo):
55+
@property
56+
def zone(self) -> str:
57+
# Compatible with pytz:
58+
# >>> ZoneInfo('UTC').key == pytz.timezone('UTC').zone == 'UTC'
59+
return self.key
60+
61+
62+
def parse_timezone(zone: str) -> tzinfo:
63+
if zone.upper() == "UTC":
64+
return ZoneInfo("UTC")
65+
try:
66+
return ZoneInfo(zone)
67+
except ZoneInfoNotFoundError as e:
68+
words = zone.split("/")
69+
# Compatible with `pytz.timezone`:
70+
# US/central -> US/Central
71+
# Europe/moscow -> Europe/Moscow
72+
# asia/ShangHai -> Asia/Shanghai
73+
styled = "/".join([i if i.isupper() else i.title() for i in words])
74+
if styled != zone:
75+
return ZoneInfo(zone)
76+
raise e
4477

4578

4679
def _reset_timezone_cache() -> None:
@@ -64,7 +97,7 @@ def localtime(value: datetime | None = None, timezone: str | None = None) -> dat
6497
"""
6598
if value is None:
6699
value = now()
67-
tz = get_default_timezone() if timezone is None else pytz.timezone(timezone)
100+
tz = get_default_timezone() if timezone is None else parse_timezone(timezone)
68101
if is_naive(value):
69102
raise ValueError("localtime() cannot be applied to a naive datetime")
70103
return value.astimezone(tz)
@@ -104,7 +137,7 @@ def make_aware(
104137
105138
:raises ValueError: when value is not naive datetime
106139
"""
107-
tz = get_default_timezone() if timezone is None else pytz.timezone(timezone)
140+
tz = get_default_timezone() if timezone is None else parse_timezone(timezone)
108141
if hasattr(tz, "localize"):
109142
return tz.localize(value, is_dst=is_dst)
110143
if is_aware(value):
@@ -119,7 +152,7 @@ def make_naive(value: datetime, timezone: str | None = None) -> datetime:
119152
120153
:raises ValueError: when value is naive datetime
121154
"""
122-
tz = get_default_timezone() if timezone is None else pytz.timezone(timezone)
155+
tz = get_default_timezone() if timezone is None else parse_timezone(timezone)
123156
if is_naive(value):
124157
raise ValueError("make_naive() cannot be applied to a naive datetime")
125158
return value.astimezone(tz).replace(tzinfo=None)

0 commit comments

Comments
 (0)