Skip to content

Commit e5e2ff3

Browse files
authored
Replace dateutil.tz.tzutc with timezone.utc (#848)
1 parent 6308271 commit e5e2ff3

File tree

3 files changed

+26
-29
lines changed

3 files changed

+26
-29
lines changed

pynamodb/attributes.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
import warnings
99
from base64 import b64encode, b64decode
1010
from copy import deepcopy
11-
from datetime import datetime, timedelta
11+
from datetime import datetime
12+
from datetime import timedelta
13+
from datetime import timezone
1214
from dateutil.parser import parse
13-
from dateutil.tz import tzutc
1415
from inspect import getfullargspec
1516
from inspect import getmembers
16-
from typing import Any, Callable, Dict, Generic, List, Mapping, Optional, TypeVar, Type, Union, Set, cast, overload
17+
from typing import Any, Callable, Dict, Generic, List, Mapping, Optional, TypeVar, Type, Union, Set, overload
1718
from typing import TYPE_CHECKING
1819

1920
from pynamodb._compat import GenericMeta
@@ -624,7 +625,7 @@ def _normalize(self, value):
624625
value = calendar.timegm(value.utctimetuple())
625626
else:
626627
raise ValueError("TTLAttribute value must be a timedelta or datetime")
627-
return datetime.utcfromtimestamp(value).replace(tzinfo=tzutc())
628+
return datetime.utcfromtimestamp(value).replace(tzinfo=timezone.utc)
628629

629630
def __set__(self, instance, value):
630631
"""
@@ -645,7 +646,7 @@ def deserialize(self, value):
645646
Deserializes a timestamp (Unix time) as a UTC datetime.
646647
"""
647648
timestamp = json.loads(value)
648-
return datetime.utcfromtimestamp(timestamp).replace(tzinfo=tzutc())
649+
return datetime.utcfromtimestamp(timestamp).replace(tzinfo=timezone.utc)
649650

650651

651652
class UTCDateTimeAttribute(Attribute[datetime]):
@@ -659,8 +660,8 @@ def serialize(self, value):
659660
Takes a datetime object and returns a string
660661
"""
661662
if value.tzinfo is None:
662-
value = value.replace(tzinfo=tzutc())
663-
fmt = value.astimezone(tzutc()).strftime(DATETIME_FORMAT)
663+
value = value.replace(tzinfo=timezone.utc)
664+
fmt = value.astimezone(timezone.utc).strftime(DATETIME_FORMAT)
664665
return fmt
665666

666667
def deserialize(self, value):
@@ -982,7 +983,7 @@ def _fast_parse_utc_datestring(datestring):
982983
return datetime(
983984
_int(datestring[0:4]), _int(datestring[5:7]), _int(datestring[8:10]),
984985
_int(datestring[11:13]), _int(datestring[14:16]), _int(datestring[17:19]),
985-
_int(round(float(datestring[19:-5]) * 1e6)), tzutc()
986+
_int(round(float(datestring[19:-5]) * 1e6)), timezone.utc
986987
)
987988
except (TypeError, ValueError):
988989
raise ValueError("Datetime string '{}' does not match format "

tests/test_attributes.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
pynamodb attributes tests
33
"""
44
import json
5-
import time
65

76
from base64 import b64encode
87
from datetime import datetime
9-
108
from datetime import timedelta
11-
from dateutil.tz import tzutc
9+
from datetime import timezone
1210

13-
from unittest.mock import patch, Mock, call
11+
from unittest.mock import patch, call
1412
import pytest
1513

1614
from pynamodb.attributes import (
@@ -25,9 +23,6 @@
2523
from pynamodb.models import Model
2624

2725

28-
UTC = tzutc()
29-
30-
3126
class AttributeTestModel(Model):
3227

3328
class Meta:
@@ -148,15 +143,15 @@ def test_utc_date_time_deserialize(self):
148143
"""
149144
UTCDateTimeAttribute.deserialize
150145
"""
151-
tstamp = datetime.now(UTC)
146+
tstamp = datetime.now(timezone.utc)
152147
attr = UTCDateTimeAttribute()
153148
assert attr.deserialize(tstamp.strftime(DATETIME_FORMAT)) == tstamp
154149

155150
def test_dateutil_parser_fallback(self):
156151
"""
157152
UTCDateTimeAttribute.deserialize
158153
"""
159-
expected_value = datetime(2047, 1, 6, 8, 21, tzinfo=tzutc())
154+
expected_value = datetime(2047, 1, 6, 8, 21, tzinfo=timezone.utc)
160155
attr = UTCDateTimeAttribute()
161156
assert attr.deserialize('January 6, 2047 at 8:21:00AM UTC') == expected_value
162157

@@ -166,7 +161,7 @@ def test_utc_date_time_deserialize_parse_args(self, parse_mock, datetime_mock):
166161
"""
167162
UTCDateTimeAttribute.deserialize
168163
"""
169-
tstamp = datetime.now(UTC)
164+
tstamp = datetime.now(timezone.utc)
170165
attr = UTCDateTimeAttribute()
171166

172167
tstamp_str = tstamp.strftime(DATETIME_FORMAT)
@@ -181,15 +176,15 @@ def test_utc_date_time_serialize(self):
181176
"""
182177
tstamp = datetime.now()
183178
attr = UTCDateTimeAttribute()
184-
assert attr.serialize(tstamp) == tstamp.replace(tzinfo=UTC).strftime(DATETIME_FORMAT)
179+
assert attr.serialize(tstamp) == tstamp.replace(tzinfo=timezone.utc).strftime(DATETIME_FORMAT)
185180

186181
def test__fast_parse_utc_datestring_roundtrips(self):
187-
tstamp = datetime.now(UTC)
182+
tstamp = datetime.now(timezone.utc)
188183
tstamp_str = tstamp.strftime(DATETIME_FORMAT)
189184
assert _fast_parse_utc_datestring(tstamp_str) == tstamp
190185

191186
def test__fast_parse_utc_datestring_no_microseconds(self):
192-
expected_value = datetime(2047, 1, 6, 8, 21, tzinfo=tzutc())
187+
expected_value = datetime(2047, 1, 6, 8, 21, tzinfo=timezone.utc)
193188
assert _fast_parse_utc_datestring('2047-01-06T08:21:00.0+0000') == expected_value
194189

195190
@pytest.mark.parametrize(
@@ -497,7 +492,7 @@ def test_timedelta_ttl(self, mock_time):
497492
mock_time.side_effect = [1559692800] # 2019-06-05 00:00:00 UTC
498493
model = AttributeTestModel()
499494
model.ttl_attr = timedelta(seconds=60)
500-
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=UTC)
495+
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=timezone.utc)
501496

502497
def test_datetime_naive_ttl(self):
503498
model = AttributeTestModel()
@@ -507,8 +502,8 @@ def test_datetime_naive_ttl(self):
507502

508503
def test_datetime_with_tz_ttl(self):
509504
model = AttributeTestModel()
510-
model.ttl_attr = datetime(2019, 6, 5, 0, 1, tzinfo=UTC)
511-
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=UTC)
505+
model.ttl_attr = datetime(2019, 6, 5, 0, 1, tzinfo=timezone.utc)
506+
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=timezone.utc)
512507

513508
def test_ttl_attribute_wrong_type(self):
514509
with pytest.raises(ValueError, match='TTLAttribute value must be a timedelta or datetime'):
@@ -531,10 +526,10 @@ def test_serialize_deserialize(self, mock_time):
531526
mock_time.side_effect = [1559692800, 1559692800] # 2019-06-05 00:00:00 UTC
532527
model = AttributeTestModel()
533528
model.ttl_attr = timedelta(minutes=1)
534-
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=UTC)
529+
assert model.ttl_attr == datetime(2019, 6, 5, 0, 1, tzinfo=timezone.utc)
535530
s = TTLAttribute().serialize(model.ttl_attr)
536531
assert s == '1559692860'
537-
assert TTLAttribute().deserialize(s) == datetime(2019, 6, 5, 0, 1, 0, tzinfo=UTC)
532+
assert TTLAttribute().deserialize(s) == datetime(2019, 6, 5, 0, 1, 0, tzinfo=timezone.utc)
538533

539534

540535
class TestJSONAttribute:

tests/test_model.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import random
66
import json
77
import copy
8-
from datetime import datetime, timedelta
8+
from datetime import datetime
9+
from datetime import timedelta
10+
from datetime import timezone
911
from unittest import TestCase
1012

1113
from botocore.client import ClientError
1214
import pytest
13-
from dateutil.tz import tzutc
1415

1516
from .deep_eq import deep_eq
1617
from pynamodb.util import snake_to_camel_case
@@ -3300,7 +3301,7 @@ def test_deserialized_with_ttl(self):
33003301
with patch(PATCH_METHOD) as req:
33013302
req.return_value = SIMPLE_MODEL_TABLE_DATA
33023303
m = TTLModel.from_raw_data({'user_name': {'S': 'mock'}, 'my_ttl': {'N': '1546300800'}})
3303-
assert m.my_ttl == datetime(2019, 1, 1, tzinfo=tzutc())
3304+
assert m.my_ttl == datetime(2019, 1, 1, tzinfo=timezone.utc)
33043305

33053306
def test_deserialized_with_invalid_type(self):
33063307
self.assertRaises(AttributeDeserializationError, TTLModel.from_raw_data, {'my_ttl': {'S': '1546300800'}})

0 commit comments

Comments
 (0)