Skip to content

Commit

Permalink
Fix #1211: In Caldav integration, events should support timezone (#1231)
Browse files Browse the repository at this point in the history
Co-authored-by: Pierre-Gilles Leymarie <pierregilles.leymarie@gmail.com>
  • Loading branch information
bertrandda and Pierre-Gilles committed Sep 10, 2021
1 parent 64d1bb5 commit f059a8a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 35 deletions.
4 changes: 4 additions & 0 deletions server/services/caldav/index.js
Expand Up @@ -11,12 +11,16 @@ module.exports = function CalDAVService(gladys, serviceId) {
const duration = require('dayjs/plugin/duration');
const advancedFormat = require('dayjs/plugin/advancedFormat');
const isBetween = require('dayjs/plugin/isBetween');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
const xmlDom = require('xmldom');

dayjs.extend(objectSupport);
dayjs.extend(duration);
dayjs.extend(advancedFormat);
dayjs.extend(isBetween);
dayjs.extend(utc);
dayjs.extend(timezone);

const calDavHandler = new CalDAVHandler(gladys, serviceId, ical, dav, dayjs, xmlDom);

Expand Down
32 changes: 21 additions & 11 deletions server/services/caldav/lib/calendar/calendar.formaters.js
Expand Up @@ -8,15 +8,18 @@
* formatRecurringEvents(event, gladysCalendar)
*/
function formatRecurringEvents(event, gladysCalendar) {
let startDate = this.dayjs(event.start);
const { tz } = event.start;
let startDate = this.dayjs.tz(this.dayjs(event.start).format('YYYY-MM-DDTHH:mm:ss'), tz);
let endDate;

if (event.end) {
endDate = this.dayjs(event.end);
endDate = this.dayjs.tz(this.dayjs(event.end).format('YYYY-MM-DDTHH:mm:ss'), tz);
} else if (event.duration) {
endDate = this.dayjs(event.start).add(this.dayjs.duration(event.duration));
endDate = this.dayjs
.tz(this.dayjs(event.start).format('YYYY-MM-DDTHH:mm:ss'), tz)
.add(this.dayjs.duration(event.duration));
} else {
endDate = this.dayjs(event.start).add(1, 'days');
endDate = this.dayjs.tz(this.dayjs(event.start).format('YYYY-MM-DDTHH:mm:ss'), tz).add(1, 'days');
}

// Calculate the duration of the event for use with recurring events.
Expand Down Expand Up @@ -49,8 +52,7 @@ function formatRecurringEvents(event, gladysCalendar) {
let curEvent = event;
let showRecurrence = true;
let curDuration = duration;

startDate = this.dayjs(date);
startDate = this.dayjs.tz(this.dayjs(date).format('YYYY-MM-DDTHH:mm:ss'), tz);

// Use just the date of the recurrence to look up overrides and exceptions (i.e. chop off time information)
const dateLookupKey = date.toISOString().substring(0, 10);
Expand All @@ -60,7 +62,7 @@ function formatRecurringEvents(event, gladysCalendar) {
// We found an override, so for this recurrence, use a potentially different title,
// start date, and duration.
curEvent = curEvent.recurrences[dateLookupKey];
startDate = this.dayjs(curEvent.start);
startDate = this.dayjs.tz(this.dayjs(curEvent.start).format('YYYY-MM-DDTHH:mm:ss'), tz);
curDuration = parseInt(this.dayjs(curEvent.end).format('x'), 10) - parseInt(startDate.format('x'), 10);
if (curEvent.status === 'CANCELLED') {
showRecurrence = false;
Expand All @@ -75,6 +77,10 @@ function formatRecurringEvents(event, gladysCalendar) {
// Set the the title and the end date from either the regular event or the recurrence override.
const recurrenceTitle = curEvent.summary;
endDate = this.dayjs(parseInt(startDate.format('x'), 10) + curDuration, 'x');
endDate = this.dayjs.tz(
this.dayjs(parseInt(startDate.format('x'), 10) + curDuration, 'x').format('YYYY-MM-DDTHH:mm:ss'),
tz,
);

// If this recurrence ends before the start of the date range, or starts after the end of the date range,
// don't process it.
Expand All @@ -101,8 +107,8 @@ function formatRecurringEvents(event, gladysCalendar) {
endDate = endDate.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
}

newEvent.start = startDate.toISOString();
newEvent.end = endDate.toISOString();
newEvent.start = startDate.format();
newEvent.end = endDate.format();

return newEvent;
}
Expand Down Expand Up @@ -138,11 +144,15 @@ function formatEvents(caldavEvents, gladysCalendar) {
};

if (caldavEvent.start) {
newEvent.start = caldavEvent.start.toISOString();
newEvent.start = this.dayjs
.tz(this.dayjs(caldavEvent.start).format('YYYY-MM-DDTHH:mm:ss'), caldavEvent.start.tz)
.format();
}

if (caldavEvent.end) {
newEvent.end = caldavEvent.end.toISOString();
newEvent.end = this.dayjs
.tz(this.dayjs(caldavEvent.end).format('YYYY-MM-DDTHH:mm:ss'), caldavEvent.end.tz)
.format();
}

if (
Expand Down
52 changes: 28 additions & 24 deletions server/test/services/caldav/lib/calendar/formaters.test.js
Expand Up @@ -29,6 +29,10 @@ dayjsUTCOverride.duration = function duration(number) {
return dayjs.duration(number);
};

dayjsUTCOverride.tz = function tz(date, timezoneValue) {
return dayjs.utc(date);
};

describe('CalDAV formaters', () => {
let caldavCalendars;
let expectedCalendars;
Expand Down Expand Up @@ -129,8 +133,8 @@ describe('CalDAV formaters', () => {
selector: 'e52c11e3-af8a-48c7-9f54-de7aba373c46',
name: 'Event 1',
location: 'Paris',
start: '2019-02-25T10:00:00.000Z',
end: '2019-02-25T12:00:00.000Z',
start: '2019-02-25T10:00:00+00:00',
end: '2019-02-25T12:00:00+00:00',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
url: 'https://caldav.host/home/event1.ics',
},
Expand All @@ -139,8 +143,8 @@ describe('CalDAV formaters', () => {
selector: '71c01038-2231-4dee-a230-6820fdb1136e',
name: 'Event 2',
location: 'Toulouse',
start: '2019-04-01T00:00:00.000Z',
end: '2019-04-02T00:00:00.000Z',
start: '2019-04-01T00:00:00+00:00',
end: '2019-04-02T00:00:00+00:00',
full_day: true,
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
url: 'https://caldav.host/home/event2.ics',
Expand All @@ -152,8 +156,8 @@ describe('CalDAV formaters', () => {
location: 'Paris',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
full_day: true,
start: '2019-09-27T00:00:00.000Z',
end: '2019-09-28T00:00:00.000Z',
start: '2019-09-27T00:00:00+00:00',
end: '2019-09-28T00:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event2',
},
{
Expand All @@ -163,19 +167,19 @@ describe('CalDAV formaters', () => {
location: 'Paris',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
full_day: true,
start: '2020-09-27T00:00:00.000Z',
end: '2020-09-28T00:00:00.000Z',
start: '2020-09-27T00:00:00+00:00',
end: '2020-09-28T00:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event2',
},
{
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
end: '2021-09-28T00:00:00.000Z',
external_id: '29f76a08-5439-4e04-bc1f-a67c32b47c802021-09-27-00-00',
full_day: true,
location: 'Paris',
name: 'Anniversaire Pepper',
selector: '29f76a08-5439-4e04-bc1f-a67c32b47c802021-09-27-00-00',
start: '2021-09-27T00:00:00.000Z',
start: '2021-09-27T00:00:00+00:00',
end: '2021-09-28T00:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event2',
},
];
Expand Down Expand Up @@ -280,8 +284,8 @@ describe('CalDAV formaters', () => {
name: 'Cours de tennis',
location: 'Stade Roland-Garros',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
start: '2019-06-01T09:00:00.000Z',
end: '2019-06-01T12:00:00.000Z',
start: '2019-06-01T09:00:00+00:00',
end: '2019-06-01T12:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event1',
},
null,
Expand All @@ -295,8 +299,8 @@ describe('CalDAV formaters', () => {
location: 'Paris',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
full_day: true,
start: '2019-09-27T00:00:00.000Z',
end: '2019-09-28T00:00:00.000Z',
start: '2019-09-27T00:00:00+00:00',
end: '2019-09-28T00:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event2',
},
{
Expand All @@ -306,8 +310,8 @@ describe('CalDAV formaters', () => {
location: 'Paris',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
full_day: true,
start: '2020-09-27T00:00:00.000Z',
end: '2020-09-28T00:00:00.000Z',
start: '2020-09-27T00:00:00+00:00',
end: '2020-09-28T00:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event2',
},
null,
Expand All @@ -319,8 +323,8 @@ describe('CalDAV formaters', () => {
name: 'Réunion Avengers',
location: 'Tour Stark',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
start: '2019-06-10T09:00:00.000Z',
end: '2019-06-10T10:00:00.000Z',
start: '2019-06-10T09:00:00+00:00',
end: '2019-06-10T10:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event3',
},
null,
Expand All @@ -330,8 +334,8 @@ describe('CalDAV formaters', () => {
name: 'Réunion Avengers, nouvel arrivant',
location: 'Tour Stark',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
start: '2019-06-24T09:00:00.000Z',
end: '2019-06-24T10:00:00.000Z',
start: '2019-06-24T09:00:00+00:00',
end: '2019-06-24T10:00:00+00:00',
url: 'https://caldav.host.com/home/recur-event3',
},
],
Expand All @@ -342,8 +346,8 @@ describe('CalDAV formaters', () => {
name: 'Gouter',
location: 'Jardin',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
start: '2019-06-20T16:00:00.000Z',
end: '2019-06-20T17:30:00.000Z',
start: '2019-06-20T16:00:00+00:00',
end: '2019-06-20T17:30:00+00:00',
url: 'https://caldav.host.com/home/recur-event4',
},
{
Expand All @@ -352,8 +356,8 @@ describe('CalDAV formaters', () => {
name: 'Gouter',
location: 'Jardin',
calendar_id: '1fe8f557-2685-4b6b-8f05-238184f6b701',
start: '2019-06-21T16:00:00.000Z',
end: '2019-06-21T17:30:00.000Z',
start: '2019-06-21T16:00:00+00:00',
end: '2019-06-21T17:30:00+00:00',
url: 'https://caldav.host.com/home/recur-event4',
},
],
Expand Down

0 comments on commit f059a8a

Please sign in to comment.