diff --git a/my/calendar/holidays.py b/my/calendar/holidays.py index 8e0c206..567ddad 100644 --- a/my/calendar/holidays.py +++ b/my/calendar/holidays.py @@ -6,14 +6,24 @@ REQUIRES = [ ] from datetime import date, datetime, timedelta +from functools import lru_cache from typing import Union +from ..core.time import zone_to_countrycode -# TODO would be nice to do it dynamically depending on the past timezones... -from workalendar.europe import UnitedKingdom # type: ignore -cal = UnitedKingdom() -# TODO that should depend on country/'location' of residence I suppose? +@lru_cache(1) +def _calendar(): + from workalendar.registry import registry # type: ignore + # todo switch to using time.tz.main once _get_tz stabilizes? + from ..time.tz import via_location as LTZ + # TODO would be nice to do it dynamically depending on the past timezones... + tz = LTZ._get_tz(datetime.now()) + assert tz is not None + + code = zone_to_countrycode(tz.zone) + Cal = registry.get_calendars()[code] + return Cal() # todo move to common? DateIsh = Union[datetime, date, str] @@ -29,7 +39,7 @@ def as_date(dd: DateIsh) -> date: def is_holiday(d: DateIsh) -> bool: day = as_date(d) - return not cal.is_working_day(day) + return not _calendar().is_working_day(day) def is_workday(d: DateIsh) -> bool: diff --git a/my/core/time.py b/my/core/time.py index 9f8d958..7f856b0 100644 --- a/my/core/time.py +++ b/my/core/time.py @@ -15,3 +15,18 @@ tz_lookup['UTC'] = pytz.utc # ugh. otherwise it'z Zulu... @lru_cache(None) def abbr_to_timezone(abbr: str) -> tzinfo: return tz_lookup[abbr] + + +def zone_to_countrycode(zone: str) -> str: + # todo make optional? + return _zones_to_countrycode()[zone] + + +@lru_cache(1) +def _zones_to_countrycode(): + # https://stackoverflow.com/a/13020785/706389 + res = {} + for countrycode, timezones in pytz.country_timezones.items(): + for timezone in timezones: + res[timezone] = countrycode + return res diff --git a/my/time/tz/via_location.py b/my/time/tz/via_location.py index c38405e..19de460 100644 --- a/my/time/tz/via_location.py +++ b/my/time/tz/via_location.py @@ -119,6 +119,7 @@ def _get_home_tz(loc) -> Optional[pytz.BaseTzInfo]: return pytz.timezone(zone) +# TODO expose? to main as well? def _get_tz(dt: datetime) -> Optional[pytz.BaseTzInfo]: res = _get_day_tz(d=dt.date()) if res is not None: diff --git a/tests/calendar.py b/tests/calendar.py index 5264313..f897efe 100644 --- a/tests/calendar.py +++ b/tests/calendar.py @@ -1,6 +1,19 @@ +from pathlib import Path + +import pytest # type: ignore + from my.calendar.holidays import is_holiday def test() -> None: assert is_holiday('20190101') assert not is_holiday('20180601') + assert is_holiday('20200906') # national holiday in Bulgaria + + +@pytest.fixture(autouse=True) +def prepare(tmp_path: Path): + from . import tz + # todo meh. fixtures can't be called directly? + orig = tz.prepare.__wrapped__ # type: ignore + yield from orig(tmp_path) diff --git a/tests/tz.py b/tests/tz.py index 43e2ed8..5d3083c 100644 --- a/tests/tz.py +++ b/tests/tz.py @@ -29,9 +29,12 @@ def test_future() -> None: def test_tz() -> None: + # todo hmm, the way it's implemented at the moment, never returns None? + # not present in the test data tz = LTZ._get_tz(D('20200101 10:00:00')) - assert tz is None + assert tz is not None + assert tz.zone == 'Europe/Sofia' tz = LTZ._get_tz(D('20170801 11:00:00')) assert tz is not None @@ -70,7 +73,7 @@ def prepare(tmp_path: Path): class location: class home: - current = (1.0, 1.0) + current = (42.697842, 23.325973) # Bulgaria, Sofia past = [ ((40.7128, -74.0060), '2005-12-04'), # NY ]