UNPKG

icalendar-events

Version:

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

58 lines (57 loc) 2.66 kB
import { DateTime } from 'luxon'; export function parseICalDateTime(line) { let dateTimeList = []; const lineUC = line.trim().toUpperCase(); if (lineUC.includes("UNTIL=")) { //case UNTIL=20130130 or UNTIL=20130130T230000 or UNTIL=20130130T230000Z const parts = lineUC.split("UNTIL="); if (parts.length < 2) throw new Error(`parseICalDateTime: Invalid DateTime ${line}`); // UNTIL can have only 1 value dateTimeList.push(parseDateString(parts[1])); } else if (line.includes(":")) { // Case DTSTART;TZID=Asia/Tokyo:20201027T163000 or DTSTART:20201027T163000 or DTSTART:20201027T163000Z // or RDATE;VALUE=DATE:19970304,19970504,19970704,19970904 // or EXDATE:20201027T163000Z,20201127T163000Z,20201227T163000Z // or RDATE;TZID=Asia/Tokyo:20201027T163000,20201127T163000,20201227T163000 for example // (case RDATE;VALUE=PERIOD is handled in parseICalPeriod function) const parts = line.split(":"); if (parts.length < 2) throw new Error(`parseICalDateTime: Invalid DateTime ${line}`); let tzid = process.env.ICALEVENTS_LOCAL_TZ; if (parts[0].replace(/tzid/, "TZID").includes("TZID=")) { tzid = parts[0].split("TZID=")[1].split(";")[0].split(":")[0].trim(); } // Multiple values separated by "," parts[1].split(",").forEach((dateString) => { dateTimeList.push(parseDateString(dateString, tzid)); }); } else { throw new Error(`parseICalDateTime: datetime could not be parsed ${line}`); } if (dateTimeList.length === 0 || !dateTimeList.every((dateTime) => dateTime !== null && dateTime.isValid)) { throw new Error(`parseICalDateTime: Invalid DateTime ${line}`); } return dateTimeList; } export function parseDateString(dateString, tzid) { let dateStringTrimmed = dateString.trim().toUpperCase(); if (dateStringTrimmed.match(/^\d{8}T\d{6}Z$/) !== null) { return DateTime.fromFormat(dateStringTrimmed, "yyyyMMdd'T'HHmmss'Z'", { zone: 'UTC' }); } else if (dateStringTrimmed.match(/^\d{8}T\d{6}$/) !== null) { tzid = tzid ?? 'local'; return DateTime.fromFormat(dateStringTrimmed, "yyyyMMdd'T'HHmmss", { zone: tzid }); } else if (dateStringTrimmed.match(/^\d{8}$/) !== null) { //Date so Time zone is set to UTC const date = DateTime.fromFormat(dateStringTrimmed, "yyyyMMdd", { zone: 'UTC' }); date.isDate = true; return date; } else { throw new Error(`parseICalDateTime: datetime could not be parsed: ${dateString}`); } }