Напоминалки: конвертация между часовыми поясами (reminders.py)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,3 +63,17 @@ def compute_next_occurrence(anchor: datetime, recurrence: str, after: datetime)
|
|||||||
day = _clamp_day(year, anchor.month, anchor.day)
|
day = _clamp_day(year, anchor.month, anchor.day)
|
||||||
candidate = candidate.replace(year=year, day=day)
|
candidate = candidate.replace(year=year, day=day)
|
||||||
return candidate
|
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))
|
||||||
|
|||||||
+25
-1
@@ -1,5 +1,6 @@
|
|||||||
from datetime import datetime
|
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():
|
def test_parse_reminder_datetime_valid():
|
||||||
@@ -46,3 +47,26 @@ def test_compute_next_unknown_recurrence_raises():
|
|||||||
import pytest
|
import pytest
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
compute_next_occurrence(datetime(2026, 1, 1, 9, 0), "daily", after=datetime(2026, 1, 1, 9, 0))
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user