dodil/erp-brick-ar
DODIL ERP brick: accounts receivable
src/brick_ar/infrastructure/business_time.py
"""Business time: which time zone is in force, chosen by the BUSINESS date, never by the UTC date.
Walking the dated business_timezone rows in order, a row takes over once the business date in the zone BEFORE it has reached
the row's valid_from. (By the UTC date a row dated tomorrow could be accepted as future and be in force at once, at 18:00 in
Pago_Pago.) Switching to a zone that is behind the old one never moves today backwards: today is at least the date it was in
the zone being left.
"""
from datetime import date, datetime
from typing import Optional
from zoneinfo import ZoneInfo
DEFAULT_ZONE = "Europe/London"
EPOCH = date(1970, 1, 1)
def resolve(rows: list, now: datetime, default: str = DEFAULT_ZONE) -> tuple:
"""(zone name, today) from the business_timezone rows ({valid_from, value}, any order) at an instant."""
name, from_a_row = default, False
floor: Optional[date] = None
for row in sorted(rows, key=lambda r: r["valid_from"]):
left = now.astimezone(ZoneInfo(name)).date()
if row["valid_from"] <= EPOCH or row["valid_from"] <= left:
if row["value"] and row["value"] != name:
if from_a_row: # leaving the unset default is not a move of a zone the business chose
floor = left if floor is None else max(floor, left)
name, from_a_row = row["value"], True
else:
break
today = now.astimezone(ZoneInfo(name)).date()
return name, today if floor is None else max(today, floor)