Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/electionguard/serializable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from datetime import datetime
import re
from os import path
from typing import Any, cast, Type, TypeVar

Expand Down Expand Up @@ -236,4 +237,14 @@ def set_deserializers() -> None:
NoneType,
)

set_deserializer(lambda dt, cls, **_: datetime.fromisoformat(dt), datetime)
set_deserializer(lambda dt, cls, **_: _deserialize_datetime(dt), datetime)


def _deserialize_datetime(value: str) -> datetime:
"""
The `fromisoformat` function doesn't recognize the Z (Zulu) suffix
to indicate UTC. For compatibility with more external clients, we
should allow it.
"""
tz_corrected = re.sub("Z$", "+00:00", value)
Copy link
Copy Markdown
Contributor

@keithrfung keithrfung Sep 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not my favorite, but this works for compatibility.

return datetime.fromisoformat(tz_corrected)
32 changes: 30 additions & 2 deletions tests/test_serializable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from datetime import datetime, timezone
from unittest import TestCase
from typing import Any, List, Optional
from os import remove
Expand Down Expand Up @@ -30,21 +31,48 @@ class DataModel:
test: int
nested: NestedModel
array: List[NestedModel]
datetime: datetime
from_json_file: Optional[Any] = None


JSON_DATA: DataModel = DataModel(
test=1, nested=NestedModel(test=1), array=[NestedModel(test=1)]
test=1,
nested=NestedModel(test=1),
datetime=datetime(2020, 9, 28, 20, 11, 31, tzinfo=timezone.utc),
array=[NestedModel(test=1)],
)
EXPECTED_JSON_STRING = '{"array": [{"test": 1}], "nested": {"test": 1}, "test": 1}'
EXPECTED_JSON_STRING = '{"array": [{"test": 1}], "datetime": "2020-09-28T20:11:31+00:00", "nested": {"test": 1}, "test": 1}'
EXPECTED_JSON_OBJECT = {
"test": 1,
"datetime": "2020-09-28T20:11:31+00:00",
"nested": {"test": 1},
"array": [{"test": 1}],
}


class TestSerializable(TestCase):
def test_read_iso_date(self) -> None:
# Arrange
target_date = datetime(2020, 9, 28, 20, 11, 31, tzinfo=timezone.utc)
representations = [
# UTC
'"2020-09-28T20:11:31+00:00"',
'"2020-09-28T20:11:31.000+00:00"',
'"2020-09-28T20:11:31.000Z"',
'"2020-09-28T20:11:31Z"',
# Other time zone
'"2020-09-28T21:11:31+01:00"',
'"2020-09-28T21:11:31.000+01:00"',
]

# Act
results = [read_json(value, datetime) for value in representations]

# Assert
# expected_timestamp = target_date.timestamp()
for result in results:
self.assertEqual(target_date, result)

def test_read_and_write_json(self) -> None:
# Act
json_string = write_json(JSON_DATA)
Expand Down