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 })
|
return NextResponse.json({ error: 'Invalid tier' }, { status: 400 })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Widen the CalDAV query by ±6 h around the day to handle DST edge cases.
|
// Cover the full ET day: EDT=UTC-4 (midnight=4AM UTC), EST=UTC-5 (midnight=5AM UTC).
|
||||||
const dayStart = new Date(`${date}T00:00:00Z`)
|
// Start at 3AM UTC (before midnight ET in any DST state) to avoid pulling in
|
||||||
dayStart.setUTCHours(dayStart.getUTCHours() - 6)
|
// afternoon events from the previous calendar day.
|
||||||
const dayEnd = new Date(`${date}T23:59:59Z`)
|
const dayStart = new Date(`${date}T03:00:00Z`)
|
||||||
dayEnd.setUTCHours(dayEnd.getUTCHours() + 6)
|
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
|
const CALDAV_TIMEOUT_MS = 6_000
|
||||||
|
|
||||||
|
|||||||
@ -60,23 +60,6 @@ function toIcalDate(d: Date): string {
|
|||||||
return d.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '')
|
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 {
|
export interface CalendarLineItem {
|
||||||
name: string
|
name: string
|
||||||
@ -152,8 +135,8 @@ export async function createDeliveryEvent(params: {
|
|||||||
'BEGIN:VEVENT',
|
'BEGIN:VEVENT',
|
||||||
`UID:${uid}`,
|
`UID:${uid}`,
|
||||||
`DTSTAMP:${toIcalDate(new Date())}`,
|
`DTSTAMP:${toIcalDate(new Date())}`,
|
||||||
`DTSTART;TZID=America/New_York:${toIcalDateET(startTime)}`,
|
`DTSTART:${toIcalDate(startTime)}`,
|
||||||
`DTEND;TZID=America/New_York:${toIcalDateET(endTime)}`,
|
`DTEND:${toIcalDate(endTime)}`,
|
||||||
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
||||||
foldLine(`LOCATION:${icalEscape(address)}`),
|
foldLine(`LOCATION:${icalEscape(address)}`),
|
||||||
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
||||||
@ -213,8 +196,8 @@ export async function createPickupEvent(params: {
|
|||||||
'BEGIN:VEVENT',
|
'BEGIN:VEVENT',
|
||||||
`UID:${uid}`,
|
`UID:${uid}`,
|
||||||
`DTSTAMP:${toIcalDate(new Date())}`,
|
`DTSTAMP:${toIcalDate(new Date())}`,
|
||||||
`DTSTART;TZID=America/New_York:${toIcalDateET(startTime)}`,
|
`DTSTART:${toIcalDate(startTime)}`,
|
||||||
`DTEND;TZID=America/New_York:${toIcalDateET(endTime)}`,
|
`DTEND:${toIcalDate(endTime)}`,
|
||||||
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
foldLine(`SUMMARY:${icalEscape(customerName)}`),
|
||||||
'LOCATION:Beach Party Balloons\\, 554 Boston Post Rd\\, Milford CT',
|
'LOCATION:Beach Party Balloons\\, 554 Boston Post Rd\\, Milford CT',
|
||||||
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
foldLine(`DESCRIPTION:${icalEscape(descParts)}`),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user