UNPKG

icalendar-events

Version:

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

56 lines (55 loc) 2.67 kB
import { Duration, Interval } from 'luxon'; import { parseDateString } from './parse-ical-datetime.js'; export function parseICalPeriod(line) { let intervalList = []; const lineUC = line.trim().toUpperCase(); if (lineUC.includes("VALUE=PERIOD")) { // Case RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H // or RDATE;VALUE=PERIOD;TZID=Asia/Tokyo:20230918T090000/20230918T100000,20230920T140000/20230920T150000,20230922T090000/20230922T100000 // or RDATE;VALUE=PERIOD;TZID=Asia/Tokyo:20230918T090000/20230918T100000,20230920T140000/20230920T150000,20230922T090000/PT1H // for example const parts = line.split(":"); if (parts.length < 2) throw new Error(`parseICalDateTime: Invalid DateTime ${line}`); let tzid = process.env.ICALEVENTS_LOCAL_TZ; const firstPartUC = parts[0].replace(/tzid/, "TZID"); if (firstPartUC.includes("TZID=")) { tzid = firstPartUC.split("TZID=")[1].split(";")[0].split(":")[0].trim(); } // Multiple values separated by "," parts[1].split(",").forEach((dateString) => { intervalList.push(parsePeriodString(dateString, tzid)); }); } else { throw new Error(`parseICalPeriod: parsed data is not a period: ${line}`); } if (intervalList.length === 0 || !intervalList.every((interval) => interval !== null && interval.isValid)) { throw new Error(`parseICalPeriod: Invalid Period ${line}`); } return intervalList; } function parsePeriodString(periodString, tzid) { if (!periodString.includes("/")) throw new Error(`parsePeriodString: period could not be parsed: ${periodString}`); const parts = periodString.split("/"); if (parts.length < 2) throw new Error(`parsePeriodString: invalid period: ${periodString}`); const periodStart = parseDateString(parts[0], tzid); let interval = null; const secondPart = parts[1].trim().toUpperCase(); if (secondPart.startsWith('P')) { // Case period-start of the form 19970101T180000Z/PT5H30M const duration = Duration.fromISO(secondPart); const periodEnd = periodStart.plus(duration); interval = Interval.fromDateTimes(periodStart, periodEnd); } else { // Case period-explicit of the form 19970101T180000Z/19970102T070000Z const periodEnd = parseDateString(secondPart, tzid); interval = Interval.fromDateTimes(periodStart, periodEnd); } if (!interval || !interval.isValid) throw new Error(`parsePeriodString: invalid period: ${periodString}`); return interval; }