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
JavaScript
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}`);
}
}