From 1828b7a6d708cecaa73f63b515fc9f909d641e7e Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 25 Jul 2023 15:42:11 +0200 Subject: [PATCH 1/3] Fix: Also convert dates for comparisons --- src/gmp/models/event.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gmp/models/event.js b/src/gmp/models/event.js index b2e33c07a0..9754956d3c 100644 --- a/src/gmp/models/event.js +++ b/src/gmp/models/event.js @@ -356,7 +356,7 @@ class Event { while (true && retries <= 5) { try { const next = it.next(); - if (next.toUnixTime() >= now.unix()) { + if (convertIcalDate(next, this.timezone).unix() >= now.unix()) { return convertIcalDate(next, this.timezone); } retries = 0; @@ -380,8 +380,9 @@ class Event { // the event is not recurring // it should only occur once on its start date const now = date(); + const start = convertIcalDate(this.event.startDate, this.timezone); - if (this.event.startDate.toUnixTime() >= now.unix()) { + if (start.unix() >= now.unix()) { return convertIcalDate(this.event.startDate, this.timezone); } } From 7e8a12d69dc58bc535618c1eae7b875d9e9e1334 Mon Sep 17 00:00:00 2001 From: Matt Mundell <32057441+mattmundell@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:48:24 +0200 Subject: [PATCH 2/3] Use start variable for return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Ricks --- src/gmp/models/event.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmp/models/event.js b/src/gmp/models/event.js index 9754956d3c..e92c4ca682 100644 --- a/src/gmp/models/event.js +++ b/src/gmp/models/event.js @@ -383,7 +383,7 @@ class Event { const start = convertIcalDate(this.event.startDate, this.timezone); if (start.unix() >= now.unix()) { - return convertIcalDate(this.event.startDate, this.timezone); + return start; } } return undefined; From 811c344b4baaeb578918ac084b8b632fa9d0c89c Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 1 Aug 2023 16:45:25 +0200 Subject: [PATCH 3/3] Add: Event test with recurrence and timezone --- src/gmp/models/__tests__/event.js | 59 ++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/gmp/models/__tests__/event.js b/src/gmp/models/__tests__/event.js index bc50989418..4762b70e86 100644 --- a/src/gmp/models/__tests__/event.js +++ b/src/gmp/models/__tests__/event.js @@ -20,6 +20,7 @@ import date from '../date'; import Event from '../event'; const ICAL_FORMAT = 'YYYYMMDD[T]HHmmss[Z]'; +const ICAL_FORMAT_TZ = 'YYYYMMDD[T]HHmmss'; describe('Event model tests', () => { test('should parse event start from icalendar without timzeone', () => { @@ -76,11 +77,7 @@ END:VCALENDAR }); test('should calculate start date as next date for daily recurrence', () => { - const now = date - .tz('utc') - .minutes(0) - .seconds(0) - .milliseconds(0); + const now = date.tz('utc').minutes(0).seconds(0).milliseconds(0); const startDate = now.clone().add(1, 'hour'); const icalendar = `BEGIN:VCALENDAR VERSION:2.0 @@ -105,11 +102,7 @@ END:VCALENDAR }); test('should calculate next day as next day for daily recurrence', () => { - const now = date - .tz('utc') - .minutes(0) - .seconds(0) - .milliseconds(0); + const now = date.tz('utc').minutes(0).seconds(0).milliseconds(0); const startDate = now.clone().subtract(1, 'hour'); const icalendar = `BEGIN:VCALENDAR VERSION:2.0 @@ -138,11 +131,7 @@ END:VCALENDAR }); test('should calculate start date as next date for no recurrence', () => { - const now = date - .tz('utc') - .minutes(0) - .seconds(0) - .milliseconds(0); + const now = date.tz('utc').minutes(0).seconds(0).milliseconds(0); const startDate = now.clone().add(1, 'hour'); const icalendar = `BEGIN:VCALENDAR VERSION:2.0 @@ -166,11 +155,7 @@ END:VCALENDAR }); test('should calculate no next date for no recurrence if start date is already over', () => { - const startDate = date - .tz('utc') - .minutes(0) - .seconds(0) - .milliseconds(0); + const startDate = date.tz('utc').minutes(0).seconds(0).milliseconds(0); const now = startDate.clone().add(1, 'hour'); const icalendar = `BEGIN:VCALENDAR VERSION:2.0 @@ -192,4 +177,38 @@ END:VCALENDAR // there should be no next event expect(nextDate).toBeUndefined(); }); + + test('should calculate next date for daily recurrence when a timezone is used', () => { + const tz = process.env.TZ; + process.env.TZ = 'America/New_York'; // UTC-4 or UTC-5 + + const now = date + .tz('America/New_York') + .minutes(0) + .seconds(0) + .milliseconds(0); + // The target date is in 2 hours. If the recurrence calculation converts the current NY + // time directly to UTC (for example 16h00 NY becomes 16h00 UTC) then the test will fail + // because UTC is more than 2 hours ahead of NY. + const expected = now.clone().add(2, 'hour'); + const start = expected.clone().subtract(24, 'hour'); + const icalendar = `BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Greenbone.net//NONSGML Greenbone Security Manager 8.0.0//EN +BEGIN:VEVENT +UID:c35f82f1-7798-4b84-b2c4-761a33068956 +DTSTART;TZID=/America/New_York:${start.format(ICAL_FORMAT_TZ)} +RRULE:FREQ=DAILY +END:VEVENT +END:VCALENDAR +`; + + const event = Event.fromIcal(icalendar, 'America/New_York'); + + const next = event.nextDate; + + expect(next.isSame(expected)).toEqual(true); + + process.env.TZ = tz; + }); });