UNPKG

icalendar-events

Version:

A RFC5545 compliant parser for iCalendar VEVENT with time zone support and accurate recurring events generation.

60 lines (59 loc) 2.79 kB
import { DateTime, Interval } from 'luxon'; import { VEvent } from './vevent.js'; import './luxon-extensions.js'; export class ICalendarEvents { // All Events in the given date range sorted, with reccurence expanded events; // Optional raw list of vevents sorted. For debugging purpose mostly. vevents = []; // Parse string iCalendar data and build the ICalendar vevents constructor(data, dateRange, options) { process.env.ICALEVENTS_LOCAL_TZ = options?.localTZ ?? 'local'; let range; if (!dateRange) { // Default Range is [start of current month - 1 year later] // Time Zone is set to UTC with the same time to avoid overflowing to previous or next day in local time zone const firstDate = DateTime.now().setZone('UTC', { keepLocalTime: true }).startOf('month'); const lastDate = firstDate.plus({ months: 11 }).endOf('month'); range = Interval.fromDateTimes(firstDate, lastDate); } else { range = dateRange; } if (!range || !range.isValid) throw new Error(`ICalEvents constructor: range is invalid: ${range.invalidReason}`); this.events = []; // Add the events as they are parsed // We don't read the VTIMEZONE, instead we just use the standard Olson TZID const eventsData = data.split('BEGIN:VEVENT'); eventsData.forEach((eventData) => { if (eventData.includes('END:VEVENT')) { let vevent = null; try { vevent = new VEvent(eventData); } catch (e) { console.error("ICalEvents constructor", `Could not parse VEVENT`); console.error("ICalEvents constructor", e); } // if event is not null and has a start date, expand recurrences if applicable // then push all events between firsDate and endDate in this.days if (vevent && vevent.dtstart !== undefined) { if (options?.withVEvent) this.vevents.push(vevent); // Add recurring events that fall in the range let allEvents = vevent.expandRecurrence(range, options?.includeDTSTART); this.events.push(...allEvents); } } }); this.vevents.sort((vevent1, vevent2) => { if (vevent1.dtstart === undefined || vevent2.dtstart === undefined) return 0; return vevent1.dtstart.valueOf() - vevent2.dtstart.valueOf(); }); this.events.sort((event1, event2) => { return event1.dtstart.valueOf() - event2.dtstart.valueOf(); }); } }