From deb78bf952403249ddbd7d3a0128eca82e3febe4 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 15 Jul 2026 22:49:34 +0000 Subject: [PATCH] v0.2.5: parse live-session date format so the countdown actually posts The active-session endpoint (/api/ParkingSession) returns EndTime as "MM/DD/YYYY HH:MM:SS" (slashes, 24h, seconds), but parseApiTime only matched the past-session "MM-DD-YYYY hh:mm AM/PM" form. Hermes' Date() can't parse either, so live sessions parsed to null, got filtered out, and refreshSessionStatus cleared the notification instead of posting it (log showed "active sessions raw" but never "show native="). parseApiTime now accepts both: - or / separators, optional seconds, optional AM/PM (24h when absent). Added a "[STATUS] parsed N future-end session(s)" log. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/app.json | 4 ++-- app/src/notifications/sessionStatus.ts | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/app.json b/app/app.json index 81a63de..d7f9dba 100644 --- a/app/app.json +++ b/app/app.json @@ -3,14 +3,14 @@ "name": "BigBrainParking", "slug": "bigbrainparking", "scheme": "bigbrainparking", - "version": "0.2.4", + "version": "0.2.5", "orientation": "portrait", "userInterfaceStyle": "automatic", "newArchEnabled": true, "icon": "./assets/icon.png", "android": { "package": "top.mowden.bigbrainparking", - "versionCode": 14, + "versionCode": 15, "edgeToEdgeEnabled": true, "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", diff --git a/app/src/notifications/sessionStatus.ts b/app/src/notifications/sessionStatus.ts index 17f5e22..98d4c08 100644 --- a/app/src/notifications/sessionStatus.ts +++ b/app/src/notifications/sessionStatus.ts @@ -29,19 +29,31 @@ async function ensureExpoChannel(): Promise { }); } -/** Parse the API's "MM-DD-YYYY hh:mm AM/PM" time into a Date. */ +/** + * Parse a ParkSmarter date into a local Date. The API uses TWO formats: + * /api/Session (past): "07-15-2026 05:00 PM" dashes, 12h, AM/PM + * /api/ParkingSession (live): "07/15/2026 17:00:00" slashes, 24h, seconds + * Both are device-local wall-clock time. Hermes' Date() can't parse either, so + * we build the Date from explicit components. + */ export function parseApiTime(s?: string | null): Date | null { if (!s) return null; - const m = String(s).match(/(\d{1,2})-(\d{1,2})-(\d{4})\s+(\d{1,2}):(\d{2})\s*(AM|PM)/i); + const m = String(s).match( + /(\d{1,2})[-/](\d{1,2})[-/](\d{4})\s+(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(AM|PM)?/i, + ); if (!m) { const d = new Date(s); return Number.isNaN(+d) ? null : d; } let hr = parseInt(m[4], 10); - const pm = /pm/i.test(m[6]); - if (pm && hr !== 12) hr += 12; - if (!pm && hr === 12) hr = 0; - return new Date(+m[3], +m[1] - 1, +m[2], hr, +m[5]); + const ap = m[7]; + if (ap) { + const pm = /pm/i.test(ap); + if (pm && hr !== 12) hr += 12; + if (!pm && hr === 12) hr = 0; + } + const sec = m[6] ? parseInt(m[6], 10) : 0; + return new Date(+m[3], +m[1] - 1, +m[2], hr, +m[5], sec); } function fmtLeft(min: number): string { @@ -123,6 +135,7 @@ export async function refreshSessionStatus(): Promise { .map((s: any) => ({ s, end: parseApiTime(s.EndTime ?? s.EndTimeDisplay) })) .filter((x) => x.end != null && x.end.getTime() > Date.now()) .sort((a, b) => a.end!.getTime() - b.end!.getTime()); + logLine(`[STATUS] parsed ${withEnd.length} future-end session(s) of ${(res.ParkingSession ?? []).length}`); if (withEnd.length === 0) { await clearSessionStatus(); return;