Giter Site home page Giter Site logo

node-ical's Introduction

node-ical

Build NPM version Downloads Contributors License Donate GitHub stars

A minimal iCalendar/ICS (http://tools.ietf.org/html/rfc5545) parser for Node.js. This module is a direct fork of the ical.js module by Peter Braden (https://github.com/peterbraden/ical.js) which is primarily targeted for parsing iCalender/ICS files in a pure JavaScript environment. (ex. within the browser itself) This node-ical module however, primarily targets Node.js use and allows for more flexible APIs and interactions within a Node environment. (like filesystem access!)

Install

node-ical is availble on npm:

npm install node-ical

API

The API has now been broken into three sections:

sync provides synchronous API functions. These are easy to use but can block the event loop and are not recommended for applications that need to serve content or handle events.

async provides proper asynchronous support for iCal parsing. All functions will either return a promise for async/await or use a callback if one is provided.

autodetect provides a mix of both for backwards compatibility with older node-ical applications.

All API functions are documented using JSDoc in the node-ical.js file. This allows for IDE hinting!

sync

// import ical
const ical = require('node-ical');

// use the sync function parseFile() to parse this ics file
const events = ical.sync.parseFile('example-calendar.ics');
// loop through events and log them
for (const event of Object.values(events)) {
    console.log(
        'Summary: ' + event.summary +
        '\nDescription: ' + event.description +
        '\nStart Date: ' + event.start.toISOString() +
        '\n'
    );
};

// or just parse some iCalendar data directly
const directEvents = ical.sync.parseICS(`
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:Hey look! An example event!
DTSTART;TZID=America/New_York:20130802T103400
DTEND;TZID=America/New_York:20130802T110400
LOCATION:1000 Broadway Ave.\, Brooklyn
DESCRIPTION: Do something in NY.
STATUS:CONFIRMED
UID:7014-1567468800-1567555199@[email protected]
END:VEVENT
END:VCALENDAR
`);
// log the ids of these events
console.log(Object.keys(directEvents));

async

// import ical
const ical = require('node-ical');

// do stuff in an async function
;(async () => {
    // load and parse this file without blocking the event loop
    const events = await ical.async.parseFile('example-calendar.ics');

    // you can also use the async lib to download and parse iCal from the web
    const webEvents = await ical.async.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics');
    // also you can pass options to axios.get() (optional though!)
    const headerWebEvents = await ical.async.fromURL(
        'http://lanyrd.com/topics/nodejs/nodejs.ics',
        { headers: { 'User-Agent': 'API-Example / 1.0' } }
    );

    // parse iCal data without blocking the main loop for extra-large events
    const directEvents = await ical.async.parseICS(`
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:Hey look! An example event!
DTSTART;TZID=America/New_York:20130802T103400
DTEND;TZID=America/New_York:20130802T110400
DESCRIPTION: Do something in NY.
UID:7014-1567468800-1567555199@[email protected]
END:VEVENT
END:VCALENDAR
    `);
})()
    .catch(console.error.bind());

// old fashioned callbacks cause why not

// parse a file with a callback
ical.async.parseFile('example-calendar.ics', function(err, data) {
    if (err) {
        console.error(err);
        process.exit(1);
    }
    console.log(data);
});

// or a URL
ical.async.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', function(err, data) { console.log(data); });

// or directly
ical.async.parseICS(`
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:Hey look! An example event!
DTSTART;TZID=America/New_York:20130802T103400
DTEND;TZID=America/New_York:20130802T110400
DESCRIPTION: Do something in NY.
UID:7014-1567468800-1567555199@[email protected]
END:VEVENT
END:VCALENDAR
`, function(err, data) { console.log(data); });

autodetect

These are the old API examples, which still work and will be converted to the new API automatically. Functions with callbacks provided will also have better performance over the older versions even if they use the old API.

Parses a string with ICS content in sync. This can block the event loop on big files.

const ical = require('node-ical');
ical.parseICS(str);

Parses a string with ICS content in async to prevent the event loop from being blocked.

const ical = require('node-ical');
ical.parseICS(str, function(err, data) {
    if (err) console.log(err);
    console.log(data);
});

Parses a string with an ICS file in sync. This can block the event loop on big files.

const ical = require('node-ical');
const data = ical.parseFile(filename);

Parses a string with an ICS file in async to prevent event loop from being blocked.

const ical = require('node-ical');
const data = ical.parseFile(filename, function(err, data) {
    if (err) console.log(err);
    console.log(data);
});

Reads in the specified iCal file from the URL, parses it and returns the parsed data.

const ical = require('node-ical');
ical.fromURL(url, options, function(err, data) {
    if (err) console.log(err);
    console.log(data);
});

Use the axios library to get the specified URL (opts gets passed on to the axios.get() call), and call the function with the result. (either an error or the data)

Example 1 - Print list of upcoming node conferences (see example.js) (parses the file synchronous)

const ical = require('node-ical');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function (err, data) {
    for (let k in data) {
        if (data.hasOwnProperty(k)) {
            const ev = data[k];
            if (data[k].type == 'VEVENT') {
                console.log(`${ev.summary} is in ${ev.location} on the ${ev.start.getDate()} of ${months[ev.start.getMonth()]} at ${ev.start.toLocaleTimeString('en-GB')}`);
            }
        }
    }
});

Recurrence rule (RRule)

Recurrence rule will be created with timezone if present in DTSTART

To get correct date from recurrences in the recurrence rule, you need to take the original timezone and your local timezone into account

If no timezone were provided when recurrence rule were created, recurrence dates should take original start timezoneoffset and the current dates timezoneoffset into account

const ical = require('node-ical');
const moment = require('moment-timezone');

ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function (err, data) {
    for (let k in data) {
        if (!Object.prototype.hasOwnProperty.call(data, k)) continue;

        const event = data[k];
        if (event.type !== 'VEVENT' || !event.rrule) continue;
        
        const dates = event.rrule.between(new Date(2021, 0, 1, 0, 0, 0, 0), new Date(2021, 11, 31, 0, 0, 0, 0))
        if (dates.length === 0) continue;

        console.log('Summary:', event.summary);
        console.log('Original start:', event.start);
        console.log('RRule start:', `${event.rrule.origOptions.dtstart} [${event.rrule.origOptions.tzid}]`)

        dates.forEach(date => {
            let newDate
            if (event.rrule.origOptions.tzid) {
                // tzid present (calculate offset from recurrence start)
                const dateTimezone = moment.tz.zone('UTC')
                const localTimezone = moment.tz.guess()
                const tz = event.rrule.origOptions.tzid === localTimezone ? event.rrule.origOptions.tzid : localTimezone
                const timezone = moment.tz.zone(tz)
                const offset = timezone.utcOffset(date) - dateTimezone.utcOffset(date)
                newDate = moment(date).add(offset, 'minutes').toDate()
            } else {
                // tzid not present (calculate offset from original start)
                newDate = new Date(date.setHours(date.getHours() - ((event.start.getTimezoneOffset() - date.getTimezoneOffset()) / 60)))
            }
            const start = moment(newDate)
            console.log('Recurrence start:', start)
        })

        console.log('-----------------------------------------------------------------------------------------');
    }
});

node-ical's People

Contributors

