Fix city-local nearby station time labels
This commit is contained in:
@@ -216,6 +216,8 @@ function buildNearbyIconHtml(detail: CityDetail, station: NearbyStation) {
|
|||||||
const utcOffsetSeconds = getDetailUtcOffsetSeconds(detail);
|
const utcOffsetSeconds = getDetailUtcOffsetSeconds(detail);
|
||||||
const formatObsTime = () => {
|
const formatObsTime = () => {
|
||||||
const raw = String(station.obs_time || "").trim();
|
const raw = String(station.obs_time || "").trim();
|
||||||
|
const label = String(station.obs_time_label || "").trim();
|
||||||
|
if (station.obs_time_display_tz === "city_local" && label) return label.replace(/Z$/i, "");
|
||||||
const epochRaw = Number(station.obs_time_epoch);
|
const epochRaw = Number(station.obs_time_epoch);
|
||||||
const epochMs = Number.isFinite(epochRaw)
|
const epochMs = Number.isFinite(epochRaw)
|
||||||
? epochRaw > 1_000_000_000_000
|
? epochRaw > 1_000_000_000_000
|
||||||
@@ -226,7 +228,6 @@ function buildNearbyIconHtml(detail: CityDetail, station: NearbyStation) {
|
|||||||
const localOffsetSeconds = utcOffsetSeconds;
|
const localOffsetSeconds = utcOffsetSeconds;
|
||||||
return formatCityLocalDateTime(epochMs, localOffsetSeconds, detail.local_date, isEnglishUi);
|
return formatCityLocalDateTime(epochMs, localOffsetSeconds, detail.local_date, isEnglishUi);
|
||||||
}
|
}
|
||||||
const label = String(station.obs_time_label || "").trim();
|
|
||||||
if (label) return label.replace(/Z$/i, "");
|
if (label) return label.replace(/Z$/i, "");
|
||||||
if (!raw) return "";
|
if (!raw) return "";
|
||||||
if (raw.endsWith("Z") || raw.includes("+00:00")) {
|
if (raw.endsWith("Z") || raw.includes("+00:00")) {
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ export interface NearbyStation {
|
|||||||
obs_time?: string | null;
|
obs_time?: string | null;
|
||||||
obs_time_epoch?: number | string | null;
|
obs_time_epoch?: number | string | null;
|
||||||
obs_time_label?: string | null;
|
obs_time_label?: string | null;
|
||||||
|
obs_time_display_tz?: "city_local" | string | null;
|
||||||
age_minutes?: number | null;
|
age_minutes?: number | null;
|
||||||
time_delta_vs_anchor_minutes?: number | null;
|
time_delta_vs_anchor_minutes?: number | null;
|
||||||
sync_status?: "synced" | "near_realtime" | "lagged" | "stale" | "unknown" | string | null;
|
sync_status?: "synced" | "near_realtime" | "lagged" | "stale" | "unknown" | string | null;
|
||||||
|
|||||||
@@ -76,9 +76,10 @@ def _format_obs_time_label(
|
|||||||
value: Any,
|
value: Any,
|
||||||
epoch_value: Any = None,
|
epoch_value: Any = None,
|
||||||
display_utc_offset_seconds: Any = None,
|
display_utc_offset_seconds: Any = None,
|
||||||
|
source_utc_offset_seconds: Any = None,
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
text = str(value or "").strip()
|
text = str(value or "").strip()
|
||||||
parsed = _parse_obs_datetime(value, epoch_value)
|
parsed = _parse_obs_datetime(value, epoch_value, source_utc_offset_seconds)
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
if display_utc_offset_seconds is not None and parsed.tzinfo is not None:
|
if display_utc_offset_seconds is not None and parsed.tzinfo is not None:
|
||||||
try:
|
try:
|
||||||
@@ -160,6 +161,7 @@ def _enrich_station_timing(
|
|||||||
row.get("obs_time"),
|
row.get("obs_time"),
|
||||||
row.get("obs_time_epoch"),
|
row.get("obs_time_epoch"),
|
||||||
display_utc_offset_seconds,
|
display_utc_offset_seconds,
|
||||||
|
row.get("obs_time_utc_offset_seconds"),
|
||||||
)
|
)
|
||||||
if display_utc_offset_seconds is not None and enriched_row.get("obs_time_label"):
|
if display_utc_offset_seconds is not None and enriched_row.get("obs_time_label"):
|
||||||
enriched_row["obs_time_display_tz"] = "city_local"
|
enriched_row["obs_time_display_tz"] = "city_local"
|
||||||
|
|||||||
@@ -215,6 +215,32 @@ def test_metar_cluster_obs_time_label_uses_dst_aware_city_time():
|
|||||||
assert row["obs_time_label"] == "07:30"
|
assert row["obs_time_label"] == "07:30"
|
||||||
|
|
||||||
|
|
||||||
|
def test_metar_cluster_naive_obs_time_is_interpreted_as_utc_before_city_display():
|
||||||
|
raw = {
|
||||||
|
"metar": {
|
||||||
|
"observation_time": "2026-04-19 08:00",
|
||||||
|
"current": {"temp": 4.0},
|
||||||
|
},
|
||||||
|
"mgm_nearby": [
|
||||||
|
{
|
||||||
|
"name": "Moscow/Vnukovo",
|
||||||
|
"icao": "UUWW",
|
||||||
|
"lat": 55.59,
|
||||||
|
"lon": 37.26,
|
||||||
|
"temp": 4.0,
|
||||||
|
"obs_time": "2026-04-19 08:00",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = build_country_network_snapshot("moscow", raw)
|
||||||
|
row = snapshot["official_nearby"][0]
|
||||||
|
|
||||||
|
assert row["source_code"] == "metar_cluster"
|
||||||
|
assert row["obs_time_label"] == "11:00"
|
||||||
|
assert row["obs_time_display_tz"] == "city_local"
|
||||||
|
|
||||||
|
|
||||||
def test_china_provider_prefers_nmc_rows_when_available():
|
def test_china_provider_prefers_nmc_rows_when_available():
|
||||||
raw = {
|
raw = {
|
||||||
"metar": {
|
"metar": {
|
||||||
|
|||||||
Reference in New Issue
Block a user