feat: Schedule bookings
This commit is contained in:
83
app/utils/toronto.ts
Normal file
83
app/utils/toronto.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
export const TZ = 'America/Toronto'
|
||||
|
||||
/** YYYY-MM-DD for right now in Toronto */
|
||||
export function todayToronto(): string {
|
||||
return toDateToronto(new Date())
|
||||
}
|
||||
|
||||
/** YYYY-MM-DD in Toronto for any Date */
|
||||
export function toDateToronto(date: Date): string {
|
||||
return new Intl.DateTimeFormat('en-CA', { timeZone: TZ }).format(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add n calendar days to a YYYY-MM-DD string.
|
||||
* Uses noon UTC so the shift is always exactly 24 h and never crosses
|
||||
* a Toronto DST boundary ambiguously.
|
||||
*/
|
||||
export function addDays(isoDate: string, n: number): string {
|
||||
const d = new Date(isoDate + 'T12:00:00Z')
|
||||
d.setUTCDate(d.getUTCDate() + n)
|
||||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
/** HH:MM in Toronto for a UTC ISO string */
|
||||
export function fmtTime(utcIso: string): string {
|
||||
return new Date(utcIso).toLocaleTimeString('en-CA', {
|
||||
timeZone: TZ,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
}
|
||||
|
||||
/** "Wednesday, April 20" style label for a YYYY-MM-DD string */
|
||||
export function fmtDateLong(isoDate: string): string {
|
||||
return new Date(isoDate + 'T12:00:00Z').toLocaleDateString('en-CA', {
|
||||
timeZone: TZ,
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
/** { weekday: "Wed", short: "Apr 20" } for a YYYY-MM-DD string */
|
||||
export function fmtDayHeader(isoDate: string): { weekday: string; short: string } {
|
||||
const d = new Date(isoDate + 'T12:00:00Z')
|
||||
return {
|
||||
weekday: d.toLocaleDateString('en-CA', { timeZone: TZ, weekday: 'short' }),
|
||||
short: d.toLocaleDateString('en-CA', { timeZone: TZ, month: 'short', day: 'numeric' }),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* YYYY-MM-DD of the Monday that starts the ISO week containing isoDate.
|
||||
* Uses the en-CA weekday name to determine day-of-week in Toronto.
|
||||
*/
|
||||
export function weekMonday(isoDate: string): string {
|
||||
const d = new Date(isoDate + 'T12:00:00Z')
|
||||
const wd = new Intl.DateTimeFormat('en-CA', { timeZone: TZ, weekday: 'short' }).format(d)
|
||||
const offsets: Record<string, number> = { Mon: 0, Tue: 1, Wed: 2, Thu: 3, Fri: 4, Sat: 5, Sun: 6 }
|
||||
const offset = offsets[wd] ?? 0
|
||||
const monday = new Date(d)
|
||||
monday.setUTCDate(d.getUTCDate() - offset)
|
||||
return monday.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
/** All 7 YYYY-MM-DD strings for the ISO week containing isoDate (Mon → Sun) */
|
||||
export function weekDates(isoDate: string): string[] {
|
||||
const mon = weekMonday(isoDate)
|
||||
return Array.from({ length: 7 }, (_, i) => addDays(mon, i))
|
||||
}
|
||||
|
||||
/**
|
||||
* UTC range string pair that safely covers all Toronto-local timestamps
|
||||
* falling within the isoFrom…isoTo date range.
|
||||
* Adds a 1-day buffer on each side so client-side date filtering is authoritative.
|
||||
*/
|
||||
export function utcRange(isoFrom: string, isoTo: string): { from: string; to: string } {
|
||||
return {
|
||||
from: addDays(isoFrom, -1) + 'T00:00:00Z',
|
||||
to: addDays(isoTo, +1) + 'T23:59:59Z',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user