Skip to content

Commit

Permalink
UTCDateTimeAttribute: support for pre-1000 dates (#949)
Browse files Browse the repository at this point in the history
  • Loading branch information
ikonst committed Jun 17, 2021
1 parent dc68e72 commit 06bab8b
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Unreleased

* Null errors should include full attribute path (#915)
* Fix type annotation of ``is_in`` conditional expression
* Fix for serializing and deserializing dates prior to year 1000


v5.0.3
Expand Down
6 changes: 5 additions & 1 deletion pynamodb/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,9 @@ def serialize(self, value):
"""
if value.tzinfo is None:
value = value.replace(tzinfo=timezone.utc)
fmt = value.astimezone(timezone.utc).strftime(DATETIME_FORMAT)
# Padding of years under 1000 is inconsistent and depends on system strftime:
# https://bugs.python.org/issue13305
fmt = value.astimezone(timezone.utc).strftime(DATETIME_FORMAT).zfill(31)
return fmt

def deserialize(self, value):
Expand All @@ -700,6 +702,8 @@ def _fast_parse_utc_date_string(date_string: str) -> datetime:
# This is ~5.8x faster than using strptime and 38x faster than dateutil.parser.parse.
_int = int # Hack to prevent global lookups of int, speeds up the function ~10%
try:
# Fix pre-1000 dates serialized on systems where strftime doesn't pad w/older PynamoDB versions.
date_string = date_string.zfill(31)
if (len(date_string) != 31 or date_string[4] != '-' or date_string[7] != '-'
or date_string[10] != 'T' or date_string[13] != ':' or date_string[16] != ':'
or date_string[19] != '.' or date_string[26:31] != '+0000'):
Expand Down
7 changes: 7 additions & 0 deletions tests/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,19 @@ def test_utc_date_time_serialize(self):
"""
assert self.attr.serialize(self.dt) == '2047-01-06T08:21:30.002000+0000'

def test_utc_date_time_serialize_pre_1000(self):
dt = self.dt.replace(year=1)
assert self.attr.serialize(dt) == '0001-01-06T08:21:30.002000+0000'

def test_utc_date_time_deserialize(self):
"""
UTCDateTimeAttribute.deserialize
"""
assert self.attr.deserialize('2047-01-06T08:21:30.002000+0000') == self.dt

def test_utc_date_time_deserialize_pre_1000_not_padded(self):
assert self.attr.deserialize('1-01-06T08:21:30.002000+0000') == self.dt.replace(year=1)

@pytest.mark.parametrize(
"invalid_string",
[
Expand Down

0 comments on commit 06bab8b

Please sign in to comment.