Skip to content

Commit 33e379d

Browse files
accept +0000 format datetime (comes from requests)
1 parent 602cf2a commit 33e379d

3 files changed

Lines changed: 59 additions & 21 deletions

File tree

models/ConfigData.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass, field, fields, is_dataclass
2-
from datetime import datetime, timezone
2+
from datetime import datetime
33
from enum import Enum
44

55
from .utils import update_dataclass_from_dict
@@ -9,6 +9,7 @@
99
MetadataConfig,
1010
ResourceConfigTemplate,
1111
)
12+
from .top_level.utils import datetime_to_string
1213
from .top_level.utils import InlineList
1314
from .top_level.providers import ProviderTemplate
1415
from .top_level.providers.records import ProviderTypes
@@ -141,14 +142,6 @@ def all_missing_props(self):
141142
return self._all_missing_props
142143
return []
143144

144-
def datetime_to_string(self, data: datetime):
145-
# normalize to UTC and format with Z
146-
if data.tzinfo is None:
147-
data = data.replace(tzinfo=timezone.utc)
148-
else:
149-
data = data.astimezone(timezone.utc)
150-
return data.strftime("%Y-%m-%dT%H:%M:%SZ")
151-
152145
def asdict_enum_safe(self, obj, datetime_to_str=False):
153146
"""Overwriting dataclass 'asdict' fuction to replace Enums with strings."""
154147
if is_dataclass(obj):
@@ -177,7 +170,7 @@ def asdict_enum_safe(self, obj, datetime_to_str=False):
177170
}
178171
else:
179172
if isinstance(obj, datetime) and datetime_to_str:
180-
return self.datetime_to_string(obj)
173+
return datetime_to_string(obj)
181174
else:
182175
return obj
183176

models/top_level/utils.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import datetime, timezone
22
from enum import Enum
3+
import re
34

45
STRING_SEPARATOR = " | "
56

@@ -64,3 +65,49 @@ def to_iso8601(dt: datetime) -> str:
6465
dt = dt.astimezone(timezone.utc)
6566

6667
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
68+
69+
70+
def datetime_to_string(data: datetime):
71+
# normalize to UTC and format with Z
72+
if data.tzinfo is None:
73+
data = data.replace(tzinfo=timezone.utc)
74+
else:
75+
data = data.astimezone(timezone.utc)
76+
return data.strftime("%Y-%m-%dT%H:%M:%SZ")
77+
78+
79+
def datetime_from_string(value: str) -> datetime | None:
80+
"""
81+
Parse common ISO8601 datetime strings and return a timezone-aware datetime.
82+
Accepts:
83+
- 2025-12-17T12:34:56Z
84+
- 2025-12-17T12:34:56+02:00
85+
- 2025-12-17T12:34:56+0200
86+
If no timezone is present, returns a UTC-aware datetime (assumption).
87+
Returns None if parsing fails.
88+
"""
89+
if not isinstance(value, str):
90+
return None
91+
92+
s = value.strip()
93+
# quick normalization: trailing Z -> +00:00
94+
if s.endswith("Z"):
95+
s = s[:-1] + "+00:00"
96+
97+
# normalize +0200 -> +02:00
98+
s = re.sub(r"([+-]\d{2})(\d{2})$", r"\1:\2", s)
99+
100+
# Try stdlib first (requires offset with colon to return aware dt)
101+
try:
102+
dt = datetime.fromisoformat(s)
103+
except Exception:
104+
dt = None
105+
106+
if dt is None:
107+
return None
108+
109+
# If dt is naive, assume UTC
110+
if dt.tzinfo is None:
111+
dt = dt.replace(tzinfo=timezone.utc)
112+
113+
return dt

models/utils.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
from types import UnionType
55
from typing import Any, get_origin, get_args, Union, get_type_hints
66

7-
from .top_level.utils import InlineList, get_enum_value_from_string
7+
from .top_level.utils import (
8+
InlineList,
9+
get_enum_value_from_string,
10+
datetime_from_string,
11+
)
812

913

1014
def update_dataclass_from_dict(
@@ -67,12 +71,7 @@ def update_dataclass_from_dict(
6771
if (datetime in args or expected_type is datetime) and isinstance(
6872
new_value, str
6973
):
70-
try:
71-
new_value = datetime.strptime(
72-
new_value, "%Y-%m-%dT%H:%M:%SZ"
73-
)
74-
except:
75-
pass
74+
new_value = datetime_from_string(new_value)
7675

7776
# Exception: remap str to Enum
7877
elif isinstance(expected_type, type) and issubclass(
@@ -294,11 +293,10 @@ def _is_instance_of_type(value, expected_type) -> bool:
294293

295294
# Exception: try cast str to datetime manually
296295
if expected_type is datetime:
297-
try:
298-
datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
296+
if datetime_from_string(value) is not None:
299297
return True
300-
except:
301-
pass
298+
else:
299+
return False
302300

303301
# Fallback for normal types
304302
return isinstance(value, expected_type)

0 commit comments

Comments
 (0)