fix: calendar event UTC times and slot query range
CalDAV DTSTART/DTEND now use UTC (Z-suffix) instead of TZID local time. Without a VTIMEZONE component, some CalDAV servers strip the TZID on return, causing ical.js to read the times as UTC — shifting every event 4 hours early and letting taken slots appear free. Slot query range changed from ±6h around UTC midnight (36-hour window) to 3AM–6AM UTC the next day (~27h) which covers the full ET business day without pulling in afternoon events from the previous day. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bb6c8a03a7
commit
175305a28f
@ -20,11 +20,12 @@ export async function GET(req: NextRequest) {
|
||||
return NextResponse.json({ error: 'Invalid tier' }, { status: 400 })
|
||||
|
||||
try {
|
||||
// Widen the CalDAV query by ±6 h around the day to handle DST edge cases.
|
||||
const dayStart = new Date(`${date}T00:00:00Z`)
|
||||
dayStart.setUTCHours(dayStart.getUTCHours() - 6)
|
||||
const dayEnd = new Date(`${date}T23:59:59Z`)
|
||||
dayEnd.setUTCHours(dayEnd.getUTCHours() + 6)
|
||||
// Cover the full ET day: EDT=UTC-4 (midnight=4AM UTC), EST=UTC-5 (midnight=5AM UTC).
|
||||
// Start at 3AM UTC (before midnight ET in any DST state) to avoid pulling in
|
||||
// afternoon events from the previous calendar day.
|
||||
const dayStart = new Date(`${date}T03:00:00Z`)
|
||||
const dayEnd = new Date(`${date}T06:00:00Z`)
|
||||
dayEnd.setUTCDate(dayEnd.getUTCDate() + 1) // next day 6AM UTC (after last EDT slot)
|
||||
|
||||
const CALDAV_TIMEOUT_MS = 6_000
|
||||
|
||||
|
||||
@ -60,23 +60,6 @@ function toIcalDate(d: Date): string {
|
||||
return d.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '')
|
||||
}
|
||||
|
||||
/** Local time in America/New_York for DTSTART/DTEND — used with TZID property */
|
||||
function toIcalDateET(d: Date): string {
|
||||
const parts = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'America/New_York',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
}).formatToParts(d)
|
||||
const p: Record<string, string> = {}
|
||||
for (const { type, value } of parts) p[type] = value
|
||||
const h = p.hour === '24' ? '00' : p.hour // Intl can emit '24' for midnight
|
||||
return `${p.year}${p.month}${p.day}T${h}${p.minute}${p.second}`
|
||||
}
|
||||
|
||||
export interface CalendarLineItem {
|
||||
name: string
|
||||
@ -152,8 +135,8 @@ export async function createDeliveryEvent(params: {
|
||||
'BEGIN:VEVENT',
|
||||
`UID:${uid}`,
|
||||
`DTSTAMP:${toIcalDate(new Date())}`,
|
||||
`DTSTART;TZID=America/New_York:${toIcalDateET(startTime)}`,
|
||||
`DTEND;TZID=America/New_York:${toIcalDateET(endTime)}`,
|
||||
`DTSTART:${toIcalDate(startTime)}`,
|
||||
`DTEND:${toIcalDate(endTime)}`,
|
||||
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
||||
foldLine(`LOCATION:${icalEscape(address)}`),
|
||||
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
||||
@ -213,8 +196,8 @@ export async function createPickupEvent(params: {
|
||||
'BEGIN:VEVENT',
|
||||
`UID:${uid}`,
|
||||
`DTSTAMP:${toIcalDate(new Date())}`,
|
||||
`DTSTART;TZID=America/New_York:${toIcalDateET(startTime)}`,
|
||||
`DTEND;TZID=America/New_York:${toIcalDateET(endTime)}`,
|
||||
`DTSTART:${toIcalDate(startTime)}`,
|
||||
`DTEND:${toIcalDate(endTime)}`,
|
||||
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
||||
'LOCATION:Beach Party Balloons\\, 554 Boston Post Rd\\, Milford CT',
|
||||
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user