diff --git a/reminders.py b/reminders.py index 975820a..2759254 100644 --- a/reminders.py +++ b/reminders.py @@ -63,3 +63,17 @@ def compute_next_occurrence(anchor: datetime, recurrence: str, after: datetime) day = _clamp_day(year, anchor.month, anchor.day) candidate = candidate.replace(year=year, day=day) return candidate + + +def server_utc_offset_hours() -> float: + """Собственное смещение сервера от UTC в часах. Часовой пояс сервера нигде + явно не настроен (может быть любым в зависимости от хостинга), поэтому + вычисляется на лету через локальную таймзону ОС.""" + offset = datetime.now().astimezone().utcoffset() + return offset.total_seconds() / 3600 if offset else 0.0 + + +def convert_time(dt: datetime, from_offset: float, to_offset: float) -> datetime: + """Переводит наивный datetime из одного часового пояса (смещение от UTC + в часах) в другой — просто сдвигает время на разницу смещений.""" + return dt + timedelta(hours=(to_offset - from_offset)) diff --git a/tests/test_reminders.py b/tests/test_reminders.py index 0e5fd93..d30ded2 100644 --- a/tests/test_reminders.py +++ b/tests/test_reminders.py @@ -1,5 +1,6 @@ from datetime import datetime -from reminders import parse_reminder_datetime, compute_next_occurrence +from reminders import parse_reminder_datetime, compute_next_occurrence, \ + server_utc_offset_hours, convert_time def test_parse_reminder_datetime_valid(): @@ -46,3 +47,26 @@ def test_compute_next_unknown_recurrence_raises(): import pytest with pytest.raises(ValueError): compute_next_occurrence(datetime(2026, 1, 1, 9, 0), "daily", after=datetime(2026, 1, 1, 9, 0)) + + +def test_server_utc_offset_hours_in_sane_range(): + """Собственное смещение сервера от UTC — разумное число часов (реальные пояса Земли).""" + offset = server_utc_offset_hours() + assert -12 <= offset <= 14 + + +def test_convert_time_to_utc(): + dt = datetime(2027, 3, 15, 9, 0) # 09:00 по времени автора (UTC+3, Москва) + result = convert_time(dt, from_offset=3, to_offset=0) # переводим в UTC + assert result == datetime(2027, 3, 15, 6, 0) + + +def test_convert_time_crosses_midnight_backwards(): + dt = datetime(2027, 3, 15, 1, 0) # 01:00 по времени автора (UTC+5, Екатеринбург) + result = convert_time(dt, from_offset=5, to_offset=-2) + assert result == datetime(2027, 3, 14, 18, 0) # переходит на предыдущие сутки + + +def test_convert_time_same_offset_is_noop(): + dt = datetime(2027, 1, 1, 12, 0) + assert convert_time(dt, from_offset=3, to_offset=3) == dt