my.calendar.holidays: unhardcode calendar, detect it from the location data
This commit is contained in:
parent
bdb5dcd221
commit
96113ad5ae
5 changed files with 49 additions and 7 deletions
|
@ -6,14 +6,24 @@ REQUIRES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
|
from functools import lru_cache
|
||||||
from typing import Union
|
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?
|
# todo move to common?
|
||||||
DateIsh = Union[datetime, date, str]
|
DateIsh = Union[datetime, date, str]
|
||||||
|
@ -29,7 +39,7 @@ def as_date(dd: DateIsh) -> date:
|
||||||
|
|
||||||
def is_holiday(d: DateIsh) -> bool:
|
def is_holiday(d: DateIsh) -> bool:
|
||||||
day = as_date(d)
|
day = as_date(d)
|
||||||
return not cal.is_working_day(day)
|
return not _calendar().is_working_day(day)
|
||||||
|
|
||||||
|
|
||||||
def is_workday(d: DateIsh) -> bool:
|
def is_workday(d: DateIsh) -> bool:
|
||||||
|
|
|
@ -15,3 +15,18 @@ tz_lookup['UTC'] = pytz.utc # ugh. otherwise it'z Zulu...
|
||||||
@lru_cache(None)
|
@lru_cache(None)
|
||||||
def abbr_to_timezone(abbr: str) -> tzinfo:
|
def abbr_to_timezone(abbr: str) -> tzinfo:
|
||||||
return tz_lookup[abbr]
|
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
|
||||||
|
|
|
@ -119,6 +119,7 @@ def _get_home_tz(loc) -> Optional[pytz.BaseTzInfo]:
|
||||||
return pytz.timezone(zone)
|
return pytz.timezone(zone)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO expose? to main as well?
|
||||||
def _get_tz(dt: datetime) -> Optional[pytz.BaseTzInfo]:
|
def _get_tz(dt: datetime) -> Optional[pytz.BaseTzInfo]:
|
||||||
res = _get_day_tz(d=dt.date())
|
res = _get_day_tz(d=dt.date())
|
||||||
if res is not None:
|
if res is not None:
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest # type: ignore
|
||||||
|
|
||||||
from my.calendar.holidays import is_holiday
|
from my.calendar.holidays import is_holiday
|
||||||
|
|
||||||
|
|
||||||
def test() -> None:
|
def test() -> None:
|
||||||
assert is_holiday('20190101')
|
assert is_holiday('20190101')
|
||||||
assert not is_holiday('20180601')
|
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)
|
||||||
|
|
|
@ -29,9 +29,12 @@ def test_future() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_tz() -> None:
|
def test_tz() -> None:
|
||||||
|
# todo hmm, the way it's implemented at the moment, never returns None?
|
||||||
|
|
||||||
# not present in the test data
|
# not present in the test data
|
||||||
tz = LTZ._get_tz(D('20200101 10:00:00'))
|
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'))
|
tz = LTZ._get_tz(D('20170801 11:00:00'))
|
||||||
assert tz is not None
|
assert tz is not None
|
||||||
|
@ -70,7 +73,7 @@ def prepare(tmp_path: Path):
|
||||||
|
|
||||||
class location:
|
class location:
|
||||||
class home:
|
class home:
|
||||||
current = (1.0, 1.0)
|
current = (42.697842, 23.325973) # Bulgaria, Sofia
|
||||||
past = [
|
past = [
|
||||||
((40.7128, -74.0060), '2005-12-04'), # NY
|
((40.7128, -74.0060), '2005-12-04'), # NY
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue