Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion supabase/functions/diff-schedule/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions supabase/functions/diff-schedule/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@ Deno.test("localToUtc converts midnight correctly", () => {
const result = localToUtc("2026-07-11", "00:00", "Europe/Lisbon");
assertEquals(result, "2026-07-10T23:00:00.000Z");
});

Deno.test("localToUtc resolves a spring-forward wall time inside the skipped hour", () => {
// Lisbon clocks jump from 01:00 to 02:00 local at 2026-03-29T01:00:00Z, so
// "01:30" never occurs. @date-fns/tz's TZDate resolves a wall time inside
// the skipped hour using the pre-transition (+00:00) offset, i.e. as if
// DST had not yet started.
const result = localToUtc("2026-03-29", "01:30", "Europe/Lisbon");
assertEquals(result, "2026-03-29T01:30:00.000Z");
});

Deno.test("localToUtc resolves a fall-back wall time inside the repeated hour", () => {
// Lisbon clocks fall back from 02:00 to 01:00 local at 2026-10-25T01:00:00Z,
// so "01:30" occurs twice. Resolve to the later (post-transition) instant.
const result = localToUtc("2026-10-25", "01:30", "Europe/Lisbon");
assertEquals(result, "2026-10-25T01:30:00.000Z");
});
14 changes: 6 additions & 8 deletions supabase/functions/diff-schedule/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TZDate } from "npm:@date-fns/tz@1.5.0";

export function toSlug(name: string): string {
return name
.toLowerCase()
Expand All @@ -21,14 +23,10 @@ export function localToUtc(
timeStr: string,
timezone: string,
): string {
const localIso = `${dateStr}T${timeStr}:00`;
const naiveUtc = new Date(localIso + "Z");
// sv-SE locale gives "YYYY-MM-DD HH:MM:SS" — unambiguously parseable as UTC
const localInTz = new Date(
naiveUtc.toLocaleString("sv-SE", { timeZone: timezone }) + "Z",
);
const offsetMs = naiveUtc.getTime() - localInTz.getTime();
return new Date(naiveUtc.getTime() + offsetMs).toISOString();
const [year, month, day] = dateStr.split("-").map(Number);
const [hour, minute] = timeStr.split(":").map(Number);
const zoned = new TZDate(year, month - 1, day, hour, minute, 0, timezone);
return new Date(+zoned).toISOString();
}

export function utcToLocalDate(utcIso: string, timezone: string): string {
Expand Down
Loading