Add destination time difference and live local times on trip pages
Closes #208
This commit is contained in:
parent
016039e78f
commit
ec413ac310
2 changed files with 168 additions and 0 deletions
102
web_view.py
102
web_view.py
|
|
@ -11,10 +11,13 @@ import os.path
|
|||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import typing
|
||||
from collections import defaultdict
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import flask
|
||||
import pytz
|
||||
import werkzeug
|
||||
import werkzeug.debug.tbtools
|
||||
import yaml
|
||||
|
|
@ -761,6 +764,104 @@ def get_prev_current_and_next_trip(
|
|||
return (prev_trip, current_trip, next_trip)
|
||||
|
||||
|
||||
def _timezone_name_from_datetime(value: typing.Any) -> str | None:
|
||||
"""Get IANA timezone name from a datetime value if available."""
|
||||
if not isinstance(value, datetime) or value.tzinfo is None:
|
||||
return None
|
||||
|
||||
key = getattr(value.tzinfo, "key", None)
|
||||
if isinstance(key, str):
|
||||
return key
|
||||
|
||||
zone = getattr(value.tzinfo, "zone", None)
|
||||
if isinstance(zone, str):
|
||||
return zone
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _format_offset_from_bristol(offset_minutes: int) -> str:
|
||||
"""Format offset from Bristol in +/-HH:MM."""
|
||||
if offset_minutes == 0:
|
||||
return "Same time as Bristol"
|
||||
sign = "+" if offset_minutes > 0 else "-"
|
||||
hours, mins = divmod(abs(offset_minutes), 60)
|
||||
return f"{sign}{hours:02d}:{mins:02d} vs Bristol"
|
||||
|
||||
|
||||
def get_destination_timezones(trip: Trip) -> list[StrDict]:
|
||||
"""Build destination timezone metadata for the trip page."""
|
||||
per_location: dict[tuple[str, str], list[str]] = defaultdict(list)
|
||||
for item in trip.accommodation + trip.conferences + trip.events:
|
||||
location = item.get("location")
|
||||
country = item.get("country")
|
||||
if not isinstance(location, str) or not isinstance(country, str):
|
||||
continue
|
||||
|
||||
key = (location, country.lower())
|
||||
timezone_name = item.get("timezone")
|
||||
if isinstance(timezone_name, str):
|
||||
per_location[key].append(timezone_name)
|
||||
|
||||
for field in (
|
||||
"from",
|
||||
"to",
|
||||
"date",
|
||||
"start",
|
||||
"end",
|
||||
"attend_start",
|
||||
"attend_end",
|
||||
):
|
||||
candidate = _timezone_name_from_datetime(item.get(field))
|
||||
if candidate:
|
||||
per_location[key].append(candidate)
|
||||
|
||||
home_now = datetime.now(ZoneInfo("Europe/London"))
|
||||
destination_times: list[StrDict] = []
|
||||
|
||||
for location, country in trip.locations():
|
||||
country_code = country.alpha_2.lower()
|
||||
key = (location, country_code)
|
||||
timezone_name = None
|
||||
|
||||
for candidate in per_location.get(key, []):
|
||||
try:
|
||||
ZoneInfo(candidate)
|
||||
timezone_name = candidate
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if not timezone_name:
|
||||
country_timezones = pytz.country_timezones.get(country_code, [])
|
||||
if len(country_timezones) == 1:
|
||||
timezone_name = country_timezones[0]
|
||||
|
||||
offset_display = "Timezone unknown"
|
||||
current_time = None
|
||||
if timezone_name:
|
||||
dest_now = datetime.now(ZoneInfo(timezone_name))
|
||||
dest_offset = dest_now.utcoffset()
|
||||
home_offset = home_now.utcoffset()
|
||||
if dest_offset is not None and home_offset is not None:
|
||||
offset_minutes = int((dest_offset - home_offset).total_seconds() // 60)
|
||||
offset_display = _format_offset_from_bristol(offset_minutes)
|
||||
current_time = dest_now.strftime("%a %H:%M:%S")
|
||||
|
||||
destination_times.append(
|
||||
{
|
||||
"location": location,
|
||||
"country_name": country.name,
|
||||
"country_flag": country.flag,
|
||||
"timezone": timezone_name,
|
||||
"offset_display": offset_display,
|
||||
"current_time": current_time,
|
||||
}
|
||||
)
|
||||
|
||||
return destination_times
|
||||
|
||||
|
||||
@app.route("/trip/<start>")
|
||||
def trip_page(start: str) -> str:
|
||||
"""Individual trip page."""
|
||||
|
|
@ -802,6 +903,7 @@ def trip_page(start: str) -> str:
|
|||
format_list_with_ampersand=format_list_with_ampersand,
|
||||
holidays=agenda.holidays.get_trip_holidays(trip),
|
||||
school_holidays=agenda.holidays.get_trip_school_holidays(trip),
|
||||
destination_times=get_destination_timezones(trip),
|
||||
human_readable_delta=agenda.utils.human_readable_delta,
|
||||
trip_weather=trip_weather,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue