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)