apollon77 avatar bzabos avatar chernjie avatar dependabot[bot] avatar ericlathrop avatar f8k8 avatar gpailler avatar jens-maus avatar jfrumar avatar jonnytest1 avatar leonrodenburg avatar lilyball avatar maksimluzik avatar mbalfour avatar michmich avatar ndaversa avatar nhubbard avatar nlang avatar peterbraden avatar powersource avatar runely avatar sdetweil avatar sedenardi avatar snyk-bot avatar sujal avatar super132 avatar takkaria avatar tenbits avatar twonky4 avatar wodka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

node-ical's Issues

No toISOString function in exdate[name]

This is thrown when an EXDATE is date only: EXDATE:20400116

TypeError: No toISOString function in exdate[name]
    at /node_modules/node-ical/ical.js:278:17
    at Array.forEach (<anonymous>)
    at Object.EXDATE (/node_modules/node-ical/ical.js:270:11)
    at Object.handleObject (/node_modules/node-ical/ical.js:521:39)
    at Object.parseLines (/node_modules/node-ical/ical.js:575:18)
    at Object.parseICS (/node_modules/node-ical/ical.js:628:12)
    at Request._callback (/node_modules/node-ical/node-ical.js:114:12)
    at Request.self.callback (/node_modules/request/request.js:185:22)
    at Request.emit (events.js:311:20)
    at Request.EventEmitter.emit (domain.js:482:12)

add attendee to type definition

currently the attendee is not typed.

Even though it is present in the data object:

[
  {
    params: {
      CUTYPE: 'INDIVIDUAL',
      ROLE: 'REQ-PARTICIPANT',
      PARTSTAT: 'NEEDS-ACTION',
      RSVP: true,
      CN: '***',
      'X-NUM-GUESTS': 0
    },
    val: 'mailto:***'
  }
]

no types for vtodo and friends

The title says it all: the types file has no mention of VTODO, VJOURNAL and others, which somewhat defeats the purpose if you're using the library for anything except just VTIMEZONE and VEVENT.

new lint github test side conflicts with local repo side

local repo says
npm run lint

 ical.js:468:24
  ✖  468:24  The catch parameter should be named err.  unicorn/catch-error-name

  node-ical.js:75:12
  ✖   75:12  The catch parameter should be named err.   

pr build test says


Error: /home/runner/work/node-ical/node-ical/node-ical.js: line 75, col 12, Error - The catch parameter `err` should be named `error`. (unicorn/catch-error-name)

Error: /home/runner/work/node-ical/node-ical/ical.js: line 468, col 24, Error - The catch parameter `err` should be named `error`. (unicorn/catch-error-name)

can't make new commit with pr build syntax

Wrong type declarations in case of parameters

I have a calender with Parameters as sumamr, description and so on.
In this case, the event parameters are not strings, they are objects.

Ical URL:
https://abfuhrtermine.awp-paf.de/WasteManagementPfaffenhofen/WasteManagementServiceServlet?ApplicationName=Calendar&SubmitAction=sync&StandortID=149316001&AboID=185329&Fra=RM;B;P;S

Example vevent:

BEGIN:VEVENT
DTSTART;VALUE=DATE:20210721
DTEND;VALUE=DATE:20210722
TRANSP:TRANSPARENT
LOCATION;LANGUAGE=de:Joseph-Fraunhofer-Str. 10, 85276 Pfaffenhofen a.d.Ilm
UID:[email protected]
DTSTAMP:20210718T091751Z
DESCRIPTION;LANGUAGE=de:Bitte stellen Sie die Behälter bis 6:00 Uhr zur Abholung bereit.
SUMMARY;LANGUAGE=de:Restmuell 
PRIORITY:9
CLASS:PUBLIC
URL:www.awp-paf.de
STATUS:CONFIRMED
END:VEVENT

