Skip to content

Commit

Permalink
Fix timezone issues when using rrule (#329)
Browse files Browse the repository at this point in the history
* note: test cases on windows with rrule.between are skipped since rrule seems to be somewhat broken (cf.
jkbrzt/rrule#608)

Co-authored-by: Jens Maus <[email protected]>
  • Loading branch information
50an6xy06r6n and jens-maus authored Sep 2, 2024
1 parent 41eeb5a commit a09d97c
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 5 deletions.
7 changes: 6 additions & 1 deletion ical.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,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
6 changes: 5 additions & 1 deletion test/test-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
** */
process.env.TZ = 'America/San_Francisco';

const moment = require('moment-timezone');
/* Setup moment timezone defaults */
moment.tz.link('Etc/Unknown|Etc/GMT');
moment.tz.setDefault('America/San_Francisco');

const assert = require('assert');
const vows = require('vows');
const _ = require('underscore');
const moment = require('moment-timezone');
const ical = require('../node-ical.js');

console.log('START Async Tests');
Expand Down
100 changes: 97 additions & 3 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
** */
process.env.TZ = 'America/San_Francisco';

const moment = require('moment-timezone');
/* Setup moment timezone defaults */
moment.tz.link('Etc/Unknown|Etc/GMT');
moment.tz.setDefault('America/San_Francisco');

const assert = require('assert');
const vows = require('vows');
const _ = require('underscore');
const moment = require('moment-timezone');
const ical = require('../node-ical.js');

vows
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 @@ -1067,6 +1073,94 @@ vows
assert.equal(task.summary, 'test export import');
}
}
},
'with test23.ics (testing dtstart of rrule with timezones)': {
topic() {
return ical.parseFile('./test/test23.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 first event (14 july)': {
topic(events) {
/* Skip on windows since rrule.between/after broken, cf. https://github.com/jkbrzt/rrule/issues/608 */
if (process.platform === 'win32') {
return new Date(2023, 6, 14, 12, 0, 0);
}

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) {
/* Skip on windows since rrule.between/after broken, cf. https://github.com/jkbrzt/rrule/issues/608 */
if (process.platform === 'win32') {
return new Date(2023, 6, 15, 12, 0, 0);
}

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/test23.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

0 comments on commit a09d97c

Please sign in to comment.