Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix timezone issues when using rrule #329

Merged
merged 10 commits into from
Sep 2, 2024
7 changes: 6 additions & 1 deletion ical.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,12 @@ module.exports = {
// If the original date has a TZID, add it
if (curr.start.tz) {
const tz = getTimeZone(curr.start.tz);
rule += `;DTSTART;TZID=${tz}:${curr.start.toISOString().replace(/[-:]/g, '')}`;
// If a timezone is provided, rrule requires the time to be local
const adjustedTimeString = curr.start
.toLocaleString('sv', {timeZone: tz})
.replace(/ /g, 'T')
.replace(/[-:]/g, '');
rule += `;DTSTART;TZID=${tz}:${adjustedTimeString}`;
} else {
rule += `;DTSTART=${curr.start.toISOString().replace(/[-:]/g, '')}`;
}
Expand Down
88 changes: 86 additions & 2 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const _ = require('underscore');
const moment = require('moment-timezone');
const ical = require('../node-ical.js');

process.on('uncaughtException', error => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should such an exception not better crash the tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh this is some weird issue with vows.js where it doesn't properly catch thrown exceptions and won't tell you what the issue actually is (you get a bunch of "callback not fired" messages instead). I found this snippet online to try to fix it but it doesn't work, and I apparently forgot to take it back out. Here's what it outputs when there's a parsing error in the test:

$ npm run test

> [email protected] test
> xo && vows test/test.js && vows test/test-async.js && printf "\n"

···········································································································✗✗·

✗ Errored » callback not fired 
      in with test22.ics (testing dtstart of rrule with timezones) first event 
      in node-ical 
      in test/test.js✗ Errored » callback not fired 
      in with test22.ics (testing dtstart of rrule with timezones) second event 
      in node-ical 
      in test/test.js[
  '✗ \x1B[31m\x1B[1mErrored\x1B[22m\x1B[39m » \x1B[1m108\x1B[22m honored ∙ \x1B[1m3\x1B[22m errored ∙ \x1B[1m4\x1B[22m dropped\n'
]

On that note, vows seems hopelessly outdated. It still uses util.print, which was EOLed in Node 12.0.0. I had to make some changes in vow itself to get any useful output on errors.

console.log('Caught exception: ' + error.stack);
});

vows
.describe('node-ical')
.addBatch({
Expand Down Expand Up @@ -150,8 +154,10 @@ vows
});
},
'tzid offset correctly applied'(event) {
const start = new Date('2002-10-28T22:00:00.000Z');
assert.equal(event.start.valueOf(), start.valueOf());
assert.ok(moment.tz.zone(event.start.tz), 'zone does not exist');
const ref = '2002-10-28T22:00:00Z';
const start = moment(event.start).tz(event.start.tz);
assert.equal(start.utc().format(), ref);
}
}
},
Expand Down Expand Up @@ -1051,6 +1057,84 @@ vows
assert.equal(task.summary, 'test export import');
}
}
},
'with test22.ics (testing dtstart of rrule with timezones)': {
topic() {
return ical.parseFile('./test/test22.ics');
},
'first event': {
topic(events) {
return _.select(_.values(events), x => {
return x.uid === '000021a';
})[0];
},
'datetype is date-time'(topic) {
assert.equal(topic.datetype, 'date-time');
},
'has GMT+1 timezone'(topic) {
assert.equal(topic.start.tz, 'Europe/Berlin');
},
'starts 14 Jul 2022 @ 12:00:00 (UTC)'(topic) {
assert.equal(topic.start.toISOString(), '2022-07-14T12:00:00.000Z');
}
},
'recurring yearly frist event (14 july)': {
topic(events) {
const ev = _.select(_.values(events), x => {
return x.uid === '000021a';
})[0];
return ev.rrule.between(new Date(2023, 0, 1), new Date(2024, 0, 1))[0];
},
'dt start well set'(topic) {
assert.equal(topic.toDateString(), new Date(2023, 6, 14).toDateString());
},
'starts 14 Jul 2023 @ 12:00:00 (UTC)'(topic) {
assert.equal(topic.toISOString(), '2023-07-14T12:00:00.000Z');
}
},
'second event': {
topic(events) {
return _.select(_.values(events), x => {
return x.uid === '000021b';
})[0];
},
'datetype is date-time'(event) {
assert.equal(event.datetype, 'date-time');
},
'start date': {
topic(event) {
return event.start;
},
'has correct timezone'(start) {
assert.equal(start.tz, 'Etc/GMT-2');
},
'starts 15 Jul 2022 @ 12:00:00 (UTC)'(start) {
assert.equal(start.toISOString(), '2022-07-15T12:00:00.000Z');
}
},
'has recurrences': {
topic(event) {
return event.rrule;
},
'that are defined'(rrule) {
assert.ok(rrule, 'no rrule defined');
},
'that have timezone info'(rrule) {
assert.ok(rrule.options.tzid, 'no tzid property on rrule');
},
'that keep correct timezone info in recurrences'(rrule) {
assert.equal(rrule.options.tzid, 'Etc/GMT-2');
}
},
'has a first recurrence': {
topic(event) {
return event.rrule.between(new Date(2023, 0, 1), new Date(2024, 0, 1))[0];
},
'that starts 15 Jul 2023 @ 12:00:00 (UTC)'(rc) {
assert.equal(rc.toISOString(), '2023-07-15T12:00:00.000Z');
}
}
}
}
})
.export(module);
21 changes: 21 additions & 0 deletions test/test21-mod.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
BEGIN:VCALENDAR
BEGIN:VEVENT
TRANSP:OPAQUE
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
CREATED:20221004T073016Z
LAST-MODIFIED:20221011T063437Z
DTSTAMP:20221011T063437Z
DTSTART;TZID="(GMT +01:00)":20221004T140000
DTEND;TZID="(GMT +01:00)":20221004T150000
SUMMARY:Music School
CLASS:PUBLIC
UID:0000021
X-MOZ-SNOOZE-TIME:20221004T113000Z
X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:20221014T203413Z
X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:20221014T203413Z
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-OWNERAPPTID:-1
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
RRULE:FREQ=WEEKLY;UNTIL=20221201T020000;BYDAY=TU
END:VEVENT
END:VCALENDAR
26 changes: 26 additions & 0 deletions test/test22.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:Fictitious Recurrence Test Calendar
BEGIN:VEVENT
CREATED:20221018T221500Z
DTSTAMP:20221019T171200Z
UID:000021a
SUMMARY:Party
RRULE:FREQ=YEARLY
DTSTART;TZID=Europe/Berlin:20220714T140000
DTEND;TZID=Europe/Berlin:20220714T210000
TRANSP:OPAQUE
SEQUENCE:5
END:VEVENT
BEGIN:VEVENT
CREATED:20221019T181700Z
DTSTAMP:20221019T191200Z
UID:000021b
SUMMARY:Party next day
RRULE:FREQ=YEARLY
DTSTART;TZID=Etc/GMT-2:20220715T140000
DTEND;TZID=Etc/GMT-2:20220715T210000
TRANSP:OPAQUE
SEQUENCE:5
END:VEVENT
END:VCALENDAR
Loading