feat: add week boundary computation with DST handling
Made-with: Cursor
This commit is contained in:
38
src/ntr_fetcher/week.py
Normal file
38
src/ntr_fetcher/week.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
EASTERN = ZoneInfo("America/New_York")
|
||||
SHOW_DAY_DEFAULT = 2 # Wednesday (Monday=0)
|
||||
SHOW_HOUR_DEFAULT = 22
|
||||
|
||||
|
||||
def get_show_week(
|
||||
now_utc: datetime,
|
||||
show_day: int = SHOW_DAY_DEFAULT,
|
||||
show_hour: int = SHOW_HOUR_DEFAULT,
|
||||
) -> tuple[datetime, datetime]:
|
||||
"""Return (week_start_utc, week_end_utc) for the show week containing now_utc.
|
||||
|
||||
The week starts at show_day at show_hour Eastern Time and runs for 7 days.
|
||||
"""
|
||||
now_et = now_utc.astimezone(EASTERN)
|
||||
|
||||
days_since_show_day = (now_et.weekday() - show_day) % 7
|
||||
candidate_date = now_et.date() - timedelta(days=days_since_show_day)
|
||||
candidate = datetime(
|
||||
candidate_date.year,
|
||||
candidate_date.month,
|
||||
candidate_date.day,
|
||||
show_hour,
|
||||
0,
|
||||
0,
|
||||
tzinfo=EASTERN,
|
||||
)
|
||||
|
||||
if candidate > now_et:
|
||||
candidate -= timedelta(days=7)
|
||||
|
||||
week_start_utc = candidate.astimezone(timezone.utc).replace(tzinfo=timezone.utc)
|
||||
week_end_utc = (candidate + timedelta(days=7)).astimezone(timezone.utc).replace(tzinfo=timezone.utc)
|
||||
|
||||
return week_start_utc, week_end_utc
|
||||
38
tests/test_week.py
Normal file
38
tests/test_week.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ntr_fetcher.week import get_show_week, SHOW_DAY_DEFAULT, SHOW_HOUR_DEFAULT
|
||||
|
||||
|
||||
def test_mid_week_thursday():
|
||||
"""Thursday should belong to the show that started the previous Wednesday."""
|
||||
# 2025-03-13 is Thursday (2026-03-13 is Friday)
|
||||
now = datetime(2025, 3, 13, 15, 0, 0, tzinfo=timezone.utc)
|
||||
start, end = get_show_week(now, show_day=2, show_hour=22)
|
||||
assert start == datetime(2025, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
|
||||
assert end == datetime(2025, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_wednesday_before_show():
|
||||
"""Wednesday before 22:00 ET belongs to the previous week's show."""
|
||||
# 2025-03-12 is Wednesday
|
||||
now = datetime(2025, 3, 12, 20, 0, 0, tzinfo=timezone.utc)
|
||||
start, end = get_show_week(now, show_day=2, show_hour=22)
|
||||
assert start == datetime(2025, 3, 6, 3, 0, 0, tzinfo=timezone.utc)
|
||||
assert end == datetime(2025, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_wednesday_after_show_starts():
|
||||
"""Wednesday at or after 22:00 ET belongs to the new week."""
|
||||
# 2025-03-13 02:30 UTC = Wed 2025-03-12 22:30 ET
|
||||
now = datetime(2025, 3, 13, 2, 30, 0, tzinfo=timezone.utc)
|
||||
start, end = get_show_week(now, show_day=2, show_hour=22)
|
||||
assert start == datetime(2025, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
|
||||
assert end == datetime(2025, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_est_period_no_dst():
|
||||
"""January — firmly in EST (UTC-5)."""
|
||||
now = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
|
||||
start, end = get_show_week(now, show_day=2, show_hour=22)
|
||||
assert start == datetime(2026, 1, 15, 3, 0, 0, tzinfo=timezone.utc)
|
||||
assert end == datetime(2026, 1, 22, 3, 0, 0, tzinfo=timezone.utc)
|
||||
Reference in New Issue
Block a user