HPI/my/time/tz/common.py

48 lines
1.6 KiB
Python

from datetime import datetime
from typing import Callable, Literal, cast
from my.core import datetime_aware
'''
Depending on the specific data provider and your level of paranoia you might expect different behaviour.. E.g.:
- if your objects already have tz info, you might not need to call localize() at all
- it's safer when either all of your objects are tz aware or all are tz unware, not a mixture
- you might trust your original timezone, or it might just be UTC, and you want to use something more reasonable
'''
TzPolicy = Literal[
'keep' , # if datetime is tz aware, just preserve it
'convert', # if datetime is tz aware, convert to provider's tz
'throw' , # if datetime is tz aware, throw exception
# todo 'warn'? not sure if very useful
]
# backwards compatibility
Policy = TzPolicy
def default_policy() -> TzPolicy:
try:
from my.config import time as user_config
return cast(TzPolicy, user_config.tz.policy)
except Exception as e:
# todo meh.. need to think how to do this more carefully
# rationale: do not mess with user's data unless they want
return 'keep'
def localize_with_policy(
lfun: Callable[[datetime], datetime_aware],
dt: datetime,
policy: TzPolicy=default_policy()
) -> datetime_aware:
tz = dt.tzinfo
if tz is None:
return lfun(dt)
if policy == 'keep':
return dt
elif policy == 'convert':
ldt = lfun(dt.replace(tzinfo=None))
return dt.astimezone(ldt.tzinfo)
else: # policy == 'error':
raise RuntimeError(f"{dt} already has timezone information (use 'policy' argument to adjust this behaviour)")