diff --git a/ical.js b/ical.js index 09308ea..afe58ec 100755 --- a/ical.js +++ b/ical.js @@ -17,6 +17,8 @@ }('ical', function(){ + var momentTz = require('moment-timezone'); + // Unescape Text re RFC 4.3.11 var text = function(t){ t = t || ""; @@ -141,16 +143,23 @@ parseInt(comps[5], 10), parseInt(comps[6], 10 ) )); - // TODO add tz } else { - newDate = new Date( - parseInt(comps[1], 10), - parseInt(comps[2], 10)-1, - parseInt(comps[3], 10), - parseInt(comps[4], 10), - parseInt(comps[5], 10), - parseInt(comps[6], 10) - ); + var p = parseParams(params); + if (p && p.TZID) { + // If timezone is specified, the value corresponds to time local to the timezone. + // So, timezone needs to be factored in before converting to UTC + // Ref: #3 option in https://www.kanzaki.com/docs/ical/dateTime.html + newDate = momentTz.tz(val, p.TZID).utc().toDate(); + } else { + newDate = new Date( + parseInt(comps[1], 10), + parseInt(comps[2], 10)-1, + parseInt(comps[3], 10), + parseInt(comps[4], 10), + parseInt(comps[5], 10), + parseInt(comps[6], 10) + ); + } } newDate = addTZ(newDate, params); diff --git a/package-lock.json b/package-lock.json index 0a9e14d..a0169b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,6 +89,14 @@ "brace-expansion": "^1.1.7" } }, + "moment-timezone": { + "version": "0.5.31", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", + "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", + "requires": { + "moment": ">= 2.9.0" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index 9ccf4bc..070a5bc 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "url": "git://github.com/peterbraden/ical.js.git" }, "dependencies": { + "moment-timezone": "^0.5.31", "rrule": "2.4.1" }, "devDependencies": { diff --git a/test/test.js b/test/test.js index c675ae8..2e04671 100755 --- a/test/test.js +++ b/test/test.js @@ -198,7 +198,9 @@ vows.describe('node-ical').addBatch({ } , 'has a start' : function(topic){ assert.equal(topic.start.tz, 'America/Phoenix') - assert.equal(topic.start.toISOString(), new Date(2011, 10, 09, 19, 0,0).toISOString()) + // 20111109T190000 in America/Phoenix maps to 20111010T020000 in UTC + // https://www.timeanddate.com/worldclock/converter.html?iso=20111010T020000&p1=197&p2=1440 + assert.equal(topic.start.toISOString(), new Date(2011, 10, 10, 02, 0,0).toISOString()) } } } @@ -255,7 +257,9 @@ vows.describe('node-ical').addBatch({ }, 'task completed': function(task){ assert.equal(task.completion, 100); - assert.equal(task.completed.toISOString(), new Date(2013, 06, 16, 10, 57, 45).toISOString()); + // 20130716T105745 in Europe/Monaco maps to 20130716T085745 in UTC + // https://www.timeanddate.com/worldclock/converter.html?iso=20130716T085700&p1=674&p2=1440 + assert.equal(task.completed.toISOString(), new Date(2013, 06, 16, 08, 57, 45).toISOString()); } } } @@ -278,7 +282,7 @@ vows.describe('node-ical').addBatch({ }, 'grabbing custom properties': { topic: function(topic) { - + } } }, @@ -491,10 +495,14 @@ vows.describe('node-ical').addBatch({ })[0]; } , 'has a start' : function(topic){ - assert.equal(topic.start.tz, "(UTC+07:00) Bangkok, Hanoi, Jakarta") - assert.equal(topic.start.toISOString(), new Date(2019, 3, 30, 9, 0, 0).toISOString()) - assert.equal(topic.end.tz, "(UTC+07:00) Bangkok, Hanoi, Jakarta") - assert.equal(topic.end.toISOString(), new Date(2019, 3, 30, 12, 0, 0).toISOString()) + assert.equal(topic.start.tz, "Asia/Jakarta") + // 20190430T090000 in Asia/Jakarta maps to 20190430T020000 in UTC + // https://www.timeanddate.com/worldclock/converter.html?iso=20190430T020000&p1=1440&p2=108 + assert.equal(topic.start.toISOString(), new Date(2019, 3, 30, 2, 0, 0).toISOString()) + assert.equal(topic.end.tz, "Asia/Jakarta") + // 20190430T120000 in Asia/Jakarta maps to 20190430T050000 in UTC + // https://www.timeanddate.com/worldclock/converter.html?iso=20190430T050000&p1=1440&p2=108 + assert.equal(topic.end.toISOString(), new Date(2019, 3, 30, 5, 0, 0).toISOString()) } } } diff --git a/test/test15.ics b/test/test15.ics index b416999..4009d7d 100644 --- a/test/test15.ics +++ b/test/test15.ics @@ -4,7 +4,7 @@ PRODID:Microsoft Exchange Server 2010 VERSION:2.0 X-WR-CALNAME:Calendar name BEGIN:VTIMEZONE -TZID:(UTC+07:00) Bangkok\, Hanoi\, Jakarta +TZID:Asia/Jakarta BEGIN:STANDARD DTSTART:16010101T000000 TZOFFSETFROM:+0700 @@ -21,8 +21,8 @@ DESCRIPTION:\n UID:040000008200E00074C5B7101A82E00800000000C9AB6E5A6AFED401000000000000000 010000000C55132227F0F0948A7D58F6190A3AEF9 SUMMARY:Test event -DTSTART;TZID="(UTC+07:00) Bangkok, Hanoi, Jakarta":20190430T090000 -DTEND;TZID="(UTC+07:00) Bangkok, Hanoi, Jakarta":20190430T120000 +DTSTART;TZID=Asia/Jakarta:20190430T090000 +DTEND;TZID=Asia/Jakarta:20190430T120000 CLASS:PUBLIC PRIORITY:5 DTSTAMP:20190507T034351Z @@ -38,4 +38,4 @@ X-MICROSOFT-CDO-IMPORTANCE:1 X-MICROSOFT-CDO-INSTTYPE:0 X-MICROSOFT-DISALLOW-COUNTER:FALSE END:VEVENT -END:VCALENDAR \ No newline at end of file +END:VCALENDAR diff --git a/test/test9.ics b/test/test9.ics index 76a1d7b..c08af3f 100644 --- a/test/test9.ics +++ b/test/test9.ics @@ -4,8 +4,8 @@ UID:eb9e1bd2-ceba-499f-be77-f02773954c72 SUMMARY:Event with an alarm DESCRIPTION:This is an event with an alarm. ORGANIZER="mailto:stomlinson@mozilla.com" -DTSTART;TZID="America/Los_Angeles":20130418T110000 -DTEND;TZID="America/Los_Angeles":20130418T120000 +DTSTART;TZID=America/Los_Angeles:20130418T110000 +DTEND;TZID=America/Los_Angeles:20130418T120000 STATUS:CONFIRMED CLASS:PUBLIC TRANSP:OPAQUE