fix: use explicit Eastern timezone for day boundary instead of system TZ
Build and Deploy / Build & Push (push) Successful in 2m50s
Build and Deploy / Build & Push (push) Successful in 2m50s
getTodayLocal() relied on system clock hours, which broke in the web container (TZ defaulting to UTC) — the day flipped at 11 PM EDT (3 AM UTC) instead of 3 AM Eastern. Now uses Intl.DateTimeFormat with an explicit America/New_York timezone. Also replaced all toISOString() date formatting with local-component helpers to avoid UTC conversion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+44
-16
@@ -12,26 +12,54 @@ export function parseStalenessHours(envVar: string | undefined, defaultHours: nu
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultHours;
|
||||
}
|
||||
|
||||
const APP_TIMEZONE = "America/New_York";
|
||||
|
||||
/**
|
||||
* Returns today's date as YYYY-MM-DD using local wall-clock time with a 3 AM
|
||||
* switchover. Before 3 AM local time we still consider it "yesterday", so the
|
||||
* calendar doesn't flip to the next day at midnight while people are still out
|
||||
* at the park.
|
||||
*
|
||||
* Important: `new Date().toISOString()` returns UTC, which causes the date to
|
||||
* advance at 8 PM EDT (UTC-4) or 7 PM EST (UTC-5) — too early. This helper
|
||||
* corrects that by using local year/month/day components and rolling back one
|
||||
* day when the local hour is before 3.
|
||||
* Returns today's date as YYYY-MM-DD in Eastern time with a 3 AM switchover.
|
||||
* Uses Intl.DateTimeFormat so it works regardless of the system/container TZ.
|
||||
*/
|
||||
export function getTodayLocal(): string {
|
||||
const now = new Date();
|
||||
if (now.getHours() < 3) {
|
||||
now.setDate(now.getDate() - 1);
|
||||
}
|
||||
const y = now.getFullYear();
|
||||
const m = String(now.getMonth() + 1).padStart(2, "0");
|
||||
const d = String(now.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${d}`;
|
||||
const fmt = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: APP_TIMEZONE,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "numeric",
|
||||
hour12: false,
|
||||
});
|
||||
const parts = fmt.formatToParts(now);
|
||||
const hour = parseInt(parts.find((p) => p.type === "hour")!.value, 10) % 24;
|
||||
|
||||
const target = hour < 3 ? new Date(now.getTime() - 86_400_000) : now;
|
||||
return formatDateTZ(target, APP_TIMEZONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a Date as YYYY-MM-DD in a specific IANA timezone.
|
||||
*/
|
||||
export function formatDateTZ(d: Date, tz: string): string {
|
||||
const parts = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: tz,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
}).formatToParts(d);
|
||||
const y = parts.find((p) => p.type === "year")!.value;
|
||||
const m = parts.find((p) => p.type === "month")!.value;
|
||||
const day = parts.find((p) => p.type === "day")!.value;
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a Date as YYYY-MM-DD using its local (system-timezone) components.
|
||||
* Use this instead of d.toISOString().slice(0,10) which converts to UTC.
|
||||
*/
|
||||
export function formatDateLocal(d: Date): string {
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user