-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathdatetime_utils.py
More file actions
70 lines (53 loc) · 2.44 KB
/
Copy pathdatetime_utils.py
File metadata and controls
70 lines (53 loc) · 2.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# This file was auto-generated by Fern from our API Definition.
import datetime as dt
from email.utils import parsedate_to_datetime
from typing import Any
import pydantic
IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
def parse_rfc2822_datetime(v: Any) -> dt.datetime:
"""
Parse an RFC 2822 datetime string (e.g., "Wed, 02 Oct 2002 13:00:00 GMT")
into a datetime object. If the value is already a datetime, return it as-is.
Falls back to ISO 8601 parsing if RFC 2822 parsing fails.
"""
if isinstance(v, dt.datetime):
return v
if isinstance(v, str):
try:
return parsedate_to_datetime(v)
except Exception:
pass
# Fallback to ISO 8601 parsing
return dt.datetime.fromisoformat(v.replace("Z", "+00:00"))
raise ValueError(f"Expected str or datetime, got {type(v)}")
class Rfc2822DateTime(dt.datetime):
"""A datetime subclass that parses RFC 2822 date strings.
On Pydantic V1, uses __get_validators__ for pre-validation.
On Pydantic V2, uses __get_pydantic_core_schema__ for BeforeValidator-style parsing.
"""
@classmethod
def __get_validators__(cls): # type: ignore[no-untyped-def]
yield parse_rfc2822_datetime
@classmethod
def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: # type: ignore[override]
from pydantic_core import core_schema
return core_schema.no_info_before_validator_function(parse_rfc2822_datetime, core_schema.datetime_schema())
def serialize_datetime(v: dt.datetime) -> str:
"""
Serialize a datetime including timezone info.
Uses the timezone info provided if present, otherwise uses the current runtime's timezone info.
UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00.
"""
def _serialize_zoned_datetime(v: dt.datetime) -> str:
if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None):
# UTC is a special case where we use "Z" at the end instead of "+00:00"
return v.isoformat().replace("+00:00", "Z")
else:
# Delegate to the typical +/- offset format
return v.isoformat()
if v.tzinfo is not None:
return _serialize_zoned_datetime(v)
else:
local_tz = dt.datetime.now().astimezone().tzinfo
localized_dt = v.replace(tzinfo=local_tz)
return _serialize_zoned_datetime(localized_dt)