The ical.js creates at line 75 (https://github.com/jens-maus/node-ical/blob/master/ical.js#L75) the following object for example for summary:

{ params: { LANGUAGE: 'de' }, val: 'Restmuell ' }

This does not match with the type declaration string.

Uncatchable Exception from rrule

we got a Sentry crash report:

https://sentry.iobroker.net/share/issue/d68908f6a8824381ab4e390108378841/

Error: Unknown RRULE property 'RRULE'
  File "/opt/iobroker/node_modules/rrule/dist/es5/webpack:/rrule/src/parsestring.ts", line 98, col 15, in null.<anonymous>
  ?, in Array.forEach
  File "/opt/iobroker/node_modules/rrule/dist/es5/webpack:/rrule/src/parsestring.ts", line 56, col 9, in parseRrule
  File "/opt/iobroker/node_modules/rrule/dist/es5/webpack:/rrule/src/parsestring.ts", line 42, col 14, in parseLine
  ?, in Array.map
  File "/opt/iobroker/node_modules/rrule/dist/es5/webpack:/rrule/src/parsestring.ts", line 7, col 41, in Function.parseString
  File "/opt/iobroker/node_modules/rrule/dist/es5/webpack:/rrule/src/rrule.ts", line 127, col 28, in Function.RRule.fromString
  File "/opt/iobroker/node_modules/node-ical/ical.js", line 415, col 30, in Object.END
    curr.rrule = rrule.fromString(rule);
  File "/opt/iobroker/node_modules/node-ical/ical.js", line 452, col 39, in Object.handleObject
    return self.objectHandlers[name](val, params, ctx, stack, line);
  File "/opt/iobroker/node_modules/node-ical/ical.js", line 499, col 18, in Object.parseLines
    ctx = self.handleObject(name, value, params, ctx, stack, l) || {};

in fact when I understood correct then https://github.com/jens-maus/node-ical/blob/master/ical.js#L415 can throw an exception ... this should be catched because of the "setImmediate" processing that exception is uncatchable else

TypeError: Cannot read property 'iana' of undefined

After update from 0.11.3 to 0.12.0 the exception TypeError: Cannot read property 'iana' of undefined is thrown when parsing an exchange ics file.
Same ics file worked fine on 0.11.3

This is happening when using async.fromURL with options {}:

const nodeIcal = require('node-ical');
let options = {};
nodeIcal.async.fromURL(url, options, (err, data) => {
    if (err) {
        console.log("Failed:", err);
    }

    console.log("Success:", data);
});

If options is not given it works, but output is string and not json

const nodeIcal = require('node-ical');
nodeIcal.async.fromURL(url, (err, data) => {
    if (err) {
        console.log("Failed:", err);
    }

    console.log("Success:", data);
});

Exception:

/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:108
  return zoneTable[msTZName].iana[0];
                             ^

TypeError: Cannot read property 'iana' of undefined
    at getIanaTZFromMS (/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:108:30)
    at /Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:175:23
    at Object.DTSTART (/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:457:32)
    at Object.handleObject (/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:483:39)
    at Object.parseLines (/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:534:18)
    at Object.parseICS (/Users/runely/source/nodeical-test/node_modules/node-ical/ical.js:587:12)
    at Request._callback (/Users/runely/source/nodeical-test/node_modules/node-ical/node-ical.js:111:12)
    at Request.self.callback (/Users/runely/source/nodeical-test/node_modules/request/request.js:185:22)
    at Request.emit (events.js:314:20)
    at Request.<anonymous> (/Users/runely/source/nodeical-test/node_modules/request/request.js:1154:10)

I've set ut a repo where you can test it out, you just need to get your own Exchange calendar url: https://github.com/runely/nodeical-test

parseICS not parse correctly multiple BEGIN:VEVENT/END:VEVENT

Hi,
I using MagicMirror calendar module and here is possibility define external calendar in ics format.
There ics parsing is done by ical.parseICS(responseData), see: https://github.com/MichMich/MagicMirror/blob/master/modules/default/calendar/calendarfetcher.js#L72.

This works fine if ical has only one BEGIN/VEVENT and END:VEVENT block (one event).
But if there has multiple blocks then this works wrong and only latest block are parsed (for example I have one week in future every day events in calendar but only latest are parsed).

You can test for example this kind ICS data:

BEGIN:VCALENDAR
VERSION;VALUE=TEXT:2.0
PRODID;VALUE=TEXT:--
CALSCALE;VALUE=TEXT:GREGORIAN
METHOD;VALUE=TEXT:PUBLISH
X-MS-OLK-FORCEINSPECTOROPEN:FALSE
NAME;VALUE=TEXT:Namedays
X-WR-CALNAME;VALUE=TEXT:Namedays
SOURCE;VALUE=URI:https://www.example.com
URL;VALUE=URI:https://www.example.com
UID;VALUE=TEXT:[email protected]
X-WR-RELCALID;VALUE=TEXT:[email protected]
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi-FI:Example
X-WR-CALDESC;VALUE=TEXT;LANGUAGE=fi-FI:Example
X-WR-TIMEZONE;VALUE=TEXT:UTC
REFRESH-INTERVAL;VALUE=DURATION:P3D
X-PUBLISHED-TTL;VALUE=DURATION:P3D
X-OWNER;VALUE=TEXT;CN=example.com:mailto:[email protected]
COLOR;VALUE=TEXT:#ffffff
X-APPLE-CALENDAR-COLOR;VALUE=TEXT:#ffffff
X-OUTLOOK-COLOR;VALUE=TEXT:#ffffff
X-FUNAMBOL-COLOR;VALUE=TEXT:#ffffff
X-LOTUS-CHARSET;VALUE=TEXT:UTF-8
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211201
DTEND;VALUE=DATE:20211202
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Oskar\, Oskari
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211202
DTEND;VALUE=DATE:20211203
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Anelma\, Unelma\, Unna
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211203
DTEND;VALUE=DATE:20211204
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Meri\, Vellamo
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211204
DTEND;VALUE=DATE:20211205
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Aira\, Airi
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211205
DTEND;VALUE=DATE:20211206
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Selma
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211206
DTEND;VALUE=DATE:20211207
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Niila\, Niilo\, Niki\, Niklas\, Niko\, Niko
 lai\, Nikolas
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211207
DTEND;VALUE=DATE:20211208
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Sampsa
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
BEGIN:VEVENT
UID;VALUE=TEXT:[email protected]
X-FUNAMBOL-ALLDAY:1
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
DTSTART;VALUE=DATE:20211208
DTEND;VALUE=DATE:20211209
SUMMARY;VALUE=TEXT;LANGUAGE=fi:Kylli\, Kyllikki
DESCRIPTION;VALUE=TEXT;LANGUAGE=fi:Updated: Wed\, 01 Dec 2021 06:41:32 
 +0000
CLASS;VALUE=TEXT:PUBLIC
CONTACT;VALUE=TEXT:[email protected]
TRANSP;VALUE=TEXT:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;VALUE=TEXT:DISABLED
STATUS;VALUE=TEXT:CONFIRMED
PRIORITY;VALUE=INTEGER:5
SEQUENCE;VALUE=INTEGER:1066
CREATED;VALUE=DATE-TIME:20211201T064132Z
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
DTSTAMP;VALUE=DATE-TIME:20211201T064132Z
END:VEVENT
LAST-MODIFIED;VALUE=DATE-TIME:20211201T064132Z
END:VCALENDAR

Include calendar attributes such as default timezone and name when parsing

Something like: For backward compatibility, create new parameter:

sync.parseFile(filename, includeCalendarAttributes).

If includeCalendarAttributes is true, add attribute
"calendarAttributes" with the values of al the calendar attributes that have a single value (versus evens and timezones which are arrays).

The request of a dependency is an expression with React

Hello there,

When I'm adding this project to React and importing it with es6 imports I get the following warning in my log:

./node_modules/node-ical/ical.js
Critical Dependency: the request of a dependency is an expression

Now I suppose this is because of the dynamic import on line 107 of ical.js. Any way to fix this?

weird calendar causes weird output

user has exported his work calendar, but only busy indications, from. Google calendar

the ical spec says UID is globally unique,
but over 600 events there are 52 UID values.

there are no RRULE entries, but each event has a recurrance-id

ical returns
uid
then a list of recurrance objects (all with the same uid in the ics file)

Offset only yields incorrect result, where offset is ambiguous

with ICS event

BEGIN:VEVENT
SUMMARY: TEST
DTSTART;TZID="(UTC-05:00) Eastern Time (US & Canada)":20201028T133000
DTEND;TZID="(UTC-05:00) Eastern Time (US & Canada)":20201028T150000
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20201015T165939Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:0
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
X-MICROSOFT-DISALLOW-COUNTER:FALSE
END:VEVENT

using the offset (-05:00) could be eastern or central time.
unfortunately in this case, parsing the Windows timezone info results in a non-standard string from the table we just added.
because the time is not UTC, we have to locate the right timezone anchor

https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values

and the darned old timezone names are off too, another lookup table..(94 entries)
the Windows to Iana table uses the new names (on the left here)
"
Eastern Standard Time | (GMT-05:00) Eastern Time (US and Canada)
"
notice the & in the event TZID and the 'and' in the reference table.
(and there are a 'few' others)

RRULE until and DTSART in different timezones, start after parse has utc

the rfc (https://tools.ietf.org/html/rfc5545#section-3.3.10) for RRULE specifies

If the "DTSTART" property is specified as a date with UTC time
or
a date with local time and time zone reference, then the

 UNTIL rule part MUST be specified as a date with UTC time.

here is the sample ics which does this correctly, but is parsed incorrectly
ical 0.8.0
rrrule 2.6.4

installed today 9/1/2020

BEGIN:VCALENDAR
METHOD:PUBLISH
PRODID:Microsoft Exchange Server 2010
VERSION:2.0
X-WR-CALNAME:Calendar
BEGIN:VTIMEZONE
TZID:Customized Time Zone
BEGIN:STANDARD
DTSTART:16010101T020000
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DESCRIPTION:[private]
RRULE:FREQ=WEEKLY;UNTIL=20201217T153500Z;INTERVAL=1;BYDAY=TU,TH;WKST=SU
UID:1173422081SZR322
SUMMARY:[private]
DTSTART;TZID=Customized Time Zone:20200825T103500
DTEND;TZID=Customized Time Zone:20200825T115000
CLASS:PUBLIC 
PRIORITY:5
DTSTAMP:20200901T175645Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION: [private]
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:1
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
X-MICROSOFT-DISALLOW-COUNTER:FALSE
END:VEVENT
END:VCALENDAR

after parse, using
const data = ical.parseICS(requestData);

the start, end and dtstart values have the wrong local time, and also with the Z timezone, instead of the specified (local) timezone
output of JSON.stringify(event)

 event={"type":"VEVENT","params":[],"description":"[private]","rrule":{"_string":null,"_cache":{"all":false,"before":[],"after":[],"between":[]},"origOptions":{"freq":2,"until":"2020-12-17T15:35:00.000Z","interval":1,"byweekday":[{"weekday":1},{"weekday":3}],"wkst":{"weekday":6},
"dtstart":"2020-08-25T09:35:00.000Z"}

,"options":{"freq":2,"until":"2020-12-17T15:35:00.000Z","interval":1,"byweekday":[1,3],"wkst":6,

"dtstart":"2020-08-25T09:35:00.000Z",

"count":null,"tzid":null,"bysetpos":null,"bymonth":null,"bymonthday":[],"bynmonthday":[],"byyearday":null,"byweekno":null,"bynweekday":null,"byhour":[9],"byminute":[35],"bysecond":[0],"byeaster":null},"timeset":[{"hour":9,"minute":35,"second":0,"millisecond":0}]},"uid":"1173422081SZR322","summary":"[private]",

"start":"2020-08-25T09:35:00.000Z",
"end":"2020-08-25T10:50:00.000Z",

"class":"PUBLIC","priority":"5","dtstamp":"2020-09-01T17:56:45.000Z","transparency":"OPAQUE","status":"CONFIRMED","sequence":"0","location":" [private]","MICROSOFT-CDO-APPT-SEQUENCE":"0","MICROSOFT-CDO-BUSYSTATUS":"BUSY","MICROSOFT-CDO-INTENDEDSTATUS":"BUSY","MICROSOFT-CDO-ALLDAYEVENT":"FALSE","MICROSOFT-CDO-IMPORTANCE":"1","MICROSOFT-CDO-INSTTYPE":"1","MICROSOFT-DONOTFORWARDMEETING":"FALSE","MICROSOFT-DISALLOW-COUNTER":"FALSE"}

ParseICS returns wrong data

Hi,
given the following ical-file

BEGIN:VCALENDAR
VERSION:2.0
PROID:jupadin
BEGIN:VTIMEZONE
TZID:Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;VALUE=DATE:20200822T000000
DTEND;VALUE=DATE:20200823T000000
RRULE:FREQ=YEARLY
SUMMARY:MyBirthday
UID:1ad611b1-d1e7-4d95-859b-b60b4f9fc453
END:VEVENT
END:VCALENDAR

the function call

ical.parseICS(data)

returns the following result

{
  "1578a0ef-7b3a-4ef8-ab09-5fd0cd247966": {
    "type": "VTIMEZONE",
    "params": [],
    "tzid": "Europe/Berlin",
    "LIC-LOCATION": "Europe/Berlin",
    "e6ac9e5c-c7e1-4c4c-9054-a69d0c1f414b": {
      "type": "DAYLIGHT",
      "params": [],
      "tzoffsetfrom": "+0100",
      "tzoffsetto": "+0200",
      "tzname": "CEST",
      "start": "1970-03-29T01:00:00.000Z",
      "datetype": "date-time",
      "rrule": "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3",
      "end": "1970-03-29T01:00:00.000Z"
    },
    "9779f127-3d4b-47da-9ca9-c7ef367a26f4": {
      "type": "STANDARD",
      "params": [],
      "tzoffsetfrom": "+0200",
      "tzoffsetto": "+0100",
      "tzname": "CET",
      "start": "1970-10-25T02:00:00.000Z",
      "datetype": "date-time",
      "rrule": "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10",
      "end": "1970-10-25T02:00:00.000Z"
    },
    "end": "2021-08-22T10:45:26.913Z"
  },
  "1ad611b1-d1e7-4d95-859b-b60b4f9fc453": {
    "type": "VEVENT",
    "params": [],
    "start": "2020-08-22T00:00:00.000Z",
    "datetype": "date",
    "end": "2020-08-22T22:00:00.000Z",
    "rrule": {
      "_cache": {
        "all": false,
        "before": [],
        "after": [],
        "between": []
      },
      "origOptions": {
        "dtstart": "2020-08-22T00:00:00.000Z",
        "freq": 0
      },
      "options": {
        "freq": 0,
        "dtstart": "2020-08-22T00:00:00.000Z",
        "interval": 1,
        "wkst": 0,
        "count": null,
        "until": null,
        "bysetpos": null,
        "bymonth": [
          8
        ],
        "bymonthday": [
          22
        ],
        "bynmonthday": [],
        "byyearday": null,
        "byweekno": null,
        "byweekday": null,
        "bynweekday": null,
        "byhour": [
          0
        ],
        "byminute": [
          0
        ],
        "bysecond": [
          0
        ],
        "byeaster": null
      }
    },
    "summary": "MyBirthday",
    "uid": "1ad611b1-d1e7-4d95-859b-b60b4f9fc453"
  }
}

where as the time zone information is not considered for the start time as one can see here

"start": "2020-08-22T00:00:00.000Z",
"end": "2020-08-22T22:00:00.000Z",

Any idea how to solve this ?

Events with no DTEND is missing end property

RFC5545, 3.6.1 (Event Component: VEVENT) states:

For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE value type but no "DTEND" nor "DURATION" property, the event's duration is taken to be one day.

For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE-TIME value type but no "DTEND" property, the event ends on the same calendar date and time of day specified by the "DTSTART" property.

When DTEND is not set on a event, the end property is missing from the event output from node-ical.

Instead the end property should be set accordingly to the rules in the RFC5545 above.


Example ics

BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:*masked-away*
X-WR-TIMEZONE:Europe/Amsterdam
BEGIN:VTIMEZONE
TZID:Europe/Amsterdam
X-LIC-LOCATION:Europe/Amsterdam
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART:20151013T193000Z
DTSTAMP:20210306T200210Z
UID:*masked-away*@google.com
CREATED:20151017T185136Z
DESCRIPTION:
LAST-MODIFIED:20151017T185136Z
LOCATION:
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:*masked-away*
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

Output from node-ical

{
  'c2c6927a-f521-41d4-aeec-bbd1aec417a5': {
    type: 'VTIMEZONE',
    params: [],
    tzid: 'Europe/Amsterdam',
    'LIC-LOCATION': 'Europe/Amsterdam',
    '3662fe0c-3e28-45de-97f3-0155ef1c7424': {
      type: 'DAYLIGHT',
      params: [],
      tzoffsetfrom: '+0100',
      tzoffsetto: '+0200',
      tzname: 'CEST',
      start: [Date],
      datetype: 'date-time',
      rrule: 'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU'
    },
    '6c58b2b3-b13a-4edd-844d-d4368b7c9383': {
      type: 'STANDARD',
      params: [],
      tzoffsetfrom: '+0200',
      tzoffsetto: '+0100',
      tzname: 'CET',
      start: [Date],
      datetype: 'date-time',
      rrule: 'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU'
    }
  },
  '*masked-away*@google.com': {
    type: 'VEVENT',
    params: [],
    start: 2015-10-13T19:30:00.000Z { tz: undefined },
    datetype: 'date-time',
    dtstamp: 2021-03-06T20:02:10.000Z { tz: undefined },
    uid: '*masked-away*@google.com',
    created: 2015-10-17T18:51:36.000Z { tz: undefined },
    description: '',
    lastmodified: 2015-10-17T18:51:36.000Z { tz: undefined },
    location: '',
    sequence: '1',
    status: 'TENTATIVE',
    summary: '*masked-away*',
    transparency: 'OPAQUE'
  }
}

0.15.1 regression

user has cal entry like this

BEGIN:VEVENT
DTSTART;TZID=EST5EDT:20220118T080000
DTEND;TZID=EST5EDT:20220118T090000
RRULE:FREQ=MONTHLY;WKST=SU;INTERVAL=1;BYMONTHDAY=18
DTSTAMP:20220425T012218Z
UID:[email protected]
CREATED:20220124T215423Z
DESCRIPTION:
LAST-MODIFIED:20220124T215423Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test entry
TRANSP:OPAQUE
END:VEVENT

in 0.13.0 it 'parsed' ok, but didn't barf , but the TZ was 'ignored'

we also added a specific dependency for luxon
"luxon": "^1.21.3",

code does

const dates = rule.between(pastLocal, futureLocal, true, limitFunction);
					Log.debug("Title: " + event.summary + ", with dates: " + JSON.stringify(dates));

we get a REALLY BIG list of dates each of which is null (0)

earch for recurring events between: Sun Apr 24 2022 20:52:33 GMT-0500 (Central Daylight Time) and Sun Apr 23 2023 23:59:59 GMT-0500 (Central Daylight Time)
[24.04.2022 20:52.33.978] [DEBUG] Title: test entry, with dates: [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null 

etc a few thousand more

parseICS producing invalid HTML in description fields

Hi, thanks for the great library.

I noticed parseICS is mangling HTML inside description fields. Given this VEVENT description with escaped quotes:

BEGIN:VEVENT
...
DESCRIPTION:<p><a href=\"http://google.com\" rel=\"noopener noreferrer\" t
 arget=\"_blank\">google.com</a></p>
X-ALT-DESC;FMTTYPE=text/html:<p><a href=\"http://google.com\" rel=\"noopen
 er noreferrer\" target=\"_blank\">google.com</a></p>
...
END:VEVENT

The output after parsing is:

{
  ...
  description: '<p><a href=\\http://google.com\\ rel=\n' +
    'oopener noreferrer\\ target=\\_blank\\>google.com</a></p>',
  'ALT-DESC': {
    params: [Object],
    val: '<p><a href=\\http://google.com\\ rel=\n' +
      'oopener noreferrer\\ target=\\_blank\\>google.com</a></p>'
  },
  ...
}

I tried unescaping the quotes but that removes them altogether, which is fine for HTML, but not for quotes in the description that aren't parent of HTML tags.

Is there any guidance on how to deal with HTML descriptions? Thanks!

rrule 2.6.5 is broken

Hi Jens,

thanks for your great node-module. I'm using it in two of my projects 👍

unfortunately the newest rrule 2.6.5 is broken.
image

I can fix it on my side, but it would be nice if you could do the same on yours 😉

kind regards,
Benjamin

Fix readme.md example

Example does not work because the loop processes timezone as if it is an event.

I will fix this.

No toISOString function in...

When calling async.fromURL(url, {}, (err, data)) this is thrown when DTSTART field in .ics file contains this: "DTSTART;TZID="tzone://Microsoft/Custom":20200830T070000"

> Deprecation warning: value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.
Arguments: 
[0] _isAMomentObject: true, _isUTC: false, _useUTC: false, _l: undefined, _i: //Microsoft/Custom:20200831T180000, _f: undefined, _strict: undefined, _locale: [object Object]
Error
    at Function.createFromInputFallback (/Users/runely/source/calendarTest/node_modules/moment/moment.js:319:25)
    at configFromString (/Users/runely/source/calendarTest/node_modules/moment/moment.js:2536:19)
    at configFromInput (/Users/runely/source/calendarTest/node_modules/moment/moment.js:2978:13)
    at prepareConfig (/Users/runely/source/calendarTest/node_modules/moment/moment.js:2961:13)
    at createFromConfig (/Users/runely/source/calendarTest/node_modules/moment/moment.js:2928:44)
    at createLocalOrUTC (/Users/runely/source/calendarTest/node_modules/moment/moment.js:3022:16)
    at createLocal (/Users/runely/source/calendarTest/node_modules/moment/moment.js:3026:16)
    at hooks (/Users/runely/source/calendarTest/node_modules/moment/moment.js:16:29)
    at Object.END (/Users/runely/source/calendarTest/node_modules/node-ical/ical.js:440:21)
    at Object.handleObject (/Users/runely/source/calendarTest/node_modules/node-ical/ical.js:514:39)
No toISOString function in curr.start //Microsoft/Custom:20200831T180000
No toISOString function in curr.start //Microsoft/Custom:20200901T160000
No toISOString function in curr.start //Microsoft/Custom:20200901T190000
No toISOString function in exdate[name] //Microsoft/Custom:20201021T180000
No toISOString function in curr.start //Microsoft/Custom:20200902T180000
No toISOString function in curr.start //Microsoft/Custom:20200902T191500
No toISOString function in curr.start //Microsoft/Custom:20200904T090000
No toISOString function in curr.start //Microsoft/Custom:20200906T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20200906T090000
No toISOString function in exdate[name] //Microsoft/Custom:20200921T193000
No toISOString function in curr.start //Microsoft/Custom:20200907T193000
No toISOString function in exdate[name] //Microsoft/Custom:20201023T090000
No toISOString function in curr.start //Microsoft/Custom:20200911T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20200913T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20200920T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20200927T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201004T090000
No toISOString function in exdate[name] //Microsoft/Custom:20201020T090000
No toISOString function in curr.start //Microsoft/Custom:20201006T090000
No toISOString function in exdate[name] //Microsoft/Custom:20201021T070000
No toISOString function in curr.start //Microsoft/Custom:20201007T070000
No toISOString function in exdate[name] //Microsoft/Custom:20201022T090000
No toISOString function in curr.start //Microsoft/Custom:20201008T090000
No toISOString function in exdate[name] //Microsoft/Custom:20201019T070000
No toISOString function in curr.start //Microsoft/Custom:20201012T070000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201014T191500
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201015T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201020T160000
No toISOString function in curr.start //Microsoft/Custom:20201026T200000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201106T090000
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201117T090000
No toISOString function in curr.start //Microsoft/Custom:20201125T191500
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201130T200000
No toISOString function in curr.start //Microsoft/Custom:20201207T200500
No toISOString function in curr.recurrenceid //Microsoft/Custom:20201231T090000
No toISOString function in curr.start //Microsoft/Custom:20210104T070000
No toISOString function in exdate[name] //Microsoft/Custom:20210119T090000
No toISOString function in curr.start //Microsoft/Custom:20210105T090000
No toISOString function in curr.start //Microsoft/Custom:20210106T070000

If timezone is not in lookup, it causes parse error

I actually ran into the issue when setting up magic mirror, but on my work outlook account, I published an ics so I could pull it in to the magic mirror dashboard. For some reason I had some past meetings that actually had "strange" timezones, that looked like Central Standard Time 1. No idea why, or how they got there. Most were sent from other people.

Issue is when parsing it fails as a whole because of these meetings. I am not sure what the correct solution would be, returning a null timezone maybe, so it does not fail to parse altogether. My gut says there should be someway to continue parsing when it is actually just an issue with the timezone not being in the lookup.

If you have a way you would like this done, I am even willing to contribute the code and open a PR. I just want to make sure it is a solution you are good with first.

Keep string properties of VCALENDAR

Many different ICAL generators, namely MS Exchange, Google Calendars, Apple Calendar, add string properties to VCALENDAR that should be kept during parsing.

E.g. X-WR-CALNAME is an extremely important string property, as well as X-WR-RELCALID, X-WR-CALDESC, X-PRIMARY-CALENDAR and actually all others.

However, current node-ical contains code that intentionally removes all string properties from VCALENDAR component. See here:

// Scan all high level object in curr and drop all strings

What's the point of that removal?

request package is deprecated

see https://github.com/request/request

We are currently replacing request with node-fetch in the magicmirror project.

node-ical is the only core dependency which is still using request, so we would be happy to get rid of this package.

Switching node-ical to node-fetch is no technical problem (could provide a PR), but the options in the fromURL function would change from request-options to node-fetch-options which would be a breaking change for the clients using this function.

Possible solutions:

  1. a new major release with breaking changes
  2. adding a new package node-ical-core with core-funtionality without the fromURL function and let node-ical use this as dependency. So we could use node-ical-core directly and/or create a third package node-ical-fetch which uses node-fetch in fromURL

Solution 2 is my favorite, what do you think about this?

uncaught exception: No toISOString function in exdate[name]

No toISOString function in exdate[name]
TypeError: No toISOString function in exdate[name] at Object.EXDATE (/opt/iobroker/node_modules/node-ical/ical.js:299:17) at Object.handleObject (/opt/iobroker/node_modules/node-ical/ical.js:571:39) at Object.parseLines (/opt/iobroker/node_modules/node-ical/ical.js:623:18) at Immediate.<anonymous> (/opt/iobroker/node_modules/node-ical/ical.js:638:16) at processImmediate (internal/timers.js:464:21)

test7 fails suddenly,

I put that ICS in the MagicMirror app which uses node-ical, and it returns a dated for next year, can't force the event in the past range

the error implies that no date was returned.

none of my console.logs from npm run test appear anywhere
how do you debug node-ical operations run via vows?

Incorrect typing of resolve type `CalendarResponse` in `async.fromURL`

👋 Hello,

We use this library for downloading ICS files from user-supplied URLs.

In real-world usage we are seeing a number of responses that do not match the typing of CalendarResponse (Record<string, CalendarComponent>).

An example is:

{
  version: '2.0',
  calscale: 'GREGORIAN',
  prodid: 'SANITISED room calendar',
  '20220813T082405Z - 25326@ord5': {
    type: 'VEVENT',
    params: [],
    uid: '20220813T082405Z - 25326@ord5',
    start: 2022-08-12T23:00:00.000Z,
    datetype: 'date-time',
    end: 2024-04-03T23:00:00.000Z,
    dtstamp: 2022-08-13T08:24:05.000Z { tz: 'Etc/UTC' },
    summary: 'RoomId: SANITISED - Room not available'
  }

You can see above a number of string values ahead of the VEVENT object.

The typing for CalendarResponse would appear to be more accurately Record<string, CalendarComponent | string>.

I'd be happy to look at raising a PR for this, if you agree that it is an issue?

"Error: Invalid URI" thrown

I get an error in Sentry when a user has entered an URI without http:// or https://. This same URL entered in Chrome (without http:// or https://) works.

Assuming the url to be http:// might be one way to go?

image

Outlook event Dates not in UTC

When parsing Outlook events, dates like event start or end are not getting converted to UTC.

var ics = 'SEE ATTACHMENT'

console.log(ical.sync.parseICS(ics))

invite-outlook.txt

vEvent component -> start is being returned as 4pm, even though it should be 2pm in UTC.

Is this correct behaviour?

Thanks! :)

optional react-native-fs require

Hi, and thanks for writing this. I'm finishing up a uni project to parse student timetables for matching and found node-ical which does the job normally, but the project currently is in react-native. I tried replacing const ical = require{'fs'}; with require{'react-native-fs'}; after yarn add react-native-fs but I get the following error:
Unable to resolve module path from /Users/osiris/Dropbox/Uni/s22021/deco3800/UQ-Pool/clientside/node_modules/node-ical/ical.js: path could not be found within the project or in these directories: node_modules/node-ical/node_modules node_modules ../node_modules

The file ical.js is there but it's not finding it when I run expo start to try on a simulator.

I'm a bit new with js node module creation and wondered if there is a dependency that breaks somewhere for some reason or is there a way to do an if else on the require import, depending on the system/end user platform being used?

thanks :)

Recurring event over daylight savings start/end

I've implemented an example by modifiying example-rrule.js - https://codesandbox.io/s/optimistic-elion-jphq4?file=/src/index.js

In this example, a daily recurring event has been created with the Australia/Sydney TZID just before Daylight Saving Time ends. The next event would appear to be the same time in Sydney but one hour later for UTC. This doesn't appear to be the behaviour.

The issue appears to start at line 81 of the example as the event.rrule seems to be in UTC time without any timezone information (event.rrule.options.tzid is undefined) .

Trying to modify the tzid to provide timezone seems to break things with the following error - Using TZID without Luxon available is unsupported. Returned times are in UTC, not the requested time zone.

A proposed solution to this is reverting to rrule version 2.6.4 (jkbrzt/rrule#427) but 2.6.6 is required by node-ical.

Any thoughts on how to tackle this issue?

no parsing in V0.14.1

I use node-ical via the iobroker adapter, since the update to version 0.14.1 the calender I am using is not being parsed correctly.
Unfortunately I cannot provide any errormessages, but with V 0.13 everything is working fine.
also see iobroker.ical Issue

Repeating Events hard to handle

I wish for a better way to handle repeaing events in the parser like parsing the repeating events as a seperate object and not having only one with the attributes...idk but it's hard to detect when a repeated event takes place. You only have the original wich says yea...i repeat but you have to manage it yourself..

Module not found: Can't resolve 'fs'

Hello Jens,

I'm receiving the above error when try to parse a local .ics file. I understand the fs package has been depreciated - I installed fs-extra instead, which is described as a drop-in replacement, and changed the reference in node_modules/node-ical/node-ical.js to
const fs = require('fs-extra') but I'm still getting the error.

Can anyone help me out, please?
Cheers, Matt

ReDoS in node-ical

Hey Jens,

Recently I found a potential ReDoS vulnerability inside node-ical and provided some proper examples. You can access the vulnerability details at huntr. Please feel free to get in touch if there are any more issues.

Best regards,
Yeting

Exdate showing blank array when EXDATE parameters exist in ical file

Here's the code I'm running.

if (fs.existsSync('data/calendar.ics')) {
                    console.log('file verified, converting...')
                    // use the sync function parseFile() to parse this ics file
                    const events = ical.sync.parseFile('data/calendar.ics');
                    // convert object to JSON string
                    const eventsJSON = JSON.stringify(events);
                    // saves data to file
                    fs.writeFile('data/calendar.json', eventsJSON, (err) => {
                        if (err) {
                            throw err;
                        }
                        console.log("JSON data is saved.");
                    });
                } else {
                    console.log('file could not be verified');
                };

This is a snippet from the ICS file:

BEGIN:VEVENT
DESCRIPTION:\n
RRULE:FREQ=WEEKLY;UNTIL=20210719T210000Z;INTERVAL=1;BYDAY=MO;WKST=SU
EXDATE;TZID=Pacific Standard Time:20210621T140000,20210705T140000
UID:040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000
 010000000384A1B20012EE9428B50E322309A05B1
SUMMARY:(5th FL) Titus and Tate
DTSTART;TZID=Pacific Standard Time:20210517T140000
DTEND;TZID=Pacific Standard Time:20210517T163000
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20211113T223638Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION:
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:1
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
X-MICROSOFT-DISALLOW-COUNTER:FALSE
END:VEVENT

This is the corresponding item in the JSON.

{
  "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1": {
    "type": "VEVENT",
    "params": [],
    "description": "\n",
    "rrule": {
      "_cache": {
        "all": false,
        "before": [],
        "after": [],
        "between": []
      },
      "origOptions": {
        "tzid": "America/Los_Angeles",
        "dtstart": "2021-05-17T21:00:00.000Z",
        "freq": 2,
        "until": "2021-07-19T21:00:00.000Z",
        "interval": 1,
        "byweekday": [
          {
            "weekday": 0
          }
        ],
        "wkst": {
          "weekday": 6
        }
      },
      "options": {
        "freq": 2,
        "dtstart": "2021-05-17T21:00:00.000Z",
        "interval": 1,
        "wkst": 6,
        "count": null,
        "until": "2021-07-19T21:00:00.000Z",
        "tzid": "America/Los_Angeles",
        "bysetpos": null,
        "bymonth": null,
        "bymonthday": [],
        "bynmonthday": [],
        "byyearday": null,
        "byweekno": null,
        "byweekday": [
          0
        ],
        "bynweekday": null,
        "byhour": [
          21
        ],
        "byminute": [
          0
        ],
        "bysecond": [
          0
        ],
        "byeaster": null
      }
    },
    "exdate": [],
    "uid": "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1",
    "summary": "(5th FL) Titus and Tate",
    "start": "2021-05-17T21:00:00.000Z",
    "datetype": "date-time",
    "end": "2021-05-17T23:30:00.000Z",
    "class": "PUBLIC",
    "priority": "5",
    "dtstamp": "2021-11-13T22:49:38.000Z",
    "transparency": "OPAQUE",
    "status": "CONFIRMED",
    "sequence": "0",
    "location": "",
    "MICROSOFT-CDO-APPT-SEQUENCE": "0",
    "MICROSOFT-CDO-BUSYSTATUS": "BUSY",
    "MICROSOFT-CDO-INTENDEDSTATUS": "BUSY",
    "MICROSOFT-CDO-ALLDAYEVENT": "FALSE",
    "MICROSOFT-CDO-IMPORTANCE": "1",
    "MICROSOFT-CDO-INSTTYPE": "1",
    "MICROSOFT-DONOTFORWARDMEETING": "FALSE",
    "MICROSOFT-DISALLOW-COUNTER": "FALSE",
    "method": "PUBLISH",
    "recurrences": {
      "2021-06-14": {
        "type": "VEVENT",
        "params": [],
        "description": "\n",
        "uid": "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1",
        "recurrenceid": "2021-06-14T21:00:00.000Z",
        "summary": "(REMOTE) Titus and Tate ",
        "start": "2021-06-14T21:00:00.000Z",
        "datetype": "date-time",
        "end": "2021-06-14T23:30:00.000Z",
        "class": "PUBLIC",
        "priority": "5",
        "dtstamp": "2021-11-13T22:49:38.000Z",
        "transparency": "OPAQUE",
        "status": "CONFIRMED",
        "sequence": "0",
        "location": "",
        "MICROSOFT-CDO-APPT-SEQUENCE": "0",
        "MICROSOFT-CDO-BUSYSTATUS": "BUSY",
        "MICROSOFT-CDO-INTENDEDSTATUS": "BUSY",
        "MICROSOFT-CDO-ALLDAYEVENT": "FALSE",
        "MICROSOFT-CDO-IMPORTANCE": "1",
        "MICROSOFT-CDO-INSTTYPE": "3",
        "MICROSOFT-DONOTFORWARDMEETING": "FALSE",
        "MICROSOFT-DISALLOW-COUNTER": "FALSE"
      },
      "2021-06-28": {
        "type": "VEVENT",
        "params": [],
        "description": "\n",
        "uid": "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1",
        "recurrenceid": "2021-06-28T21:00:00.000Z",
        "summary": "REMOTE T&T",
        "start": "2021-06-28T21:00:00.000Z",
        "datetype": "date-time",
        "end": "2021-06-28T23:30:00.000Z",
        "class": "PUBLIC",
        "priority": "5",
        "dtstamp": "2021-11-13T22:49:38.000Z",
        "transparency": "OPAQUE",
        "status": "CONFIRMED",
        "sequence": "0",
        "location": "",
        "MICROSOFT-CDO-APPT-SEQUENCE": "0",
        "MICROSOFT-CDO-BUSYSTATUS": "BUSY",
        "MICROSOFT-CDO-INTENDEDSTATUS": "BUSY",
        "MICROSOFT-CDO-ALLDAYEVENT": "FALSE",
        "MICROSOFT-CDO-IMPORTANCE": "1",
        "MICROSOFT-CDO-INSTTYPE": "3",
        "MICROSOFT-DONOTFORWARDMEETING": "FALSE",
        "MICROSOFT-DISALLOW-COUNTER": "FALSE"
      },
      "2021-07-12": {
        "type": "VEVENT",
        "params": [],
        "description": "\n",
        "uid": "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1",
        "recurrenceid": "2021-07-12T21:00:00.000Z",
        "summary": "(Remote) Titus and Tate",
        "start": "2021-07-12T21:00:00.000Z",
        "datetype": "date-time",
        "end": "2021-07-12T23:30:00.000Z",
        "class": "PUBLIC",
        "priority": "5",
        "dtstamp": "2021-11-13T22:49:38.000Z",
        "transparency": "OPAQUE",
        "status": "CONFIRMED",
        "sequence": "0",
        "location": "",
        "MICROSOFT-CDO-APPT-SEQUENCE": "0",
        "MICROSOFT-CDO-BUSYSTATUS": "BUSY",
        "MICROSOFT-CDO-INTENDEDSTATUS": "BUSY",
        "MICROSOFT-CDO-ALLDAYEVENT": "FALSE",
        "MICROSOFT-CDO-IMPORTANCE": "1",
        "MICROSOFT-CDO-INSTTYPE": "3",
        "MICROSOFT-DONOTFORWARDMEETING": "FALSE",
        "MICROSOFT-DISALLOW-COUNTER": "FALSE"
      },
      "2021-07-19": {
        "type": "VEVENT",
        "params": [],
        "description": "\n",
        "uid": "040000008200E00074C5B7101A82E008000000009CF65AD5C445D701000000000000000010000000384A1B20012EE9428B50E322309A05B1",
        "recurrenceid": "2021-07-19T21:00:00.000Z",
        "summary": "(5th FL) Titus and Tate",
        "start": "2021-07-19T21:00:00.000Z",
        "datetype": "date-time",
        "end": "2021-07-19T23:00:00.000Z",
        "class": "PUBLIC",
        "priority": "5",
        "dtstamp": "2021-11-13T22:49:38.000Z",
        "transparency": "OPAQUE",
        "status": "CONFIRMED",
        "sequence": "0",
        "location": "",
        "MICROSOFT-CDO-APPT-SEQUENCE": "0",
        "MICROSOFT-CDO-BUSYSTATUS": "BUSY",
        "MICROSOFT-CDO-INTENDEDSTATUS": "BUSY",
        "MICROSOFT-CDO-ALLDAYEVENT": "FALSE",
        "MICROSOFT-CDO-IMPORTANCE": "1",
        "MICROSOFT-CDO-INSTTYPE": "3",
        "MICROSOFT-DONOTFORWARDMEETING": "FALSE",
        "MICROSOFT-DISALLOW-COUNTER": "FALSE"
      }
    }
  }
}

Australien calendars is one day off

Australian calendars are parsed wrong and ends up with events one day late.

When this calendar is parsed in Google Calendar or Apple calendar in Australia it shows up every other monday at 01:00 AM.
When parsed through node-ical, they show up every other tuesdays at 01:00 AM.

When this calendar is parsed in Google Calendar or Apple calendar in Norway it shows up every other sunday at 17:30.
When parsed through node-ical, they show up every other monday at 17:30.

node-ical parses Australian calendar one day late!

ICS:

BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-TIMEZONE:Australia/Adelaide
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:Australia/Adelaide
X-LIC-LOCATION:Australia/Adelaide
BEGIN:STANDARD
TZOFFSETFROM:+1030
TZOFFSETTO:+0930
TZNAME:ACST
DTSTART:19700405T030000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0930
TZOFFSETTO:+1030
TZNAME:ACDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Australia/Adelaide:20210426T010000
DTEND;TZID=Australia/Adelaide:20210426T010000
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=MO
DTSTAMP:20210507T081822Z
***@***.***
CREATED:20201218T215752Z
DESCRIPTION:
LAST-MODIFIED:20210506T020030Z
LOCATION:
SEQUENCE:4
STATUS:CONFIRMED
SUMMARY:meals
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

Issue on my GitHub

Server timezone enforced on all day events

When an all-day event is created in Google Calendar the start and end times are formatted as follows in the ics file:

DTSTART;VALUE=DATE:20201225 DTEND;VALUE=DATE:20201226

My node server is on UTC time so when this event is parsed the following is returned:

start: "2020-12-25T00:00:00.000Z", ... end: "2020-12-26T00:00:00.000Z",

It is not possible to tell from this if the event is intended to be a timezone agnostic all-day event or a 24-hour event that just happens to start at 00:00 UTC.

I can hardcode an ugly fix in my app for my needs but curious if there is any other option?

Parsing ics-files returns weird values

Hi there,

parsing the following ics-file

BEGIN:VCALENDAR
VERSION:2.0
PROID:jupadin
BEGIN:VEVENT
DTSTART;VALUE=DATE:20200901
DTEND;VALUE=DATE:20200902
RRULE:FREQ=YEARLY;INTERVAL=1
SUMMARY:My Birthday
UID:93239f92-e731-4eff-b31f-df236f4e59f2
END:VEVENT
END:VCALENDAR

returns

{
  "type": "VEVENT",
  "params": [],
  "start": "2020-09-01T00:00:00.000Z",
  "datetype": "date",
  "end": "2020-09-01T22:00:00.000Z",
  "rrule": {
    "_cache": {
      "all": false,
      "before": [],
      "after": [],
      "between": []
    },
    "origOptions": {
      "dtstart": "2020-09-01T00:00:00.000Z",
      "freq": 0,
      "interval": 1
    },
    "options": {
      "freq": 0,
      "dtstart": "2020-09-01T00:00:00.000Z",
      "interval": 1,
      "wkst": 0,
      "count": null,
      "until": null,
      "bysetpos": null,
      "bymonth": [
        9
      ],
      "bymonthday": [
        1
      ],
      "bynmonthday": [],
      "byyearday": null,
      "byweekno": null,
      "byweekday": null,
      "bynweekday": null,
      "byhour": [
        0
      ],
      "byminute": [
        0
      ],
      "bysecond": [
        0
      ],
      "byeaster": null
    }
  },
  "summary": "My Birthday",
  "uid": "93239f92-e731-4eff-b31f-df236f4e59f2"
}

where the start and end field are somehow weird / wrong (?):

"start": "2020-09-01T00:00:00.000Z",

and

"end": "2020-09-01T22:00:00.000Z",

Instead the start value has to be

"start": "2020-08-30T22:00:00.000Z",

since my local time zone is UTC+2 ?

Wrong datetime for recurring events

There seems to be a bug when a recurring event has it's initial start datetime one recurrence before DST timezone change. The one recurrence event before the change will be correct, all next events after seems to be using the first events timezone even though it should have changed, making them all wrong.

Here’s an example of a ics which has this problem

When parsed through node-ical the first event has correct time but the day is wrong (probably because it passes 00:00 AM). All next events should have changed timezone but are still using the same timezone as the initial datetime

ICS
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-TIMEZONE:Australia/Adelaide
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:Australia/Adelaide
X-LIC-LOCATION:Australia/Adelaide
BEGIN:STANDARD
TZOFFSETFROM:+1030
TZOFFSETTO:+0930
TZNAME:ACST
DTSTART:19700405T030000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0930
TZOFFSETTO:+1030
TZNAME:ACDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Australia/Adelaide:20210401T093000
DTEND;TZID=Australia/Adelaide:20210401T120000
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20220506T142959Z;BYDAY=TH
DTSTAMP:20210402T040825Z
UID:[email protected]
CREATED:20210218T020324Z
DESCRIPTION:
LAST-MODIFIED:20210331T222932Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:clear space
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

Here’s an example of a ics which does not have this problem

This ics does not have this problem because the inital start datetime is just passed the timezone change. When parsed through node-ical, all events has correct datetime

ICS
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-TIMEZONE:Australia/Adelaide
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:Australia/Adelaide
X-LIC-LOCATION:Australia/Adelaide
BEGIN:STANDARD
TZOFFSETFROM:+1030
TZOFFSETTO:+0930
TZNAME:ACST
DTSTART:19700405T030000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0930
TZOFFSETTO:+1030
TZNAME:ACDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Australia/Adelaide:20210408T093000
DTEND;TZID=Australia/Adelaide:20210408T120000
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20220506T142959Z;BYDAY=TH
DTSTAMP:20210402T040825Z
UID:[email protected]
CREATED:20210218T020324Z
DESCRIPTION:
LAST-MODIFIED:20210331T222932Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:clear space
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

Enhancement: function to create an "icalobject" with timezone and events

Something like:
ical.sync.parseFile({ filename: 'example-calendar.ics', createObject: true })

or

ical.sync.parseFile({ filename: 'example-calendar.ics', createExtendedJson: true })

If createObject is true:

  • object.events.keyValues holds an array of key value pairs where the value is an event,
  • ?? object.event.keyArray holds the key for the events
  • ?? object.event.valueArray holds an array of events
  • object.timezoneIds is an array of timezone ids
  • object.calendarAttributes returns an object with these properties:
    • name: string
    • description: string
    • defaultTimezoneId: string

If createExtendedJson is true, do the same as createObject but return the json string of the object.

If this makes sense, I can work on it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.