dataclass-wizard
dataclass-wizard copied to clipboard
Allow for arbitrary ISO8601 datetime strings to parse by using dateutil.
- Dataclass Wizard version: 0.22.1
- Python version: 3.9
- Operating System: Ubuntu
Description
When parsing JSON data outputs from a GQL endpoint, we sometimes encounter datetime strings that conform to ISO8601 but not to python's datetime.fromisoformat
method. This is a known issue where the datetime
documentation recommends using python-dateutil
for this.
What I Did
Try to parse a JSON with the following datetime string:
{
"created_at": "2021-12-08T12:25:28.61+00:00",
}
Suggested solution
Replace the datetime.fromisoformat
parsing with the dateutil.parser.isoparse
method.
This would introduce a dependency on python-dateutil
.
I would be happy to make this PR if it's 👍 'd
Thanks for reporting this! I was able to look into this briefly, and implemented a working solution using the docs on patterned dates and times as a reference, and specifying a custom parse format to datetime.strptime
as below.
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, DateTimePattern
# as `datetime.fromisoformat()` only accepts 3 or 6 decimal places for fractional seconds
CustomISOFormat = DateTimePattern['%Y-%m-%dT%H:%M:%S.%f%z']
@dataclass
class A(JSONWizard, str=False):
created_at: CustomISOFormat
json_dict = {
"created_at": "2021-12-08T12:25:28.61+00:00",
}
print(A.from_dict(json_dict))
# A(created_at=datetime.datetime(2021, 12, 8, 12, 25, 28, 610000, tzinfo=datetime.timezone.utc))
I am certainly not against it we do want to continue to look into a path using dateutil.parser.isoparse
. The only thought I had is, we could add the new dependency under lazy_imports.py
so that the module is lazily loaded, and then maybe introduce a new class-based Meta field to use dateutil
as a dependency instead of datetime
for parsing an ISO format string.
Even though this might introduce a bit of undue work, I would welcome a PR that achieved this, as long as we retain the default behavior of parsing ISO strings using datetime.fromisoformat
for backwards compatibility purposes.
Alternatively, adding another workaround with custom type hooks, to override to use dateutil.parser.isoparse
as the load function for datetime
types. This approach seems a little bit faster than the one with datetime.strptime
above.
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
# pip install python-dateutil
import dateutil.parser
from dataclass_wizard import JSONWizard, LoadMixin
from dataclass_wizard.decorators import _single_arg_alias
from dataclass_wizard.type_def import N
class CustomLoader(LoadMixin):
@staticmethod
@_single_arg_alias(dateutil.parser.isoparse)
def load_to_datetime(o: str | N, _: type[datetime]) -> datetime:
# alias: isoparse(o)
...
@dataclass
class MyClass(JSONWizard, CustomLoader, str=False):
created_at: datetime
print(MyClass.from_dict({"created_at": "2021-12-08T12:25:28.61+00:00"}))