From 77a3e8048763681c84f6363a427ce61e02e75bfd Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 10:21:26 -0500 Subject: [PATCH 01/34] fix dates with node-ical@0.19.0 --- modules/default/calendar/calendarfetcher.js | 2 +- .../default/calendar/calendarfetcherutils.js | 216 +++++++++++++++--- package.json | 2 +- .../3_move_first_allday_repeating_event.js | 36 +++ .../calendar/chicago_late_in_timezone.js | 41 ++++ .../germany_at_end_of_day_repeating.js | 31 +++ tests/electron/modules/calendar_spec.js | 36 +++ .../3_move_first_allday_repeating_event.ics | 35 +++ tests/mocks/chicago_late_in_timezone.ics | 15 ++ .../mocks/germany_at_end_of_day_repeating.ics | 15 ++ 10 files changed, 400 insertions(+), 29 deletions(-) create mode 100644 tests/configs/modules/calendar/3_move_first_allday_repeating_event.js create mode 100644 tests/configs/modules/calendar/chicago_late_in_timezone.js create mode 100644 tests/configs/modules/calendar/germany_at_end_of_day_repeating.js create mode 100644 tests/mocks/3_move_first_allday_repeating_event.ics create mode 100644 tests/mocks/chicago_late_in_timezone.ics create mode 100644 tests/mocks/germany_at_end_of_day_repeating.ics diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 824c7c5b4f..82ef79c1cb 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -56,7 +56,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn try { data = ical.parseICS(responseData); - Log.debug(`parsed data=${JSON.stringify(data)}`); + Log.debug(`parsed data=${JSON.stringify(data,null,2)}`); events = CalendarFetcherUtils.filterEvents(data, { excludedEvents, includePastEvents, diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index eec3540f29..b0990be55e 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -160,7 +160,7 @@ const CalendarFetcherUtils = { } if (event.type === "VEVENT") { - Log.debug(`Event:\n${JSON.stringify(event)}`); + Log.debug(`Event:\n${JSON.stringify(event,null,2)}`); let startMoment = eventDate(event, "start"); let endMoment; @@ -246,6 +246,8 @@ const CalendarFetcherUtils = { const location = event.location || false; const geo = event.geo || false; const description = event.description || false; + let d1; + let d2; if (event.rrule && typeof event.rrule !== "undefined" && !isFacebookBirthday) { const rule = event.rrule; @@ -283,20 +285,69 @@ const CalendarFetcherUtils = { } futureLocal = futureMoment.toDate(); // future } - Log.debug(`Search for recurring events between: ${pastLocal} and ${futureLocal}`); - const hasByWeekdayRule = rule.options.byweekday !== undefined && rule.options.byweekday !== null; const oneDayInMs = 24 * 60 * 60 * 1000; + d1=new Date(new Date(pastLocal.valueOf() - oneDayInMs).getTime()) + d2=new Date(new Date(futureLocal.valueOf() + oneDayInMs).getTime()) + Log.debug(`Search for recurring events between: ${d1} and ${d2}`); + + //rule.origOptions.dtstart=rule.options.dtstart= + event.start=rule.options.dtstart// new Date(new Date(event.start.valueOf()).getTime()) + + Log.debug("fix rrule start=",rule.options.dtstart) + Log.debug("event before rrule.between=",event) + // if there are excluded dates, their date is incorrect and possibly key as well. + if(event.exdate != undefined){ + Object.keys(event.exdate).forEach(dateKey=>{ + // get the date + let exdate=event.exdate[dateKey] + exdate=new Date(new Date(exdate.valueOf()-(60*60*1000)).getTime()) + Log.debug("new exDate item=",exdate," with old key=", dateKey) + let newkey=exdate.toISOString().slice(0,10) + if(newkey!=dateKey){ + Log.debug("new exDate item=",exdate," key="+newkey) + event.exdate[newkey]=exdate + //delete event.exdate[dateKey] + } + }) + } + const hasByWeekdayRule = rule.options.byweekday !== undefined && rule.options.byweekday !== null; + Log.debug(`RRule: ${rule.toString()}`); rule.options.tzid = null; // RRule gets *very* confused with timezones - let dates = rule.between(new Date(pastLocal.valueOf() - oneDayInMs), new Date(futureLocal.valueOf() + oneDayInMs), true, () => { return true; }); - Log.debug(`Title: ${event.summary}, with dates: ${JSON.stringify(dates)}`); + + let dates = rule.between(d1,d2, true, () => { return true; }); + + Log.debug(`Title: ${event.summary}, with dates: \n\n${JSON.stringify(dates)}\n`); dates = dates.filter((d) => { if (JSON.stringify(d) === "null") return false; else return true; }); + if(true){ + + let datesLocal=[] + let offset = d1.getTimezoneOffset() // this will be local timezone.. oops.. need relative to ics timezone + Log.debug("offset =",offset) + dates.forEach(d =>{ + let dtext=d.toISOString().slice(0,-5) + Log.debug(" date text form without tz=",dtext) + let dLocal= new Date(d.valueOf()+(offset*60000)) + let offset2=dLocal.getTimezoneOffset() + Log.debug("date after offset applied=", dLocal) + if(offset!=offset2){ + // woops, dst/std switch + let delta = offset-offset2 + Log.debug("offset delta=", delta) + dLocal= new Date(d.valueOf()+((offset-delta)*60000)) + Log.debug("corrected normalized date=",dLocal) + } else + Log.debug(" neutralized date=",dLocal); + datesLocal.push(dLocal) + }) + dates=datesLocal + } // RRule can generate dates with an incorrect recurrence date. Process the array here and apply date correction. - if (hasByWeekdayRule) { + if (false && hasByWeekdayRule) { Log.debug("Rule has byweekday, checking for correction"); dates.forEach((date, index, arr) => { // NOTE: getTimezoneOffset() is negative of the expected value. For America/Los_Angeles under DST (GMT-7), @@ -322,13 +373,13 @@ const CalendarFetcherUtils = { // The dates array from rrule can be confused by DST. If the event was created during DST and we // are querying a date range during non-DST, rrule can have the incorrect time for the date range. // Reprocess the array here computing and applying the time offset. - dates.forEach((date, index, arr) => { + /*dates.forEach((date, index, arr) => { let adjustHours = CalendarFetcherUtils.calculateTimezoneAdjustment(event, date); if (adjustHours !== 0) { Log.debug(`Applying timezone adjustment hours=${adjustHours} to ${date}`); arr[index] = new Date(date.valueOf() + (adjustHours * 60 * 60 * 1000)); } - }); + });*/ // The "dates" array contains the set of dates within our desired date range range that are valid // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that @@ -351,15 +402,17 @@ const CalendarFetcherUtils = { // Lastly, sometimes rrule doesn't include the event.start even if it is in the requested range. Ensure // inclusion here. Unfortunately dates.includes() doesn't find it so we have to do forEach(). - { + /*{ let found = false; - dates.forEach((d) => { if (d.valueOf() === event.start.valueOf()) found = true; }); + dates.forEach((d) => { Log.debug(" d=",d, "start=", event.start); if (d.valueOf() == event.start.valueOf()) found = true; }); if (!found) { Log.debug(`event.start=${event.start} was not included in results from rrule; adding`); dates.splice(0, 0, event.start); } - } + }*/ + // get our runtime timezone offset + const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) // Loop through the set of date entries to see which recurrences should be added to our event list. for (let d in dates) { let date = dates[d]; @@ -367,33 +420,41 @@ const CalendarFetcherUtils = { let curDurationMs = durationMs; let showRecurrence = true; - startMoment = moment(date); + let startMoment = moment(date) - // Remove the time information of each date by using its substring, using the following method: - // .toISOString().substring(0,10). - // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ - // (see https://momentjs.com/docs/#/displaying/as-iso-string/). - // This must be done after `date` is adjusted - const dateKey = date.toISOString().substring(0, 10); + let dateKey = CalendarFetcherUtils.getDateKeyFromDate(date, nowDiff) + Log.debug("event date dateKey=",dateKey) // For each date that we're checking, it's possible that there is a recurrence override for that one day. - if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { - // We found an override, so for this recurrence, use a potentially different title, start date, and duration. - curEvent = curEvent.recurrences[dateKey]; - startMoment = moment(curEvent.start); - curDurationMs = curEvent.end.valueOf() - startMoment.valueOf(); + if (curEvent.recurrences !== undefined){ + Log.debug("have recurrences=",curEvent.recurrences) + if(curEvent.recurrences[dateKey] !== undefined) { + Log.debug("have a recurrence match dateKey=",dateKey) + // We found an override, so for this recurrence, use a potentially different title, start date, and duration. + curEvent = curEvent.recurrences[dateKey]; + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event, nowDiff) //moment(curEvent.start); + date=curEvent.start + curDurationMs = curEvent.end.valueOf() - startMoment.valueOf(); + } } // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { - // This date is an exception date, which means we should skip it in the recurrence pattern. - showRecurrence = false; + else if (curEvent.exdate !== undefined){ + Log.debug("have datekey=", dateKey, " exdates=", curEvent.exdate) + if(curEvent.exdate[dateKey] !== undefined) { + // This date is an exception date, which means we should skip it in the recurrence pattern. + showRecurrence = false; + } } Log.debug(`duration: ${curDurationMs}`); + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(date, event, nowDiff) + endMoment = moment(startMoment.valueOf() + curDurationMs); + if (startMoment.valueOf() === endMoment.valueOf()) { endMoment = endMoment.endOf("day"); } + Log.debug("startMoment post=", startMoment) const recurrenceTitle = CalendarFetcherUtils.getTitleFromEvent(curEvent); @@ -408,7 +469,7 @@ const CalendarFetcherUtils = { } if (showRecurrence === true) { - Log.debug(`saving event: ${description}`); + Log.debug(`saving event: ${recurrenceTitle}`); newEvents.push({ title: recurrenceTitle, startDate: startMoment.format("x"), @@ -421,7 +482,10 @@ const CalendarFetcherUtils = { geo: geo, description: description }); + } else { + Log.debug("not saving event ", recurrenceTitle, new Date(startMoment)) } + Log.debug(" ") } // End recurring event parsing. } else { @@ -488,6 +552,104 @@ const CalendarFetcherUtils = { return newEvents; }, + getDateKeyFromDate(date, nowDiff){ + const startMoment = moment(date) + const startday= date.getDate(); + let adjustment=0 + Log.debug("startMoment pre=", startMoment," day of month=", ("0"+startday).slice(-2), " nowDiff=", nowDiff, " start time="+date.toString().split(" ")[4].slice(0,2)) + // Remove the time information of each date by using its substring, using the following method: + // .toISOString().substring(0,10). + // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ + // (see https://momentjs.com/docs/#/displaying/as-iso-string/). + // This must be done after `date` is adjusted + // + // BUT be careful of DST/STD date shift, use the raw day of the month, + // + Log.debug("date string= ",date.toString()) + Log.debug("date iso string ", date.toISOString() ) + // if the dates are different + if(date.toString().slice(8,10) != date.toISOString().slice(8,10)){ + // if the tostring date is less + if(date.toString().slice(8,10) < date.toISOString().slice(8,10)){ + Log.debug("hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) + if(date.toString().split(" ")[4].slice(0,2)<=date.toISOString().substring(7, 9)){ + adjustment=-1; + } else { + adjustment=1; + } + } else { // tostring is more + Log.debug("hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) + if(date.toString().split(" ")[4].slice(0,2)<=date.toISOString().substring(7, 9)){ + adjustment=1; + } else { + adjustment=-1; + } + } + } + return dateKey = date.toISOString().substring(0, 8)+("0"+(startday+adjustment)).slice(-2); + }, + getTimezoneOffsetFromTimezone(timeZone){ + const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); + Log.debug("tz offset=",str) + const [_,h,m] = str.match(/([+-]\d+):(\d+)$/) || [, '+00', '00']; + return h * 60 + (h > 0 ? +m : -m); + }, + getAdjustedStartMoment(date, event, nowDiff){ + let eventDiff= CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz) // watch out, start tz is cleared to handle rrule + + Log.debug("tz diff event=",eventDiff, " local=",nowDiff," end event timezone=", event.end.tz) + + // if the diffs are different (not same tz for processing as event) + if (nowDiff!=eventDiff){ + // if signs are different + if(Math.sign(nowDiff)!= Math.sign(eventDiff)){ + // its the accumulated total + Log.debug("diff signs, accumulate") + eventDiff=Math.abs(eventDiff)+Math.abs(nowDiff) + // sign of diff depends on where you are looking at which event. + // australia looking at US, add to get same time + Log.debug("new different event diff=",eventDiff) + if(Math.sign(nowDiff)==-1){ + eventDiff*=-1 + // US looking at australia event have to subtract + Log.debug("new same sign event diff=",eventDiff) + } + } + else{ + // signs are the same, all east of UTC or all west of UTC + // if the signs are negative (west of UTC) + Log.debug("signs are the same") + if(Math.sign(eventDiff)==-1){ + //if west, looking at more west + if(nowDiff { + return new Date("01 Oct 2024 10:38:00 GMT+2:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/chicago_late_in_timezone.js b/tests/configs/modules/calendar/chicago_late_in_timezone.js new file mode 100644 index 0000000000..ae44d775d6 --- /dev/null +++ b/tests/configs/modules/calendar/chicago_late_in_timezone.js @@ -0,0 +1,41 @@ +let config = { + timeFormat: 24, + logLevel: [ + "INFO", + "LOG", + "WARN", + "ERROR", + "DEBUG" + ], + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOFDays: 4, + calendars: [ + { + maximumEntries: 100, + //url: "http://localhost:8080/tests/mocks/chicago_late_in_timezone.ics" + url: "http://localhost:8080/modules/default/calendar/chicago_late_in_timezone.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("01 Sept 2024 10:38:00 GMT-05:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js new file mode 100644 index 0000000000..ca33056043 --- /dev/null +++ b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js @@ -0,0 +1,31 @@ +let config = { + timeFormat: 12, + + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + hideDuplicates: false, + maximumEntries: 100, + sliceMultiDayEvents: true, + dateFormat: "MMM Do, HH:mm", + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/germany_at_end_of_day_repeating.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("01 Sept 2024 10:38:00 GMT+2:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 8731c3cd1a..07853060e4 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -22,6 +22,14 @@ describe("Calendar module", () => { return await loc.count(); }; + const doTestTableContent = async (table_row, table_column, content) => { + const elem = await global.page.locator(table_row); + const table_col_data = await global.page.locator(table_column); + const date = table_col_data.first(); + await expect(date.textContent()).resolves.toContain(content); + return true; + }; + afterEach(async () => { await helpers.stopApplication(); }); @@ -147,4 +155,32 @@ describe("Calendar module", () => { }); }); + describe("sliceMultiDayEvents direct count", () => { + it("Issue #3452 split multiday in Europe", async () => { + await helpers.startApplication("tests/configs/modules/calendar/sliceMultiDayEvents.js", "01 Sept 2024 10:38:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestCount()).resolves.toBe(6); + }); + }); + + describe("germany timezone", () => { + it("Issue #unknown fullday timezone East of UTC edge", async () => { + await helpers.startApplication("tests/configs/modules/calendar/germany_at_end_of_day_repeating.js", "01 Sept 2024 10:38:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "Oct 22nd, 23:00")).resolves.toBe(true); + }); + }); + + describe("germany all day repeating moved (recurrence and exdate)", () => { + it("Issue #unknown fullday timezone East of UTC event moved", async () => { + await helpers.startApplication("tests/configs/modules/calendar/3_move_first_allday_repeating_event.js", "01 Oct 2024 10:38:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "12th.Oct")).resolves.toBe(true); + }); + }); + + describe("chicago late in timezone", () => { + it("Issue #unknown rrule US close to timezone edge", async () => { + await helpers.startApplication("tests/configs/modules/calendar/chicago_late_in_timezone.js", "01 Sept 2024 10:38:00 GMT-5:00", ["js/electron.js"], "America/Chicago"); + await expect(doTestTableContent(".calendar .event", ".time", "10th.Sep, 20:15")).resolves.toBe(true); + }); + }); + }); diff --git a/tests/mocks/3_move_first_allday_repeating_event.ics b/tests/mocks/3_move_first_allday_repeating_event.ics new file mode 100644 index 0000000000..d0587e35e1 --- /dev/null +++ b/tests/mocks/3_move_first_allday_repeating_event.ics @@ -0,0 +1,35 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:TestCal +X-WR-TIMEZONE:Europe/Berlin +X-WR-CALDESC:Calendar for testing purposes +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241011 +DTEND;VALUE=DATE:20241012 +RRULE:FREQ=WEEKLY;WKST=MO;COUNT=5;BYDAY=FR +DTSTAMP:20241009T153220Z +UID:2m6mt1p89l2anl74915ur3hsgm@google.com +CREATED:20241009T153058Z +LAST-MODIFIED:20241009T153205Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:TestCal_AllDayRepeatingEvent +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241012 +DTEND;VALUE=DATE:20241013 +DTSTAMP:20241009T153220Z +UID:2m6mt1p89l2anl74915ur3hsgm@google.com +RECURRENCE-ID;VALUE=DATE:20241011 +CREATED:20241009T153058Z +LAST-MODIFIED:20241009T153205Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:TestCal_AllDayRepeatingEvent +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR diff --git a/tests/mocks/chicago_late_in_timezone.ics b/tests/mocks/chicago_late_in_timezone.ics new file mode 100644 index 0000000000..2c9447723c --- /dev/null +++ b/tests/mocks/chicago_late_in_timezone.ics @@ -0,0 +1,15 @@ +BEGIN:VEVENT +CREATED:20240904T053053Z +DTEND;TZID=America/Chicago:20240910T211500 +DTSTAMP:20240925T005517Z +DTSTART;TZID=America/Chicago:20240910T201500 +LAST-MODIFIED:20240925T005515Z +LOCATION:Dance Class +RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:2D48CA37-FCE5-4E16-871 +9-1F47160BDBA3 +RRULE:FREQ=WEEKLY;UNTIL=20250601T011500Z +SEQUENCE:3 +SUMMARY:Wife Barre Class +UID:39669340-7AFD-4685-9BD6-6CE4B715486E +X-APPLE-CREATOR-IDENTITY:com.apple.mobilecal +END:VEVENT \ No newline at end of file diff --git a/tests/mocks/germany_at_end_of_day_repeating.ics b/tests/mocks/germany_at_end_of_day_repeating.ics new file mode 100644 index 0000000000..9a72df0aed --- /dev/null +++ b/tests/mocks/germany_at_end_of_day_repeating.ics @@ -0,0 +1,15 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20241022T230000 +DTEND;TZID=Europe/Berlin:20241023T000000 +RRULE:FREQ=DAILY;WKST=MO;COUNT=4 +DTSTAMP:20241009T153220Z +UID:2m6mt1p89l2anl74915ur3hsgm@google.com +CREATED:20241009T153058Z +LAST-MODIFIED:20241009T153205Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:TestCal_AllDayRepeatingEvent +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR \ No newline at end of file From b046b00fb804cd47e592168bea3fe3d7592e80ba Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 10:55:48 -0500 Subject: [PATCH 02/34] update to node-ical 0.19.0 --- package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 985f396fbc..a2e8c34843 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "iconv-lite": "^0.6.3", "module-alias": "^2.2.3", "moment": "^2.30.1", - "node-ical": "0.18.0", + "node-ical": "^0.19.0", "pm2": "^5.4.2", "socket.io": "^4.8.0", "suncalc": "^1.9.0", @@ -3251,12 +3251,12 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -9323,15 +9323,15 @@ } }, "node_modules/node-ical": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.18.0.tgz", - "integrity": "sha512-FrOUPztjw9OUgSB9o/ffhl86BiVClQTut97C2NqCwKIgOAcKPEw5UQMuSuNJO/Y4hqTyJdKZh2TCqNHQnE9YFg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.19.0.tgz", + "integrity": "sha512-WukOgHg7q44trEfA3wAYGHBvVP0LX6mR/STkI2da3Ke34F2Em0aUPIg+74SUf5qGymNraLSqUv6IpQYt2adOug==", "license": "Apache-2.0", "dependencies": { - "axios": "1.6.7", - "moment-timezone": "^0.5.44", + "axios": "1.7.7", + "moment-timezone": "^0.5.45", "rrule": "2.8.1", - "uuid": "^9.0.0" + "uuid": "^10.0.0" } }, "node_modules/node-int64": { @@ -12603,9 +12603,9 @@ } }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" From 40bfc53026b1c38b01bbf01c62d0b72870dc5a4a Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 10:58:32 -0500 Subject: [PATCH 03/34] add CHANGELOG update --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c31665ac36..ce83cc53a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,6 @@ _This release is scheduled to be released on 2025-01-01._ ### Added -- [linter] re-added `eslint-plugin-import`now that it supports ESLint v9 - ### Removed - [tests] Removed node-pty and drivelist from rebuilded test (#3575) @@ -21,13 +19,13 @@ _This release is scheduled to be released on 2025-01-01._ - [repo] reactivated `stale.yaml` as github action to mark issues as stale after 60 days and close them 7 days later (if no activity) - [core] Update electron dependency to v32 (test electron rebuild) -- [tests] All test configs have been updated to allow full external access, allowing for easier debugging (especially when running as a container) ### Fixed - [updatenotification] Fix pm2 using detection when pm2 script is in MagicMirror root folder (#3576) - [core] Fix loading node_helper of modules: avoid black screen, display errors and continue loading with next module (#3578) - [weather] changed default value for weatherEndpoint of provider openweathermap to "/onecall" (#3574) +- [calendar] - update to resolve issues #3098 #3144 #3351 #3422 #3443 #3467 #3537 related to timezone changes ## [2.29.0] - 2024-10-01 From b42bdb095d8af51fae52176ca6507059e9f30220 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 11:24:57 -0500 Subject: [PATCH 04/34] fix for notification from other modules --- modules/default/calendar/calendar.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 31b863eb28..e8d99b7d96 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -168,12 +168,17 @@ Module.register("calendar", { this.selfUpdate(); }, + notificationReceived(notification,payload,sender){ - // Override socket notification handler. - socketNotificationReceived (notification, payload) { if (notification === "FETCH_CALENDAR") { - this.sendSocketNotification(notification, { url: payload.url, id: this.identifier }); + if (this.hasCalendarURL(payload.url)) { + this.sendSocketNotification(notification, { url: payload.url, id: this.identifier }); + } } + }, + + // Override socket notification handler. + socketNotificationReceived (notification, payload) { if (this.identifier !== payload.id) { return; From 152312fee95ee93bbd1e20cae4a0c6cf9edd1483 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 14:28:04 -0500 Subject: [PATCH 05/34] remove logLevel config from chicago tz test config.js --- tests/configs/modules/calendar/chicago_late_in_timezone.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/configs/modules/calendar/chicago_late_in_timezone.js b/tests/configs/modules/calendar/chicago_late_in_timezone.js index ae44d775d6..2ff02c5d19 100644 --- a/tests/configs/modules/calendar/chicago_late_in_timezone.js +++ b/tests/configs/modules/calendar/chicago_late_in_timezone.js @@ -1,12 +1,5 @@ let config = { timeFormat: 24, - logLevel: [ - "INFO", - "LOG", - "WARN", - "ERROR", - "DEBUG" - ], modules: [ { module: "calendar", From f6d14aa64c094592751cf5d903ddc4c72e9bb285 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 17 Oct 2024 15:28:36 -0500 Subject: [PATCH 06/34] fix chicago test duration to cover ics event time --- .../default/calendar/calendarfetcherutils.js | 36 ++++++++++--------- .../calendar/chicago_late_in_timezone.js | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index b0990be55e..a6cac13e65 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -310,7 +310,7 @@ const CalendarFetcherUtils = { } }) } - const hasByWeekdayRule = rule.options.byweekday !== undefined && rule.options.byweekday !== null; + //const hasByWeekdayRule = rule.options.byweekday !== undefined && rule.options.byweekday !== null; Log.debug(`RRule: ${rule.toString()}`); rule.options.tzid = null; // RRule gets *very* confused with timezones @@ -318,6 +318,7 @@ const CalendarFetcherUtils = { let dates = rule.between(d1,d2, true, () => { return true; }); Log.debug(`Title: ${event.summary}, with dates: \n\n${JSON.stringify(dates)}\n`); + dates = dates.filter((d) => { if (JSON.stringify(d) === "null") return false; else return true; @@ -347,7 +348,7 @@ const CalendarFetcherUtils = { } // RRule can generate dates with an incorrect recurrence date. Process the array here and apply date correction. - if (false && hasByWeekdayRule) { + if (false) { Log.debug("Rule has byweekday, checking for correction"); dates.forEach((date, index, arr) => { // NOTE: getTimezoneOffset() is negative of the expected value. For America/Los_Angeles under DST (GMT-7), @@ -411,8 +412,6 @@ const CalendarFetcherUtils = { } }*/ - // get our runtime timezone offset - const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) // Loop through the set of date entries to see which recurrences should be added to our event list. for (let d in dates) { let date = dates[d]; @@ -422,7 +421,7 @@ const CalendarFetcherUtils = { let startMoment = moment(date) - let dateKey = CalendarFetcherUtils.getDateKeyFromDate(date, nowDiff) + let dateKey = CalendarFetcherUtils.getDateKeyFromDate(date) Log.debug("event date dateKey=",dateKey) // For each date that we're checking, it's possible that there is a recurrence override for that one day. @@ -432,7 +431,7 @@ const CalendarFetcherUtils = { Log.debug("have a recurrence match dateKey=",dateKey) // We found an override, so for this recurrence, use a potentially different title, start date, and duration. curEvent = curEvent.recurrences[dateKey]; - startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event, nowDiff) //moment(curEvent.start); + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event) //moment(curEvent.start); date=curEvent.start curDurationMs = curEvent.end.valueOf() - startMoment.valueOf(); } @@ -447,14 +446,13 @@ const CalendarFetcherUtils = { } Log.debug(`duration: ${curDurationMs}`); - startMoment = CalendarFetcherUtils.getAdjustedStartMoment(date, event, nowDiff) + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(date, event) endMoment = moment(startMoment.valueOf() + curDurationMs); if (startMoment.valueOf() === endMoment.valueOf()) { endMoment = endMoment.endOf("day"); } - Log.debug("startMoment post=", startMoment) const recurrenceTitle = CalendarFetcherUtils.getTitleFromEvent(curEvent); @@ -552,11 +550,12 @@ const CalendarFetcherUtils = { return newEvents; }, - getDateKeyFromDate(date, nowDiff){ - const startMoment = moment(date) + getDateKeyFromDate(date){ + // get our runtime timezone offset + const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) const startday= date.getDate(); let adjustment=0 - Log.debug("startMoment pre=", startMoment," day of month=", ("0"+startday).slice(-2), " nowDiff=", nowDiff, " start time="+date.toString().split(" ")[4].slice(0,2)) + Log.debug(" day of month=", ("0"+startday).slice(-2), " nowDiff=", nowDiff, " start time="+date.toString().split(" ")[4].slice(0,2)) // Remove the time information of each date by using its substring, using the following method: // .toISOString().substring(0,10). // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ @@ -589,12 +588,16 @@ const CalendarFetcherUtils = { return dateKey = date.toISOString().substring(0, 8)+("0"+(startday+adjustment)).slice(-2); }, getTimezoneOffsetFromTimezone(timeZone){ - const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); - Log.debug("tz offset=",str) - const [_,h,m] = str.match(/([+-]\d+):(\d+)$/) || [, '+00', '00']; - return h * 60 + (h > 0 ? +m : -m); + const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); + Log.debug("tz offset=",str) + const [_,h,m] = str.match(/([+-]\d+):(\d+)$/) || [, '+00', '00']; + return h * 60 + (h > 0 ? +m : -m); }, - getAdjustedStartMoment(date, event, nowDiff){ + getAdjustedStartMoment(date, event){ + // get our runtime timezone offset + let startMoment = moment(date) + Log.debug("startMoment pre=", startMoment) + const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) let eventDiff= CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz) // watch out, start tz is cleared to handle rrule Log.debug("tz diff event=",eventDiff, " local=",nowDiff," end event timezone=", event.end.tz) @@ -648,6 +651,7 @@ const CalendarFetcherUtils = { eventDiff=0 startMoment = moment.tz(new Date(date.valueOf()-(eventDiff*(60*1000))), event.end.tz); } + Log.debug("startMoment post=", startMoment) return startMoment }, /** diff --git a/tests/configs/modules/calendar/chicago_late_in_timezone.js b/tests/configs/modules/calendar/chicago_late_in_timezone.js index 2ff02c5d19..63fb2948ab 100644 --- a/tests/configs/modules/calendar/chicago_late_in_timezone.js +++ b/tests/configs/modules/calendar/chicago_late_in_timezone.js @@ -11,7 +11,7 @@ let config = { fullDayEventDateFormat: "Do.MMM", timeFormat: "absolute", getRelative: 0, - maximumNumberOFDays: 4, + maximumNumberOfDays: 20, calendars: [ { maximumEntries: 100, From 9bb089caaaf512491135e5538a2c1f5349ac08e6 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Sun, 20 Oct 2024 07:58:26 +1100 Subject: [PATCH 07/34] fix testcases add/ipwhitelist and chaicago in wrong place --- .../modules/calendar/3_move_first_allday_repeating_event.js | 3 +++ tests/configs/modules/calendar/chicago_late_in_timezone.js | 5 ++++- .../modules/calendar/germany_at_end_of_day_repeating.js | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js b/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js index 3b392755ce..8d1df3df0a 100644 --- a/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js +++ b/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js @@ -1,4 +1,7 @@ let config = { + address: "0.0.0.0", + ipWhitelist: [], + timeFormat: 24, units: "metric", modules: [ diff --git a/tests/configs/modules/calendar/chicago_late_in_timezone.js b/tests/configs/modules/calendar/chicago_late_in_timezone.js index 63fb2948ab..14c08e3e3e 100644 --- a/tests/configs/modules/calendar/chicago_late_in_timezone.js +++ b/tests/configs/modules/calendar/chicago_late_in_timezone.js @@ -1,4 +1,7 @@ let config = { + address: "0.0.0.0", + ipWhitelist: [], + timeFormat: 24, modules: [ { @@ -16,7 +19,7 @@ let config = { { maximumEntries: 100, //url: "http://localhost:8080/tests/mocks/chicago_late_in_timezone.ics" - url: "http://localhost:8080/modules/default/calendar/chicago_late_in_timezone.ics" + url: "http://localhost:8080/tests/mocks/chicago_late_in_timezone.ics" } ] } diff --git a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js index ca33056043..9310b3ffb4 100644 --- a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js +++ b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js @@ -1,4 +1,7 @@ let config = { + address: "0.0.0.0", + ipWhitelist: [], + timeFormat: 12, modules: [ From dbc2fe8e1df390b0de443353e3b3d485f2cc20fd Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Sat, 19 Oct 2024 18:07:18 -0500 Subject: [PATCH 08/34] fix and add another testcase for end of day roll over tested from diff timezone viewing --- .../default/calendar/calendarfetcherutils.js | 152 +++++++++++++----- .../calendar/end_of_day_berlin_moved.js | 37 +++++ tests/electron/modules/calendar_spec.js | 34 +++- tests/mocks/end_of_day_berlin_moved.ics | 54 +++++++ 4 files changed, 238 insertions(+), 39 deletions(-) create mode 100644 tests/configs/modules/calendar/end_of_day_berlin_moved.js create mode 100644 tests/mocks/end_of_day_berlin_moved.ics diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index a6cac13e65..5dad55b0d2 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -294,23 +294,9 @@ const CalendarFetcherUtils = { event.start=rule.options.dtstart// new Date(new Date(event.start.valueOf()).getTime()) Log.debug("fix rrule start=",rule.options.dtstart) - Log.debug("event before rrule.between=",event) - // if there are excluded dates, their date is incorrect and possibly key as well. - if(event.exdate != undefined){ - Object.keys(event.exdate).forEach(dateKey=>{ - // get the date - let exdate=event.exdate[dateKey] - exdate=new Date(new Date(exdate.valueOf()-(60*60*1000)).getTime()) - Log.debug("new exDate item=",exdate," with old key=", dateKey) - let newkey=exdate.toISOString().slice(0,10) - if(newkey!=dateKey){ - Log.debug("new exDate item=",exdate," key="+newkey) - event.exdate[newkey]=exdate - //delete event.exdate[dateKey] - } - }) - } - //const hasByWeekdayRule = rule.options.byweekday !== undefined && rule.options.byweekday !== null; + Log.debug("event before rrule.between=",JSON.stringify(event,null,2), "exdates=", event.exdate) + // fixup the exdate and recurrence date to local time too for post between() handling + CalendarFetcherUtils.fixEventtoLocal(event) Log.debug(`RRule: ${rule.toString()}`); rule.options.tzid = null; // RRule gets *very* confused with timezones @@ -389,13 +375,14 @@ const CalendarFetcherUtils = { // because the logic below will filter out any recurrences that don't actually belong within // our display range. // Would be great if there was a better way to handle this. - Log.debug(`event.recurrences: ${event.recurrences}`); + Log.debug("event.recurrences:",event.recurrences); if (event.recurrences !== undefined) { for (let dateKey in event.recurrences) { // Only add dates that weren't already in the range we added from the rrule so that // we don't double-add those events. let d = new Date(dateKey); - if (!moment(d).isBetween(pastMoment, futureMoment)) { + if (!moment(d).isBetween(d1, d2)) { + Log.debug("adding recurring event not found in between list =", d, " should not happen now using local dates oct 17,24") dates.push(d); } } @@ -428,12 +415,17 @@ const CalendarFetcherUtils = { if (curEvent.recurrences !== undefined){ Log.debug("have recurrences=",curEvent.recurrences) if(curEvent.recurrences[dateKey] !== undefined) { - Log.debug("have a recurrence match dateKey=",dateKey) + Log.debug("have a recurrence match for dateKey=",dateKey) // We found an override, so for this recurrence, use a potentially different title, start date, and duration. curEvent = curEvent.recurrences[dateKey]; + curEvent.start= new Date(new Date(curEvent.start.valueOf()).getTime()) + curEvent.end= new Date(new Date(curEvent.end.valueOf()).getTime()) startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event) //moment(curEvent.start); + endMoment= CalendarFetcherUtils.getAdjustedStartMoment(curEvent.end, event) date=curEvent.start - curDurationMs = curEvent.end.valueOf() - startMoment.valueOf(); + curDurationMs = new Date(endMoment).valueOf() - startMoment.valueOf(); + } else { + Log.debug("recurrence key ", dateKey, " doesn't match") } } // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. @@ -549,11 +541,81 @@ const CalendarFetcherUtils = { return newEvents; }, + fixEventtoLocal(event){ + // if there are excluded dates, their date is incorrect and possibly key as well. + if(event.exdate != undefined){ + Object.keys(event.exdate).forEach(dateKey=>{ + // get the date + let exdate=event.exdate[dateKey] + Log.debug("exdate w key=", exdate) + //exdate=CalendarFetcherUtils.convertDateToLocalTime(exdate, event.end.tz) + exdate=new Date(new Date(exdate.valueOf()-((120*60*1000))).getTime()) + Log.debug("new exDate item=",exdate," with old key=", dateKey) + let newkey=exdate.toISOString().slice(0,10) + if(newkey!=dateKey){ + Log.debug("new exDate item=",exdate," key="+newkey) + event.exdate[newkey]=exdate + //delete event.exdate[dateKey] + } + }) + Log.debug("updated exdate list=", event.exdate) + } + if(event.recurrences){ + Object.keys(event.recurrences).forEach(dateKey=>{ + let exdate=event.recurrences[dateKey] + //exdate=new Date(new Date(exdate.valueOf()-(60*60*1000)).getTime()) + Log.debug("new recurrence item=",exdate," with old key=", dateKey) + exdate.start=CalendarFetcherUtils.convertDateToLocalTime(exdate.start, exdate.start.tz) + exdate.end=CalendarFetcherUtils.convertDateToLocalTime(exdate.end, exdate.end.tz) + Log.debug("adjusted recurringEvent start=", exdate.start, " end=",exdate.end) + }) + } + Log.debug("modified recurrences before rrule.between", event.recurrences) + }, + convertDateToLocalTime(date,tz){ + let delta_tz_offset=0 + let now_offset=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) + let event_offset=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(tz) + Log.debug("date to convert=", date) + if(Math.sign(now_offset)!=Math.sign(event_offset)){ + delta_tz_offset=Math.abs(now_offset)+Math.abs(event_offset) + } else { + // signs are the same + // if negative + if(Math.sign(now_offset)==-1){ + // la looking at chicago + if(now_offsetdate.toISOString().substring(11, 13)){ + Log.debug("tso ",date.toString().split(" ")[4].slice(0,2)," is more than iso ",date.toISOString().substring(11, 13)) adjustment=-1; + Log.debug("subtracting a day") } else { + Log.debug("adding a day") adjustment=1; - } + }*/ } else { // tostring is more - Log.debug("hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) - if(date.toString().split(" ")[4].slice(0,2)<=date.toISOString().substring(7, 9)){ - adjustment=1; - } else { - adjustment=-1; + if(date.toString().slice(8,10) > date.toISOString().slice(8,10)){ + startday=date.toISOString().slice(8,10) + Log.debug("> ", startday) + /*Log.debug("more hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) + if(date.toString().split(" ")[4].slice(0,2)date.toISOString().substring(11, 13)){ + adjustment=-1; + Log.debug("subtracting a day") + } + }*/ } } - } - return dateKey = date.toISOString().substring(0, 8)+("0"+(startday+adjustment)).slice(-2); + //} + return dateKey = date.toISOString().substring(0, 8)+("0"+startday).slice(-2); }, getTimezoneOffsetFromTimezone(timeZone){ const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); @@ -594,11 +670,13 @@ const CalendarFetcherUtils = { return h * 60 + (h > 0 ? +m : -m); }, getAdjustedStartMoment(date, event){ - // get our runtime timezone offset + let startMoment = moment(date) + Log.debug("startMoment pre=", startMoment) - const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) - let eventDiff= CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz) // watch out, start tz is cleared to handle rrule + // get our runtime timezone offset + const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) // 10/18 16:49, 300 + let eventDiff= CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz) // watch out, start tz is cleared to handle rrule 120 23:49 Log.debug("tz diff event=",eventDiff, " local=",nowDiff," end event timezone=", event.end.tz) @@ -615,7 +693,7 @@ const CalendarFetcherUtils = { if(Math.sign(nowDiff)==-1){ eventDiff*=-1 // US looking at australia event have to subtract - Log.debug("new same sign event diff=",eventDiff) + Log.debug("new diff, same sign, total event diff=",eventDiff) } } else{ diff --git a/tests/configs/modules/calendar/end_of_day_berlin_moved.js b/tests/configs/modules/calendar/end_of_day_berlin_moved.js new file mode 100644 index 0000000000..3332f7bcf6 --- /dev/null +++ b/tests/configs/modules/calendar/end_of_day_berlin_moved.js @@ -0,0 +1,37 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/end_of_day_berlin_moved.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 07853060e4..da56fdb822 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -1,4 +1,7 @@ const helpers = require("../helpers/global-setup"); +//const stdMocks = require('std-mocks') + +//stdMocks.use(); describe("Calendar module", () => { @@ -22,10 +25,12 @@ describe("Calendar module", () => { return await loc.count(); }; - const doTestTableContent = async (table_row, table_column, content) => { + const doTestTableContent = async (table_row, table_column, content, last = false) => { const elem = await global.page.locator(table_row); const table_col_data = await global.page.locator(table_column); - const date = table_col_data.first(); + let date; + if (last) date = table_col_data.last(); + else date = table_col_data.first(); await expect(date.textContent()).resolves.toContain(content); return true; }; @@ -183,4 +188,29 @@ describe("Calendar module", () => { }); }); + describe("berlin late in day event moved, viewed from berlin", () => { + it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { + await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", true)).resolves.toBe(true); + }); + }); + + describe("berlin late in day event moved, viewed from sydney", () => { + it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { + await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Australia/Sydney"); + await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct, 01:00-02:00", true)).resolves.toBe(true); + }); + }); + + describe("berlin late in day event moved, viewed from chicago", () => { + it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { + await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "America/Chicago"); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 16:00-17:00", true)).resolves.toBe(true); + }); + }); + + }); + +//var output = stdMocks.flush() +//console.log(output.stdout) diff --git a/tests/mocks/end_of_day_berlin_moved.ics b/tests/mocks/end_of_day_berlin_moved.ics new file mode 100644 index 0000000000..c5a02d0336 --- /dev/null +++ b/tests/mocks/end_of_day_berlin_moved.ics @@ -0,0 +1,54 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:test for mirror +X-WR-TIMEZONE:America/Chicago +X-WR-CALDESC:used to test mirror +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:GMT+2 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:GMT+1 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20241021T230000 +DTEND;TZID=Europe/Berlin:20241022T000000 +RRULE:FREQ=DAILY;WKST=SU;COUNT=3 +DTSTAMP:20241019T133432Z +UID:0kj3dtvgskhhpli1392n111145@google.com +CREATED:20241018T213040Z +LAST-MODIFIED:20241018T213126Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:test +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20241024T230000 +DTEND;TZID=Europe/Berlin:20241025T000000 +DTSTAMP:20241019T133432Z +UID:0kj3dtvgskhhpli1392n111145@google.com +RECURRENCE-ID;TZID=Europe/Berlin:20241021T230000 +CREATED:20241018T213040Z +LAST-MODIFIED:20241018T213126Z +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:test +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR + From 626c5e7f454ed30864b73d4f1bcf0bc4e7eef7da Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Sun, 20 Oct 2024 17:50:32 +0200 Subject: [PATCH 09/34] use updated node-ical add new testcases to be used --- package.json | 2 +- tests/mocks/RepeatingEvent.Oct21.ics | 28 +++++++++++++++++++ ...whole_day_moved_over_dst_change_berlin.ics | 28 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/mocks/RepeatingEvent.Oct21.ics create mode 100644 tests/mocks/whole_day_moved_over_dst_change_berlin.ics diff --git a/package.json b/package.json index 4b87cabb24..d897678167 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "iconv-lite": "^0.6.3", "module-alias": "^2.2.3", "moment": "^2.30.1", - "node-ical": "^0.19.0", + "node-ical": "github:sdetweil/node-ical#fixdates", "pm2": "^5.4.2", "socket.io": "^4.8.0", "suncalc": "^1.9.0", diff --git a/tests/mocks/RepeatingEvent.Oct21.ics b/tests/mocks/RepeatingEvent.Oct21.ics new file mode 100644 index 0000000000..9eb6130960 --- /dev/null +++ b/tests/mocks/RepeatingEvent.Oct21.ics @@ -0,0 +1,28 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20241028T000000 +DTEND;TZID=Europe/Berlin:20241028T010000 +RRULE:FREQ=DAILY;COUNT=3 +DTSTAMP:20241020T093758Z +UID:053fdshnnibo92lu97rsoeqoti@google.com +CREATED:20241020T093230Z +LAST-MODIFIED:20241020T093230Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:RepeatingEventWeekAfterToday +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20241021T000000 +DTEND;TZID=Europe/Berlin:20241021T010000 +RRULE:FREQ=DAILY;COUNT=3 +DTSTAMP:20241020T093758Z +UID:1a6kk47pp61k4td2h9rlf0lv69@google.com +CREATED:20241020T093255Z +LAST-MODIFIED:20241020T093437Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:RepeatingEventDayAfterToday +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/tests/mocks/whole_day_moved_over_dst_change_berlin.ics b/tests/mocks/whole_day_moved_over_dst_change_berlin.ics new file mode 100644 index 0000000000..7335ccfdb4 --- /dev/null +++ b/tests/mocks/whole_day_moved_over_dst_change_berlin.ics @@ -0,0 +1,28 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241027 +DTEND;VALUE=DATE:20241028 +RRULE:FREQ=DAILY;WKST=SU;COUNT=3 +DTSTAMP:20241020T152634Z +UID:14nv8jl8d6dvdbl477lod4fftf@google.com +CREATED:20241020T152434Z +LAST-MODIFIED:20241020T152536Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:test whole day moved +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241030 +DTEND;VALUE=DATE:20241031 +DTSTAMP:20241020T152634Z +UID:14nv8jl8d6dvdbl477lod4fftf@google.com +RECURRENCE-ID;VALUE=DATE:20241028 +CREATED:20241020T152434Z +LAST-MODIFIED:20241020T152536Z +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:test whole day moved +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR From ebca3074c690140f11633c9ec1746d303a26816a Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 21 Oct 2024 15:28:07 +0200 Subject: [PATCH 10/34] add testcases for edge conditions --- .../calendar/berlin_end_of_day_repeating.js | 37 +++++++++++++++++++ .../configs/modules/calendar/berlin_multi.js | 37 +++++++++++++++++++ ...n_whole_day_event_moved_over_dst_change.js | 37 +++++++++++++++++++ tests/electron/modules/calendar_spec.js | 24 ++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 tests/configs/modules/calendar/berlin_end_of_day_repeating.js create mode 100644 tests/configs/modules/calendar/berlin_multi.js create mode 100644 tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js diff --git a/tests/configs/modules/calendar/berlin_end_of_day_repeating.js b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js new file mode 100644 index 0000000000..3332f7bcf6 --- /dev/null +++ b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js @@ -0,0 +1,37 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/end_of_day_berlin_moved.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/berlin_multi.js b/tests/configs/modules/calendar/berlin_multi.js new file mode 100644 index 0000000000..8b7d953419 --- /dev/null +++ b/tests/configs/modules/calendar/berlin_multi.js @@ -0,0 +1,37 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/RepeatingEvent.Oct21.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js b/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js new file mode 100644 index 0000000000..65c00cb973 --- /dev/null +++ b/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js @@ -0,0 +1,37 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/whole_day_moved_over_dst_change_berlin.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index da56fdb822..fce4524408 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -209,6 +209,30 @@ describe("Calendar module", () => { }); }); + describe("berlin multi-events inside offset", () => { + it("some events before DST. some after midnight", async () => { + await helpers.startApplication("tests/configs/modules/calendar/berlin_multi.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct, 00:00-01:00", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "21st.Oct, 00:00-01:00", false)).resolves.toBe(true); + }); + }); + + describe("berlin whole day repeating, start moved after end", () => { + it("some events before DST. some after", async () => { + await helpers.startApplication("tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "27th.Oct", false)).resolves.toBe(true); + }); + }); + + describe("berlin 11pm-midnight", () => { + it("right inside the offset, before midnight", async () => { + await helpers.startApplication("tests/configs/modules/calendar/berlin_end_of_day_repeating.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "22nd.Oct, 23:00-00:00", false)).resolves.toBe(true); + }); + }); + }); From b9eddc0c3f41f10d24e04caef4b4dd92c723dd85 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 21 Oct 2024 17:06:14 -0500 Subject: [PATCH 11/34] fix for 3267 and event with both exdate and recurrence changes --- CHANGELOG.md | 1 + modules/default/calendar/calendar.js | 5 +- .../default/calendar/calendarfetcherutils.js | 95 +++++-------------- .../exdate_and_recurrence_together.js | 37 ++++++++ tests/electron/modules/calendar_spec.js | 43 ++++++--- .../mocks/exdate_and_recurrence_together.ics | 48 ++++++++++ 6 files changed, 145 insertions(+), 84 deletions(-) create mode 100644 tests/configs/modules/calendar/exdate_and_recurrence_together.js create mode 100644 tests/mocks/exdate_and_recurrence_together.ics diff --git a/CHANGELOG.md b/CHANGELOG.md index 93efc1a5bb..1028eb14a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ _This release is scheduled to be released on 2025-01-01._ - [core] Fix loading node_helper of modules: avoid black screen, display errors and continue loading with next module (#3578) - [weather] changed default value for weatherEndpoint of provider openweathermap to "/onecall" (#3574) - [calendar] - update to resolve issues #3098 #3144 #3351 #3422 #3443 #3467 #3537 related to timezone changes +- [calendar] - fixes #3267 (styles array), also fixes event with both exdate AND recurrence(and testcase) ## [2.29.0] - 2024-10-01 diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index e8d99b7d96..e31bd3d9e4 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -891,7 +891,10 @@ Module.register("calendar", { let p = this.getCalendarProperty(url, property, defaultValue); if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") { const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName); - p = className + p; + if(p instanceof Array) + p.push(className) + else + p = className + p; } if (!(p instanceof Array)) p = [p]; diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 5dad55b0d2..ffde4ac6f9 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -305,68 +305,34 @@ const CalendarFetcherUtils = { Log.debug(`Title: ${event.summary}, with dates: \n\n${JSON.stringify(dates)}\n`); + // shouldn't need this anymore, as RRULE not passed junk dates = dates.filter((d) => { if (JSON.stringify(d) === "null") return false; else return true; }); - if(true){ - - let datesLocal=[] - let offset = d1.getTimezoneOffset() // this will be local timezone.. oops.. need relative to ics timezone - Log.debug("offset =",offset) - dates.forEach(d =>{ - let dtext=d.toISOString().slice(0,-5) - Log.debug(" date text form without tz=",dtext) - let dLocal= new Date(d.valueOf()+(offset*60000)) - let offset2=dLocal.getTimezoneOffset() - Log.debug("date after offset applied=", dLocal) - if(offset!=offset2){ - // woops, dst/std switch - let delta = offset-offset2 - Log.debug("offset delta=", delta) - dLocal= new Date(d.valueOf()+((offset-delta)*60000)) - Log.debug("corrected normalized date=",dLocal) - } else - Log.debug(" neutralized date=",dLocal); - datesLocal.push(dLocal) - }) - dates=datesLocal - } - // RRule can generate dates with an incorrect recurrence date. Process the array here and apply date correction. - if (false) { - Log.debug("Rule has byweekday, checking for correction"); - dates.forEach((date, index, arr) => { - // NOTE: getTimezoneOffset() is negative of the expected value. For America/Los_Angeles under DST (GMT-7), - // this value is +420. For Australia/Sydney under DST (GMT+11), this value is -660. - const tzOffset = date.getTimezoneOffset() / 60; - const hour = date.getHours(); - if ((tzOffset < 0) && (hour < -tzOffset)) { // east of GMT - Log.debug(`East of GMT (tzOffset: ${tzOffset}) and hour=${hour} < ${-tzOffset}, Subtracting 1 day from ${date}`); - arr[index] = new Date(date.valueOf() - oneDayInMs); - } else if ((tzOffset > 0) && (hour >= (24 - tzOffset))) { // west of GMT - Log.debug(`West of GMT (tzOffset: ${tzOffset}) and hour=${hour} >= 24-${tzOffset}, Adding 1 day to ${date}`); - arr[index] = new Date(date.valueOf() + oneDayInMs); - } - }); - // Adjusting the dates could push it beyond the 'until' date, so filter those out here. - if (rule.options.until !== null) { - dates = dates.filter((date) => { - return date.valueOf() <= rule.options.until.valueOf(); - }); - } - } + // go thru all the rrule.between() dates and put back the tz offset removed so between would work + let datesLocal=[] + let offset = d1.getTimezoneOffset() + Log.debug("offset =",offset) + dates.forEach(d =>{ + let dtext=d.toISOString().slice(0,-5) + Log.debug(" date text form without tz=",dtext) + let dLocal= new Date(d.valueOf()+(offset*60000)) + let offset2=dLocal.getTimezoneOffset() + Log.debug("date after offset applied=", dLocal) + if(offset!=offset2){ + // woops, dst/std switch + let delta = offset-offset2 + Log.debug("offset delta=", delta) + dLocal= new Date(d.valueOf()+((offset-delta)*60000)) + Log.debug("corrected normalized date=",dLocal) + } else + Log.debug(" neutralized date=",dLocal); + datesLocal.push(dLocal) + }) + dates=datesLocal - // The dates array from rrule can be confused by DST. If the event was created during DST and we - // are querying a date range during non-DST, rrule can have the incorrect time for the date range. - // Reprocess the array here computing and applying the time offset. - /*dates.forEach((date, index, arr) => { - let adjustHours = CalendarFetcherUtils.calculateTimezoneAdjustment(event, date); - if (adjustHours !== 0) { - Log.debug(`Applying timezone adjustment hours=${adjustHours} to ${date}`); - arr[index] = new Date(date.valueOf() + (adjustHours * 60 * 60 * 1000)); - } - });*/ // The "dates" array contains the set of dates within our desired date range range that are valid // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that @@ -388,17 +354,6 @@ const CalendarFetcherUtils = { } } - // Lastly, sometimes rrule doesn't include the event.start even if it is in the requested range. Ensure - // inclusion here. Unfortunately dates.includes() doesn't find it so we have to do forEach(). - /*{ - let found = false; - dates.forEach((d) => { Log.debug(" d=",d, "start=", event.start); if (d.valueOf() == event.start.valueOf()) found = true; }); - if (!found) { - Log.debug(`event.start=${event.start} was not included in results from rrule; adding`); - dates.splice(0, 0, event.start); - } - }*/ - // Loop through the set of date entries to see which recurrences should be added to our event list. for (let d in dates) { let date = dates[d]; @@ -420,7 +375,7 @@ const CalendarFetcherUtils = { curEvent = curEvent.recurrences[dateKey]; curEvent.start= new Date(new Date(curEvent.start.valueOf()).getTime()) curEvent.end= new Date(new Date(curEvent.end.valueOf()).getTime()) - startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event) //moment(curEvent.start); + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event) endMoment= CalendarFetcherUtils.getAdjustedStartMoment(curEvent.end, event) date=curEvent.start curDurationMs = new Date(endMoment).valueOf() - startMoment.valueOf(); @@ -429,7 +384,7 @@ const CalendarFetcherUtils = { } } // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - else if (curEvent.exdate !== undefined){ + if (curEvent.exdate !== undefined){ Log.debug("have datekey=", dateKey, " exdates=", curEvent.exdate) if(curEvent.exdate[dateKey] !== undefined) { // This date is an exception date, which means we should skip it in the recurrence pattern. @@ -526,7 +481,9 @@ const CalendarFetcherUtils = { startDate: startMoment.add(adjustHours, "hours").format("x"), endDate: endMoment.add(adjustHours, "hours").format("x"), fullDayEvent: fullDayEvent, + recurringEvent: false, class: event.class, + firstYear: event.start.getFullYear(), location: location, geo: geo, description: description diff --git a/tests/configs/modules/calendar/exdate_and_recurrence_together.js b/tests/configs/modules/calendar/exdate_and_recurrence_together.js new file mode 100644 index 0000000000..8e5790de79 --- /dev/null +++ b/tests/configs/modules/calendar/exdate_and_recurrence_together.js @@ -0,0 +1,37 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/exdate_and_recurrence_together.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT+07:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index fce4524408..b46ee0eb13 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -25,12 +25,16 @@ describe("Calendar module", () => { return await loc.count(); }; - const doTestTableContent = async (table_row, table_column, content, last = false) => { + const first = 0; + const second = 1; + const third = 2; + const last = -1; + + // get results of table row and column, can select specific row of results, + // row is 0 based index -1 is last, 0 is first... need 10th(human count), use 9 as row + const doTestTableContent = async (table_row, table_column, content, row = first) => { const elem = await global.page.locator(table_row); - const table_col_data = await global.page.locator(table_column); - let date; - if (last) date = table_col_data.last(); - else date = table_col_data.first(); + const date = await global.page.locator(table_column).locator(`nth=${row}`); await expect(date.textContent()).resolves.toContain(content); return true; }; @@ -191,48 +195,59 @@ describe("Calendar module", () => { describe("berlin late in day event moved, viewed from berlin", () => { it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); - await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", last)).resolves.toBe(true); }); }); describe("berlin late in day event moved, viewed from sydney", () => { it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Australia/Sydney"); - await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct, 01:00-02:00", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct, 01:00-02:00", last)).resolves.toBe(true); }); }); describe("berlin late in day event moved, viewed from chicago", () => { it("Issue #unknown rrule ETC+2 close to timezone edge", async () => { await helpers.startApplication("tests/configs/modules/calendar/end_of_day_berlin_moved.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "America/Chicago"); - await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 16:00-17:00", true)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 16:00-17:00", last)).resolves.toBe(true); }); }); describe("berlin multi-events inside offset", () => { it("some events before DST. some after midnight", async () => { await helpers.startApplication("tests/configs/modules/calendar/berlin_multi.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); - await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct, 00:00-01:00", true)).resolves.toBe(true); - await expect(doTestTableContent(".calendar .event", ".time", "21st.Oct, 00:00-01:00", false)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct, 00:00-01:00", last)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "21st.Oct, 00:00-01:00", first)).resolves.toBe(true); }); }); describe("berlin whole day repeating, start moved after end", () => { it("some events before DST. some after", async () => { await helpers.startApplication("tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); - await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct", true)).resolves.toBe(true); - await expect(doTestTableContent(".calendar .event", ".time", "27th.Oct", false)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "30th.Oct", last)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "27th.Oct", first)).resolves.toBe(true); }); }); describe("berlin 11pm-midnight", () => { it("right inside the offset, before midnight", async () => { await helpers.startApplication("tests/configs/modules/calendar/berlin_end_of_day_repeating.js", "08 Oct 2024 12:30:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); - await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", true)).resolves.toBe(true); - await expect(doTestTableContent(".calendar .event", ".time", "22nd.Oct, 23:00-00:00", false)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "24th.Oct, 23:00-00:00", last)).resolves.toBe(true); + await expect(doTestTableContent(".calendar .event", ".time", "22nd.Oct, 23:00-00:00", first)).resolves.toBe(true); }); }); + describe("both moved and delete events in recurring list", () => { + it("with moved before and after original", async () => { + await helpers.startApplication("tests/configs/modules/calendar/exdate_and_recurrence_together.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Los_Angeles"); + // moved after end at oct 26 + await expect(doTestTableContent(".calendar .event", ".time", "27th.Oct, 14:30-15:30", last)).resolves.toBe(true); + // moved before start at oct 23 + await expect(doTestTableContent(".calendar .event", ".time", "22nd.Oct, 14:30-15:30", first)).resolves.toBe(true); + // remaining original 4th, now 3rd + await expect(doTestTableContent(".calendar .event", ".time", "26th.Oct, 14:30-15:30", second)).resolves.toBe(true); + }); + }); }); diff --git a/tests/mocks/exdate_and_recurrence_together.ics b/tests/mocks/exdate_and_recurrence_together.ics new file mode 100644 index 0000000000..d366cc42df --- /dev/null +++ b/tests/mocks/exdate_and_recurrence_together.ics @@ -0,0 +1,48 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;TZID=America/Los_Angeles:20241023T143000 +DTEND;TZID=America/Los_Angeles:20241023T153000 +RRULE:FREQ=DAILY;COUNT=4 +EXDATE;TZID=America/Los_Angeles:20241025T143000 +DTSTAMP:20241021T193426Z +UID:18rd721lfqpue2o08icsqek198@google.com +CREATED:20241021T192450Z +DESCRIPTION:we will move one entry and delete another  ending w 3 of the 4  + start/end\, middle moved after end and 3rd deleted +LAST-MODIFIED:20241021T193419Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:recurrence and exclusion together +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Los_Angeles:20241022T143000 +DTEND;TZID=America/Los_Angeles:20241022T153000 +DTSTAMP:20241021T193426Z +UID:18rd721lfqpue2o08icsqek198@google.com +RECURRENCE-ID;TZID=America/Los_Angeles:20241023T143000 +CREATED:20241021T192450Z +DESCRIPTION:we will move one entry and delete another  ending w 3 of the 4  + start/end\, middle moved after end and 3rd deleted +LAST-MODIFIED:20241021T193419Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:recurrence and exclusion together +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Los_Angeles:20241027T143000 +DTEND;TZID=America/Los_Angeles:20241027T153000 +DTSTAMP:20241021T193426Z +UID:18rd721lfqpue2o08icsqek198@google.com +RECURRENCE-ID;TZID=America/Los_Angeles:20241024T143000 +CREATED:20241021T192450Z +DESCRIPTION:we will move one entry and delete another  ending w 3 of the 4  + start/end\, middle moved after end and 3rd deleted +LAST-MODIFIED:20241021T193419Z +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:recurrence and exclusion together +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR From 5309d4b6802a2a63801c06561fdab2451e633bba Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 21 Oct 2024 17:11:58 -0500 Subject: [PATCH 12/34] use updated node-ical, not yet on npm store --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d897678167..aa1c2b1440 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "iconv-lite": "^0.6.3", "module-alias": "^2.2.3", "moment": "^2.30.1", - "node-ical": "github:sdetweil/node-ical#fixdates", + "node-ical": "github:jens-maus/node-ical#master", "pm2": "^5.4.2", "socket.io": "^4.8.0", "suncalc": "^1.9.0", From f6bf0c17910acac02f0b81b9b5cbffce88c95b28 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 21 Oct 2024 17:56:03 -0500 Subject: [PATCH 13/34] cleanup up utils, remove dead old code, add header to new functions --- .../default/calendar/calendarfetcherutils.js | 89 ++++++++++--------- tests/electron/modules/calendar_spec.js | 1 + 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index ffde4ac6f9..9ecbb73181 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -263,9 +263,10 @@ const CalendarFetcherUtils = { // For recurring events, get the set of start dates that fall within the range // of dates we're looking for. - // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time + let pastLocal; let futureLocal; + if (CalendarFetcherUtils.isFullDayEvent(event)) { Log.debug("fullday"); // if full day event, only use the date part of the ranges @@ -290,8 +291,7 @@ const CalendarFetcherUtils = { d2=new Date(new Date(futureLocal.valueOf() + oneDayInMs).getTime()) Log.debug(`Search for recurring events between: ${d1} and ${d2}`); - //rule.origOptions.dtstart=rule.options.dtstart= - event.start=rule.options.dtstart// new Date(new Date(event.start.valueOf()).getTime()) + event.start=rule.options.dtstart Log.debug("fix rrule start=",rule.options.dtstart) Log.debug("event before rrule.between=",JSON.stringify(event,null,2), "exdates=", event.exdate) @@ -341,6 +341,9 @@ const CalendarFetcherUtils = { // because the logic below will filter out any recurrences that don't actually belong within // our display range. // Would be great if there was a better way to handle this. + // + // i don't think we will ever see this anymore (oct 2024) due to code fixes for rrule.between() + // Log.debug("event.recurrences:",event.recurrences); if (event.recurrences !== undefined) { for (let dateKey in event.recurrences) { @@ -498,6 +501,12 @@ const CalendarFetcherUtils = { return newEvents; }, + /** + * fixup thew event fields that have dates to use local time + * BEFORE calling rrule.between + * @param the event being processed + * @returns nothing + */ fixEventtoLocal(event){ // if there are excluded dates, their date is incorrect and possibly key as well. if(event.exdate != undefined){ @@ -529,7 +538,13 @@ const CalendarFetcherUtils = { } Log.debug("modified recurrences before rrule.between", event.recurrences) }, - + /** + * convert a UTC date to local time + * BEFORE calling rrule.between + * @param date ti conert + * tz event is currently in + * @returns updated date object + */ convertDateToLocalTime(date,tz){ let delta_tz_offset=0 let now_offset=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) @@ -569,63 +584,51 @@ const CalendarFetcherUtils = { Log.debug("modified date =", newdate) return newdate }, + /** + * get the exdate/recurrence hash key from the date object + * BEFORE calling rrule.between + * @param the date of the event + * @returns string date key YYYY-MM-DD + */ getDateKeyFromDate(date){ // get our runtime timezone offset const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) let startday= date.getDate(); let adjustment=0 Log.debug(" day of month=", ("0"+startday).slice(-2), " nowDiff=", nowDiff, " start time="+date.toString().split(" ")[4].slice(0,2)) - // Remove the time information of each date by using its substring, using the following method: - // .toISOString().substring(0,10). - // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ - // (see https://momentjs.com/docs/#/displaying/as-iso-string/). - // This must be done after `date` is adjusted - // - // BUT be careful of DST/STD date shift, use the raw day of the month, - // Log.debug("date string= ",date.toString()) Log.debug("date iso string ", date.toISOString() ) // if the dates are different - //if(date.toString().slice(8,10) != date.toISOString().slice(8,10)){ - // if the tostring date is less - if(date.toString().slice(8,10) < date.toISOString().slice(8,10)){ - startday=date.toString().slice(8,10) - Log.debug("< ", startday) - /* - Log.debug("less hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) - if(date.toString().split(" ")[4].slice(0,2)>date.toISOString().substring(11, 13)){ - Log.debug("tso ",date.toString().split(" ")[4].slice(0,2)," is more than iso ",date.toISOString().substring(11, 13)) - adjustment=-1; - Log.debug("subtracting a day") - } else { - Log.debug("adding a day") - adjustment=1; - }*/ - } else { // tostring is more - if(date.toString().slice(8,10) > date.toISOString().slice(8,10)){ - startday=date.toISOString().slice(8,10) - Log.debug("> ", startday) - /*Log.debug("more hour before conversion=",date.toString().split(" ")[4].slice(0,2), " after=", date.toISOString().substring(11, 13) ) - if(date.toString().split(" ")[4].slice(0,2)date.toISOString().substring(11, 13)){ - adjustment=-1; - Log.debug("subtracting a day") - } - }*/ - } + if(date.toString().slice(8,10) < date.toISOString().slice(8,10)){ + startday=date.toString().slice(8,10) + Log.debug("< ", startday) + } else { // tostring is more + if(date.toString().slice(8,10) > date.toISOString().slice(8,10)){ + startday=date.toISOString().slice(8,10) + Log.debug("> ", startday) } - //} + } return dateKey = date.toISOString().substring(0, 8)+("0"+startday).slice(-2); }, + /** + * get the timezone offset from the timezone string + * + * @param the timezone string + * @returns the numerical offset + */ getTimezoneOffsetFromTimezone(timeZone){ const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); Log.debug("tz offset=",str) const [_,h,m] = str.match(/([+-]\d+):(\d+)$/) || [, '+00', '00']; return h * 60 + (h > 0 ? +m : -m); }, + /** + * fixup the date start moment after rrule.between returns date array + * + * @param date object from rrule.between results + * the event object it came from + * @returns moment object + */ getAdjustedStartMoment(date, event){ let startMoment = moment(date) diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index b46ee0eb13..42d3cab529 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -32,6 +32,7 @@ describe("Calendar module", () => { // get results of table row and column, can select specific row of results, // row is 0 based index -1 is last, 0 is first... need 10th(human count), use 9 as row + // uses playwright nth locator syntax const doTestTableContent = async (table_row, table_column, content, row = first) => { const elem = await global.page.locator(table_row); const date = await global.page.locator(table_column).locator(`nth=${row}`); From 2402fff7b64610d387abcc77fd86f056aa3409d4 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 22 Oct 2024 17:12:47 -0500 Subject: [PATCH 14/34] fix on testcase, add another for start one tz, end another --- .../modules/calendar/diff_tz_start_end.js | 38 +++++++++++++++++++ .../germany_at_end_of_day_repeating.js | 5 ++- tests/electron/modules/calendar_spec.js | 12 +++++- tests/mocks/diff_tz_start_end.ics | 14 +++++++ 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/configs/modules/calendar/diff_tz_start_end.js create mode 100644 tests/mocks/diff_tz_start_end.ics diff --git a/tests/configs/modules/calendar/diff_tz_start_end.js b/tests/configs/modules/calendar/diff_tz_start_end.js new file mode 100644 index 0000000000..cbd658cf48 --- /dev/null +++ b/tests/configs/modules/calendar/diff_tz_start_end.js @@ -0,0 +1,38 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + dateEndFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + maximumNumberOfDays: 28, + showEnd: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/diff_tz_start_end.ics" + } + ] + } + } + ] +}; + +Date.now = () => { + return new Date("08 Oct 2024 12:30:00 GMT-07:00").valueOf(); +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js index 9310b3ffb4..b26b1d1cdc 100644 --- a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js +++ b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js @@ -13,6 +13,9 @@ let config = { maximumEntries: 100, sliceMultiDayEvents: true, dateFormat: "MMM Do, HH:mm", + timeFormat: "absolute", + getRelative: 0, + urgency: 0, calendars: [ { maximumEntries: 100, @@ -25,7 +28,7 @@ let config = { }; Date.now = () => { - return new Date("01 Sept 2024 10:38:00 GMT+2:00").valueOf(); + return new Date("01 Oct 2024 10:38:00 GMT+2:00").valueOf(); }; /*************** DO NOT EDIT THE LINE BELOW ***************/ diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 42d3cab529..5cc67059f2 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -174,8 +174,8 @@ describe("Calendar module", () => { describe("germany timezone", () => { it("Issue #unknown fullday timezone East of UTC edge", async () => { - await helpers.startApplication("tests/configs/modules/calendar/germany_at_end_of_day_repeating.js", "01 Sept 2024 10:38:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); - await expect(doTestTableContent(".calendar .event", ".time", "Oct 22nd, 23:00")).resolves.toBe(true); + await helpers.startApplication("tests/configs/modules/calendar/germany_at_end_of_day_repeating.js", "01 Oct 2024 10:38:00 GMT+02:00", ["js/electron.js"], "Europe/Berlin"); + await expect(doTestTableContent(".calendar .event", ".time", "Oct 22nd, 23:00", first)).resolves.toBe(true); }); }); @@ -250,6 +250,14 @@ describe("Calendar module", () => { }); }); + describe("one event", () => { + it("start/end in diff timezones", async () => { + await helpers.startApplication("tests/configs/modules/calendar/diff_tz_start_end.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Chicago"); + // just + await expect(doTestTableContent(".calendar .event", ".time", "29th.Oct, 05:00-30th.Oct, 18:00", first)).resolves.toBe(true); + }); + }); + }); //var output = stdMocks.flush() diff --git a/tests/mocks/diff_tz_start_end.ics b/tests/mocks/diff_tz_start_end.ics new file mode 100644 index 0000000000..b59ba41ed4 --- /dev/null +++ b/tests/mocks/diff_tz_start_end.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20241029T100000Z +DTEND:20241030T230000Z +DTSTAMP:20241022T203806Z +UID:04ivnntdi20rqsk0iesabsdhmj@google.com +CREATED:20241022T203738Z +LAST-MODIFIED:20241022T203738Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:start/end on diff tz +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR From e68a5f6ea25d12bb6fa2943417101c355e727125 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 23 Oct 2024 09:29:05 -0500 Subject: [PATCH 15/34] add support for end date display for fullday events, showEnd:true, dates not the same --- CHANGELOG.md | 2 ++ modules/default/calendar/calendar.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1028eb14a0..82645b3736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ _This release is scheduled to be released on 2025-01-01._ ### Added +- [calendar] - added ability to display end date for full date events, where end is not same day (showEnd=true) + ### Removed - [tests] Removed node-pty and drivelist from rebuilded test (#3575) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index e31bd3d9e4..035578ed29 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -434,6 +434,10 @@ Module.register("calendar", { //subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day event.endDate -= ONE_SECOND; timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); + if (this.config.showEnd && event.startDate !== event.endDate) { + timeWrapper.innerHTML += "-"; + timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + } } else if (this.config.getRelative > 0 && event.startDate < now) { // Ongoing and getRelative is set timeWrapper.innerHTML = CalendarUtils.capFirst( From 5265ba5f65c42917808e3e9e42972ad39bee9905 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 23 Oct 2024 12:45:17 -0300 Subject: [PATCH 16/34] add run date/time to testcase file as helper failed to do that --- ...lday_event_over_multiple_days_nonrepeating.ics | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics diff --git a/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics b/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics new file mode 100644 index 0000000000..8d506e5479 --- /dev/null +++ b/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics @@ -0,0 +1,15 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241025 +DTEND;VALUE=DATE:20241031 +DTSTAMP:20241023T141110Z +UID:60nobfcu0ct96jgsh5nhcia24b@google.com +CREATED:20241023T141019Z +DESCRIPTION:test for all day end viewing +LAST-MODIFIED:20241023T141019Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:simple all day event over many days (not repeating) +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR From 2763f7544700de4c5b4ed09734cb72527814ed80 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 23 Oct 2024 12:59:41 -0300 Subject: [PATCH 17/34] Revert "add run date/time to testcase file as helper failed to do that, wrong file" This reverts commit 5265ba5f65c42917808e3e9e42972ad39bee9905. --- ...lday_event_over_multiple_days_nonrepeating.ics | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics diff --git a/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics b/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics deleted file mode 100644 index 8d506e5479..0000000000 --- a/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN:VCALENDAR -BEGIN:VEVENT -DTSTART;VALUE=DATE:20241025 -DTEND;VALUE=DATE:20241031 -DTSTAMP:20241023T141110Z -UID:60nobfcu0ct96jgsh5nhcia24b@google.com -CREATED:20241023T141019Z -DESCRIPTION:test for all day end viewing -LAST-MODIFIED:20241023T141019Z -SEQUENCE:0 -STATUS:CONFIRMED -SUMMARY:simple all day event over many days (not repeating) -TRANSP:TRANSPARENT -END:VEVENT -END:VCALENDAR From d10855aad5c82969d851971d36c2f9a629b05577 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 23 Oct 2024 13:03:43 -0300 Subject: [PATCH 18/34] add run date/time to testcase js file as helper failed to do that --- .../configs/modules/calendar/show-duplicates-in-calendar.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/configs/modules/calendar/show-duplicates-in-calendar.js b/tests/configs/modules/calendar/show-duplicates-in-calendar.js index 7ad420944d..4858791615 100644 --- a/tests/configs/modules/calendar/show-duplicates-in-calendar.js +++ b/tests/configs/modules/calendar/show-duplicates-in-calendar.js @@ -27,6 +27,11 @@ let config = { ] }; +// adde here short term, was executed in the testcase runner helper, but failed?? +Date.now = () => { + return new Date("15 Sep 2024 12:30:00 GMT").valueOf(); +}; + /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; From a4fb29d80ea48083d78c3ae2e14a745f69c8c514 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 25 Oct 2024 16:09:48 -0500 Subject: [PATCH 19/34] fix testcases for showEndsOnlyWithDuration, add showEnd:true support for fullday non-repeating events --- CHANGELOG.md | 2 ++ modules/default/calendar/calendar.js | 16 +++++---- .../3_move_first_allday_repeating_event.js | 4 --- .../calendar/berlin_end_of_day_repeating.js | 5 +-- .../configs/modules/calendar/berlin_multi.js | 5 +-- ...n_whole_day_event_moved_over_dst_change.js | 4 --- .../calendar/chicago_late_in_timezone.js | 4 --- .../modules/calendar/diff_tz_start_end.js | 5 +-- .../calendar/end_of_day_berlin_moved.js | 5 +-- ...multiple_days_non_repeating_display_end.js | 34 +++++++++++++++++++ ...tiple_days_non_repeating_no_display_end.js | 32 +++++++++++++++++ .../exdate_and_recurrence_together.js | 5 +-- ...y_event_over_multiple_days_nonrepeating.js | 33 ++++++++++++++++++ .../germany_at_end_of_day_repeating.js | 4 --- .../calendar/show-duplicates-in-calendar.js | 4 --- tests/electron/modules/calendar_spec.js | 26 +++++++++++++- ..._time_over_multiple_days_non_repeating.ics | 14 ++++++++ ..._event_over_multiple_days_nonrepeating.ics | 15 ++++++++ 18 files changed, 169 insertions(+), 48 deletions(-) create mode 100644 tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js create mode 100644 tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js create mode 100644 tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js create mode 100644 tests/mocks/event_with_time_over_multiple_days_non_repeating.ics create mode 100644 tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics diff --git a/CHANGELOG.md b/CHANGELOG.md index 714940a3e0..75b34baf2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ _This release is scheduled to be released on 2025-01-01._ - [tests] fix testcases with hard coded Date.now (#3597) - [calendar] - update to resolve issues #3098 #3144 #3351 #3422 #3443 #3467 #3537 related to timezone changes - [calendar] - fixes #3267 (styles array), also fixes event with both exdate AND recurrence(and testcase) +- [calendar] - fix showEndsOnlyWithDuration not working, #3598 +- [calendar] - fix showEnd for Full Day events #3602 ## [2.29.0] - 2024-10-01 diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 035578ed29..8d341093b7 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -422,21 +422,23 @@ Module.register("calendar", { timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); // Add end time if showEnd if (this.config.showEnd) { - if (this.config.showEndsOnlyWithDuration && event.startDate === event.endDate) { - // no duration here, don't display end - } else { - timeWrapper.innerHTML += "-"; - timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + if (event.startDate !== event.endDate) { + // duration here, display end if requested + if(this.config.showEndsOnlyWithDuration){ + timeWrapper.innerHTML += "-"; + timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + } } } + // For full day events we use the fullDayEventDateFormat if (event.fullDayEvent) { //subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day event.endDate -= ONE_SECOND; timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); - if (this.config.showEnd && event.startDate !== event.endDate) { + if (this.config.showEnd && moment(event.startDate,"x").format("YYYYMMDD") !== moment(event.endDate,"x").format("YYYYMMDD")) { timeWrapper.innerHTML += "-"; - timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat)); } } else if (this.config.getRelative > 0 && event.startDate < now) { // Ongoing and getRelative is set diff --git a/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js b/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js index 8d1df3df0a..30afae2ccb 100644 --- a/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js +++ b/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js @@ -29,10 +29,6 @@ let config = { ] }; -Date.now = () => { - return new Date("01 Oct 2024 10:38:00 GMT+2:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/berlin_end_of_day_repeating.js b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js index 3332f7bcf6..a9da2fe812 100644 --- a/tests/configs/modules/calendar/berlin_end_of_day_repeating.js +++ b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js @@ -16,6 +16,7 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, @@ -27,10 +28,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/berlin_multi.js b/tests/configs/modules/calendar/berlin_multi.js index 8b7d953419..98614170bb 100644 --- a/tests/configs/modules/calendar/berlin_multi.js +++ b/tests/configs/modules/calendar/berlin_multi.js @@ -16,6 +16,7 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, @@ -27,10 +28,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js b/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js index 65c00cb973..255bbd950e 100644 --- a/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js +++ b/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js @@ -27,10 +27,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/chicago_late_in_timezone.js b/tests/configs/modules/calendar/chicago_late_in_timezone.js index 14c08e3e3e..3591b4cb79 100644 --- a/tests/configs/modules/calendar/chicago_late_in_timezone.js +++ b/tests/configs/modules/calendar/chicago_late_in_timezone.js @@ -27,10 +27,6 @@ let config = { ] }; -Date.now = () => { - return new Date("01 Sept 2024 10:38:00 GMT-05:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/diff_tz_start_end.js b/tests/configs/modules/calendar/diff_tz_start_end.js index cbd658cf48..bd3db82a7f 100644 --- a/tests/configs/modules/calendar/diff_tz_start_end.js +++ b/tests/configs/modules/calendar/diff_tz_start_end.js @@ -17,6 +17,7 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, @@ -28,10 +29,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT-07:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/end_of_day_berlin_moved.js b/tests/configs/modules/calendar/end_of_day_berlin_moved.js index 3332f7bcf6..a9da2fe812 100644 --- a/tests/configs/modules/calendar/end_of_day_berlin_moved.js +++ b/tests/configs/modules/calendar/end_of_day_berlin_moved.js @@ -16,6 +16,7 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, @@ -27,10 +28,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT+02:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js new file mode 100644 index 0000000000..ef60df4c94 --- /dev/null +++ b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js @@ -0,0 +1,34 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + dateEndFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + showEnd: true, + showEndsOnlyWithDuration: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics" + } + ] + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js new file mode 100644 index 0000000000..ec11bf978e --- /dev/null +++ b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js @@ -0,0 +1,32 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + dateEndFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics" + } + ] + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/exdate_and_recurrence_together.js b/tests/configs/modules/calendar/exdate_and_recurrence_together.js index 8e5790de79..221265aa9f 100644 --- a/tests/configs/modules/calendar/exdate_and_recurrence_together.js +++ b/tests/configs/modules/calendar/exdate_and_recurrence_together.js @@ -16,6 +16,7 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, @@ -27,10 +28,6 @@ let config = { ] }; -Date.now = () => { - return new Date("08 Oct 2024 12:30:00 GMT+07:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js b/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js new file mode 100644 index 0000000000..486a5db987 --- /dev/null +++ b/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js @@ -0,0 +1,33 @@ +let config = { + address: "0.0.0.0", + ipWhitelist: [], + + timeFormat: 24, + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + fade: false, + urgency: 0, + dateFormat: "Do.MMM, HH:mm", + fullDayEventDateFormat: "Do.MMM", + timeFormat: "absolute", + getRelative: 0, + showEnd: true, + showEndsOnlyWithDuration: true, + calendars: [ + { + maximumEntries: 100, + url: "http://localhost:8080/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics" + } + ] + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js index b26b1d1cdc..e9d7e88337 100644 --- a/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js +++ b/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js @@ -27,10 +27,6 @@ let config = { ] }; -Date.now = () => { - return new Date("01 Oct 2024 10:38:00 GMT+2:00").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/configs/modules/calendar/show-duplicates-in-calendar.js b/tests/configs/modules/calendar/show-duplicates-in-calendar.js index 4858791615..b5268d52ae 100644 --- a/tests/configs/modules/calendar/show-duplicates-in-calendar.js +++ b/tests/configs/modules/calendar/show-duplicates-in-calendar.js @@ -28,10 +28,6 @@ let config = { }; // adde here short term, was executed in the testcase runner helper, but failed?? -Date.now = () => { - return new Date("15 Sep 2024 12:30:00 GMT").valueOf(); -}; - /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 5cc67059f2..21adffc4b4 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -250,7 +250,7 @@ describe("Calendar module", () => { }); }); - describe("one event", () => { + describe("one event diff tz", () => { it("start/end in diff timezones", async () => { await helpers.startApplication("tests/configs/modules/calendar/diff_tz_start_end.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Chicago"); // just @@ -258,6 +258,30 @@ describe("Calendar module", () => { }); }); + describe("one event non repeating", () => { + it("fullday non-repeating", async () => { + await helpers.startApplication("tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Chicago"); + // just + await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct-30th.Oct", first)).resolves.toBe(true); + }); + }); + + describe("one event no end display", () => { + it("don't display end", async () => { + await helpers.startApplication("tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Chicago"); + // just + await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct, 20:00", first)).resolves.toBe(true); + }); + }); + + describe("display end display end", () => { + it("display end", async () => { + await helpers.startApplication("tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js", "08 Oct 2024 12:30:00 GMT-07:00", ["js/electron.js"], "America/Chicago"); + // just + await expect(doTestTableContent(".calendar .event", ".time", "25th.Oct, 20:00-26th.Oct, 06:00", first)).resolves.toBe(true); + }); + }); + }); //var output = stdMocks.flush() diff --git a/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics b/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics new file mode 100644 index 0000000000..a12d58dd1c --- /dev/null +++ b/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20241026T010000Z +DTEND:20241026T110000Z +DTSTAMP:20241024T153358Z +UID:4maud6s79m41a99pj2g7j5km0a@google.com +CREATED:20241024T153313Z +LAST-MODIFIED:20241024T153330Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Sleep over at Bobs +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics b/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics new file mode 100644 index 0000000000..8d506e5479 --- /dev/null +++ b/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics @@ -0,0 +1,15 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;VALUE=DATE:20241025 +DTEND;VALUE=DATE:20241031 +DTSTAMP:20241023T141110Z +UID:60nobfcu0ct96jgsh5nhcia24b@google.com +CREATED:20241023T141019Z +DESCRIPTION:test for all day end viewing +LAST-MODIFIED:20241023T141019Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:simple all day event over many days (not repeating) +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR From 094c19979371143c6434b7aa645e3f51db7848c5 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Sat, 26 Oct 2024 09:08:03 -0500 Subject: [PATCH 20/34] fix showEndsOnlyWithDuration, only applies to full day events --- CHANGELOG.md | 2 +- modules/default/calendar/calendar.js | 7 +++---- .../modules/calendar/berlin_end_of_day_repeating.js | 1 - tests/configs/modules/calendar/berlin_multi.js | 1 - tests/configs/modules/calendar/diff_tz_start_end.js | 1 - tests/configs/modules/calendar/end_of_day_berlin_moved.js | 1 - ...th_time_over_multiple_days_non_repeating_display_end.js | 1 - ...time_over_multiple_days_non_repeating_no_display_end.js | 2 ++ .../modules/calendar/exdate_and_recurrence_together.js | 1 - .../fullday_event_over_multiple_days_nonrepeating.js | 1 - 10 files changed, 6 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b34baf2f..b99c5d6954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ _This release is scheduled to be released on 2025-01-01._ - [tests] fix testcases with hard coded Date.now (#3597) - [calendar] - update to resolve issues #3098 #3144 #3351 #3422 #3443 #3467 #3537 related to timezone changes - [calendar] - fixes #3267 (styles array), also fixes event with both exdate AND recurrence(and testcase) -- [calendar] - fix showEndsOnlyWithDuration not working, #3598 +- [calendar] - fix showEndsOnlyWithDuration not working, #3598, applies ONLY to full day events - [calendar] - fix showEnd for Full Day events #3602 ## [2.29.0] - 2024-10-01 diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 8d341093b7..af4c838710 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -422,12 +422,10 @@ Module.register("calendar", { timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); // Add end time if showEnd if (this.config.showEnd) { + // andhas a duation if (event.startDate !== event.endDate) { - // duration here, display end if requested - if(this.config.showEndsOnlyWithDuration){ timeWrapper.innerHTML += "-"; timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); - } } } @@ -436,7 +434,8 @@ Module.register("calendar", { //subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day event.endDate -= ONE_SECOND; timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); - if (this.config.showEnd && moment(event.startDate,"x").format("YYYYMMDD") !== moment(event.endDate,"x").format("YYYYMMDD")) { + // only show end if requested and allowed and the dates are different + if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && moment(event.startDate,"x").format("YYYYMMDD") !== moment(event.endDate,"x").format("YYYYMMDD")) { timeWrapper.innerHTML += "-"; timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat)); } diff --git a/tests/configs/modules/calendar/berlin_end_of_day_repeating.js b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js index a9da2fe812..b706347897 100644 --- a/tests/configs/modules/calendar/berlin_end_of_day_repeating.js +++ b/tests/configs/modules/calendar/berlin_end_of_day_repeating.js @@ -16,7 +16,6 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/berlin_multi.js b/tests/configs/modules/calendar/berlin_multi.js index 98614170bb..50069371ba 100644 --- a/tests/configs/modules/calendar/berlin_multi.js +++ b/tests/configs/modules/calendar/berlin_multi.js @@ -16,7 +16,6 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/diff_tz_start_end.js b/tests/configs/modules/calendar/diff_tz_start_end.js index bd3db82a7f..829ef3a0ad 100644 --- a/tests/configs/modules/calendar/diff_tz_start_end.js +++ b/tests/configs/modules/calendar/diff_tz_start_end.js @@ -17,7 +17,6 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/end_of_day_berlin_moved.js b/tests/configs/modules/calendar/end_of_day_berlin_moved.js index a9da2fe812..b706347897 100644 --- a/tests/configs/modules/calendar/end_of_day_berlin_moved.js +++ b/tests/configs/modules/calendar/end_of_day_berlin_moved.js @@ -16,7 +16,6 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js index ef60df4c94..95989648ca 100644 --- a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js +++ b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js @@ -16,7 +16,6 @@ let config = { timeFormat: "absolute", getRelative: 0, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js index ec11bf978e..ef60df4c94 100644 --- a/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js +++ b/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js @@ -15,6 +15,8 @@ let config = { fullDayEventDateFormat: "Do.MMM", timeFormat: "absolute", getRelative: 0, + showEnd: true, + showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/exdate_and_recurrence_together.js b/tests/configs/modules/calendar/exdate_and_recurrence_together.js index 221265aa9f..b1dc06389b 100644 --- a/tests/configs/modules/calendar/exdate_and_recurrence_together.js +++ b/tests/configs/modules/calendar/exdate_and_recurrence_together.js @@ -16,7 +16,6 @@ let config = { getRelative: 0, maximumNumberOfDays: 28, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, diff --git a/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js b/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js index 486a5db987..7ec5885cb7 100644 --- a/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js +++ b/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js @@ -15,7 +15,6 @@ let config = { timeFormat: "absolute", getRelative: 0, showEnd: true, - showEndsOnlyWithDuration: true, calendars: [ { maximumEntries: 100, From 9ac5ff2d1b9083cfa47b47dd85cf2bc9a0e43758 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 4 Nov 2024 09:54:16 -0600 Subject: [PATCH 21/34] upgrade node-ical to latest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cb5e3c458e..80596246f4 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "iconv-lite": "^0.6.3", "module-alias": "^2.2.3", "moment": "^2.30.1", - "node-ical": "github:jens-maus/node-ical#master", + "node-ical": "^0.20.1", "pm2": "^5.4.2", "socket.io": "^4.8.1", "suncalc": "^1.9.0", From d973ed9a7088c473d6f7af21c7244a46e00cbf16 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 13 Nov 2024 08:45:07 -0600 Subject: [PATCH 22/34] fix lint issues --- modules/default/calendar/calendar.js | 14 +- modules/default/calendar/calendarfetcher.js | 2 +- .../default/calendar/calendarfetcherutils.js | 307 +++++++++--------- package-lock.json | 11 +- 4 files changed, 167 insertions(+), 167 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index af4c838710..35769f4c6e 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -168,7 +168,7 @@ Module.register("calendar", { this.selfUpdate(); }, - notificationReceived(notification,payload,sender){ + notificationReceived (notification, payload, sender) { if (notification === "FETCH_CALENDAR") { if (this.hasCalendarURL(payload.url)) { @@ -424,8 +424,8 @@ Module.register("calendar", { if (this.config.showEnd) { // andhas a duation if (event.startDate !== event.endDate) { - timeWrapper.innerHTML += "-"; - timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + timeWrapper.innerHTML += "-"; + timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); } } @@ -435,7 +435,7 @@ Module.register("calendar", { event.endDate -= ONE_SECOND; timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); // only show end if requested and allowed and the dates are different - if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && moment(event.startDate,"x").format("YYYYMMDD") !== moment(event.endDate,"x").format("YYYYMMDD")) { + if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && moment(event.startDate, "x").format("YYYYMMDD") !== moment(event.endDate, "x").format("YYYYMMDD")) { timeWrapper.innerHTML += "-"; timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat)); } @@ -896,10 +896,8 @@ Module.register("calendar", { let p = this.getCalendarProperty(url, property, defaultValue); if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") { const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName); - if(p instanceof Array) - p.push(className) - else - p = className + p; + if (p instanceof Array) p.push(className); + else p = className + p; } if (!(p instanceof Array)) p = [p]; diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 82ef79c1cb..2a56ff5b4d 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -56,7 +56,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn try { data = ical.parseICS(responseData); - Log.debug(`parsed data=${JSON.stringify(data,null,2)}`); + Log.debug(`parsed data=${JSON.stringify(data, null, 2)}`); events = CalendarFetcherUtils.filterEvents(data, { excludedEvents, includePastEvents, diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 9ecbb73181..0bb11a3b81 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -160,7 +160,7 @@ const CalendarFetcherUtils = { } if (event.type === "VEVENT") { - Log.debug(`Event:\n${JSON.stringify(event,null,2)}`); + Log.debug(`Event:\n${JSON.stringify(event, null, 2)}`); let startMoment = eventDate(event, "start"); let endMoment; @@ -287,21 +287,21 @@ const CalendarFetcherUtils = { futureLocal = futureMoment.toDate(); // future } const oneDayInMs = 24 * 60 * 60 * 1000; - d1=new Date(new Date(pastLocal.valueOf() - oneDayInMs).getTime()) - d2=new Date(new Date(futureLocal.valueOf() + oneDayInMs).getTime()) + d1 = new Date(new Date(pastLocal.valueOf() - oneDayInMs).getTime()); + d2 = new Date(new Date(futureLocal.valueOf() + oneDayInMs).getTime()); Log.debug(`Search for recurring events between: ${d1} and ${d2}`); - event.start=rule.options.dtstart + event.start = rule.options.dtstart; - Log.debug("fix rrule start=",rule.options.dtstart) - Log.debug("event before rrule.between=",JSON.stringify(event,null,2), "exdates=", event.exdate) + Log.debug("fix rrule start=", rule.options.dtstart); + Log.debug("event before rrule.between=", JSON.stringify(event, null, 2), "exdates=", event.exdate); // fixup the exdate and recurrence date to local time too for post between() handling - CalendarFetcherUtils.fixEventtoLocal(event) + CalendarFetcherUtils.fixEventtoLocal(event); Log.debug(`RRule: ${rule.toString()}`); rule.options.tzid = null; // RRule gets *very* confused with timezones - let dates = rule.between(d1,d2, true, () => { return true; }); + let dates = rule.between(d1, d2, true, () => { return true; }); Log.debug(`Title: ${event.summary}, with dates: \n\n${JSON.stringify(dates)}\n`); @@ -311,27 +311,26 @@ const CalendarFetcherUtils = { else return true; }); - // go thru all the rrule.between() dates and put back the tz offset removed so between would work - let datesLocal=[] - let offset = d1.getTimezoneOffset() - Log.debug("offset =",offset) - dates.forEach(d =>{ - let dtext=d.toISOString().slice(0,-5) - Log.debug(" date text form without tz=",dtext) - let dLocal= new Date(d.valueOf()+(offset*60000)) - let offset2=dLocal.getTimezoneOffset() - Log.debug("date after offset applied=", dLocal) - if(offset!=offset2){ + // go thru all the rrule.between() dates and put back the tz offset removed so rrule.between would work + let datesLocal = []; + let offset = d1.getTimezoneOffset(); + Log.debug("offset =", offset); + dates.forEach((d) => { + let dtext = d.toISOString().slice(0, -5); + Log.debug(" date text form without tz=", dtext); + let dLocal = new Date(d.valueOf() + (offset * 60000)); + let offset2 = dLocal.getTimezoneOffset(); + Log.debug("date after offset applied=", dLocal); + if (offset !== offset2) { // woops, dst/std switch - let delta = offset-offset2 - Log.debug("offset delta=", delta) - dLocal= new Date(d.valueOf()+((offset-delta)*60000)) - Log.debug("corrected normalized date=",dLocal) - } else - Log.debug(" neutralized date=",dLocal); - datesLocal.push(dLocal) - }) - dates=datesLocal + let delta = offset - offset2; + Log.debug("offset delta=", delta); + dLocal = new Date(d.valueOf() + ((offset - delta) * 60000)); + Log.debug("corrected normalized date=", dLocal); + } else Log.debug(" neutralized date=", dLocal); + datesLocal.push(dLocal); + }); + dates = datesLocal; // The "dates" array contains the set of dates within our desired date range range that are valid @@ -344,14 +343,14 @@ const CalendarFetcherUtils = { // // i don't think we will ever see this anymore (oct 2024) due to code fixes for rrule.between() // - Log.debug("event.recurrences:",event.recurrences); + Log.debug("event.recurrences:", event.recurrences); if (event.recurrences !== undefined) { for (let dateKey in event.recurrences) { // Only add dates that weren't already in the range we added from the rrule so that // we don't double-add those events. let d = new Date(dateKey); if (!moment(d).isBetween(d1, d2)) { - Log.debug("adding recurring event not found in between list =", d, " should not happen now using local dates oct 17,24") + Log.debug("adding recurring event not found in between list =", d, " should not happen now using local dates oct 17,24"); dates.push(d); } } @@ -364,39 +363,39 @@ const CalendarFetcherUtils = { let curDurationMs = durationMs; let showRecurrence = true; - let startMoment = moment(date) + let startMoment = moment(date); - let dateKey = CalendarFetcherUtils.getDateKeyFromDate(date) + let dateKey = CalendarFetcherUtils.getDateKeyFromDate(date); - Log.debug("event date dateKey=",dateKey) + Log.debug("event date dateKey=", dateKey); // For each date that we're checking, it's possible that there is a recurrence override for that one day. - if (curEvent.recurrences !== undefined){ - Log.debug("have recurrences=",curEvent.recurrences) - if(curEvent.recurrences[dateKey] !== undefined) { - Log.debug("have a recurrence match for dateKey=",dateKey) + if (curEvent.recurrences !== undefined) { + Log.debug("have recurrences=", curEvent.recurrences); + if (curEvent.recurrences[dateKey] !== undefined) { + Log.debug("have a recurrence match for dateKey=", dateKey); // We found an override, so for this recurrence, use a potentially different title, start date, and duration. curEvent = curEvent.recurrences[dateKey]; - curEvent.start= new Date(new Date(curEvent.start.valueOf()).getTime()) - curEvent.end= new Date(new Date(curEvent.end.valueOf()).getTime()) - startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event) - endMoment= CalendarFetcherUtils.getAdjustedStartMoment(curEvent.end, event) - date=curEvent.start + curEvent.start = new Date(new Date(curEvent.start.valueOf()).getTime()); + curEvent.end = new Date(new Date(curEvent.end.valueOf()).getTime()); + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.start, event); + endMoment = CalendarFetcherUtils.getAdjustedStartMoment(curEvent.end, event); + date = curEvent.start; curDurationMs = new Date(endMoment).valueOf() - startMoment.valueOf(); } else { - Log.debug("recurrence key ", dateKey, " doesn't match") + Log.debug("recurrence key ", dateKey, " doesn't match"); } } // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - if (curEvent.exdate !== undefined){ - Log.debug("have datekey=", dateKey, " exdates=", curEvent.exdate) - if(curEvent.exdate[dateKey] !== undefined) { + if (curEvent.exdate !== undefined) { + Log.debug("have datekey=", dateKey, " exdates=", curEvent.exdate); + if (curEvent.exdate[dateKey] !== undefined) { // This date is an exception date, which means we should skip it in the recurrence pattern. showRecurrence = false; } } Log.debug(`duration: ${curDurationMs}`); - startMoment = CalendarFetcherUtils.getAdjustedStartMoment(date, event) + startMoment = CalendarFetcherUtils.getAdjustedStartMoment(date, event); endMoment = moment(startMoment.valueOf() + curDurationMs); @@ -431,9 +430,9 @@ const CalendarFetcherUtils = { description: description }); } else { - Log.debug("not saving event ", recurrenceTitle, new Date(startMoment)) + Log.debug("not saving event ", recurrenceTitle, new Date(startMoment)); } - Log.debug(" ") + Log.debug(" "); } // End recurring event parsing. } else { @@ -501,43 +500,45 @@ const CalendarFetcherUtils = { return newEvents; }, + /** * fixup thew event fields that have dates to use local time * BEFORE calling rrule.between * @param the event being processed * @returns nothing */ - fixEventtoLocal(event){ + fixEventtoLocal (event) { // if there are excluded dates, their date is incorrect and possibly key as well. - if(event.exdate != undefined){ - Object.keys(event.exdate).forEach(dateKey=>{ + if (event.exdate !== undefined) { + Object.keys(event.exdate).forEach((dateKey) => { // get the date - let exdate=event.exdate[dateKey] - Log.debug("exdate w key=", exdate) + let exdate = event.exdate[dateKey]; + Log.debug("exdate w key=", exdate); //exdate=CalendarFetcherUtils.convertDateToLocalTime(exdate, event.end.tz) - exdate=new Date(new Date(exdate.valueOf()-((120*60*1000))).getTime()) - Log.debug("new exDate item=",exdate," with old key=", dateKey) - let newkey=exdate.toISOString().slice(0,10) - if(newkey!=dateKey){ - Log.debug("new exDate item=",exdate," key="+newkey) - event.exdate[newkey]=exdate + exdate = new Date(new Date(exdate.valueOf() - ((120 * 60 * 1000))).getTime()); + Log.debug("new exDate item=", exdate, " with old key=", dateKey); + let newkey = exdate.toISOString().slice(0, 10); + if (newkey !== dateKey) { + Log.debug("new exDate item=", exdate, ` key=${newkey}`); + event.exdate[newkey] = exdate; //delete event.exdate[dateKey] } - }) - Log.debug("updated exdate list=", event.exdate) + }); + Log.debug("updated exdate list=", event.exdate); } - if(event.recurrences){ - Object.keys(event.recurrences).forEach(dateKey=>{ - let exdate=event.recurrences[dateKey] + if (event.recurrences) { + Object.keys(event.recurrences).forEach((dateKey) => { + let exdate = event.recurrences[dateKey]; //exdate=new Date(new Date(exdate.valueOf()-(60*60*1000)).getTime()) - Log.debug("new recurrence item=",exdate," with old key=", dateKey) - exdate.start=CalendarFetcherUtils.convertDateToLocalTime(exdate.start, exdate.start.tz) - exdate.end=CalendarFetcherUtils.convertDateToLocalTime(exdate.end, exdate.end.tz) - Log.debug("adjusted recurringEvent start=", exdate.start, " end=",exdate.end) - }) + Log.debug("new recurrence item=", exdate, " with old key=", dateKey); + exdate.start = CalendarFetcherUtils.convertDateToLocalTime(exdate.start, exdate.start.tz); + exdate.end = CalendarFetcherUtils.convertDateToLocalTime(exdate.end, exdate.end.tz); + Log.debug("adjusted recurringEvent start=", exdate.start, " end=", exdate.end); + }); } - Log.debug("modified recurrences before rrule.between", event.recurrences) + Log.debug("modified recurrences before rrule.between", event.recurrences); }, + /** * convert a UTC date to local time * BEFORE calling rrule.between @@ -545,83 +546,82 @@ const CalendarFetcherUtils = { * tz event is currently in * @returns updated date object */ - convertDateToLocalTime(date,tz){ - let delta_tz_offset=0 - let now_offset=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) - let event_offset=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(tz) - Log.debug("date to convert=", date) - if(Math.sign(now_offset)!=Math.sign(event_offset)){ - delta_tz_offset=Math.abs(now_offset)+Math.abs(event_offset) + convertDateToLocalTime (date, tz) { + let delta_tz_offset = 0; + let now_offset = CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()); + let event_offset = CalendarFetcherUtils.getTimezoneOffsetFromTimezone(tz); + Log.debug("date to convert=", date); + if (Math.sign(now_offset) !== Math.sign(event_offset)) { + delta_tz_offset = Math.abs(now_offset) + Math.abs(event_offset); } else { // signs are the same // if negative - if(Math.sign(now_offset)==-1){ + if (Math.sign(now_offset) === -1) { // la looking at chicago - if(now_offset date.toISOString().slice(8,10)){ - startday=date.toISOString().slice(8,10) - Log.debug("> ", startday) + if (date.toString().slice(8, 10) < date.toISOString().slice(8, 10)) { + startday = date.toString().slice(8, 10); + Log.debug("< ", startday); + } else { // tostring is more + if (date.toString().slice(8, 10) > date.toISOString().slice(8, 10)) { + startday = date.toISOString().slice(8, 10); + Log.debug("> ", startday); } } - return dateKey = date.toISOString().substring(0, 8)+("0"+startday).slice(-2); + return date.toISOString().substring(0, 8) + (`0${startday}`).slice(-2); }, + /** * get the timezone offset from the timezone string * * @param the timezone string * @returns the numerical offset */ - getTimezoneOffsetFromTimezone(timeZone){ - const str = new Date().toLocaleString('en', {timeZone, timeZoneName: 'longOffset'}); - Log.debug("tz offset=",str) - const [_,h,m] = str.match(/([+-]\d+):(\d+)$/) || [, '+00', '00']; + getTimezoneOffsetFromTimezone (timeZone) { + const str = new Date().toLocaleString("en", { timeZone, timeZoneName: "longOffset" }); + Log.debug("tz offset=", str); + const [_, h, m] = str.match(/([+-]\d+):(\d+)$/) || ["+00", "00"]; return h * 60 + (h > 0 ? +m : -m); }, + /** * fixup the date start moment after rrule.between returns date array * @@ -629,69 +629,72 @@ const CalendarFetcherUtils = { * the event object it came from * @returns moment object */ - getAdjustedStartMoment(date, event){ + getAdjustedStartMoment (date, event) { - let startMoment = moment(date) + let startMoment = moment(date); - Log.debug("startMoment pre=", startMoment) + Log.debug("startMoment pre=", startMoment); // get our runtime timezone offset - const nowDiff=CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()) // 10/18 16:49, 300 - let eventDiff= CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz) // watch out, start tz is cleared to handle rrule 120 23:49 + const nowDiff = CalendarFetcherUtils.getTimezoneOffsetFromTimezone(moment.tz.guess()); // 10/18 16:49, 300 + let eventDiff = CalendarFetcherUtils.getTimezoneOffsetFromTimezone(event.end.tz); // watch out, start tz is cleared to handle rrule 120 23:49 - Log.debug("tz diff event=",eventDiff, " local=",nowDiff," end event timezone=", event.end.tz) + Log.debug("tz diff event=", eventDiff, " local=", nowDiff, " end event timezone=", event.end.tz); // if the diffs are different (not same tz for processing as event) - if (nowDiff!=eventDiff){ + if (nowDiff !== eventDiff) { // if signs are different - if(Math.sign(nowDiff)!= Math.sign(eventDiff)){ + if (Math.sign(nowDiff) !== Math.sign(eventDiff)) { // its the accumulated total - Log.debug("diff signs, accumulate") - eventDiff=Math.abs(eventDiff)+Math.abs(nowDiff) + Log.debug("diff signs, accumulate"); + eventDiff = Math.abs(eventDiff) + Math.abs(nowDiff); // sign of diff depends on where you are looking at which event. // australia looking at US, add to get same time - Log.debug("new different event diff=",eventDiff) - if(Math.sign(nowDiff)==-1){ - eventDiff*=-1 + Log.debug("new different event diff=", eventDiff); + if (Math.sign(nowDiff) === -1) { + eventDiff *= -1; // US looking at australia event have to subtract - Log.debug("new diff, same sign, total event diff=",eventDiff) + Log.debug("new diff, same sign, total event diff=", eventDiff); } } - else{ + else { // signs are the same, all east of UTC or all west of UTC // if the signs are negative (west of UTC) - Log.debug("signs are the same") - if(Math.sign(eventDiff)==-1){ + Log.debug("signs are the same"); + if (Math.sign(eventDiff) === -1) { //if west, looking at more west - if(nowDiff Date: Wed, 13 Nov 2024 11:18:18 -0600 Subject: [PATCH 23/34] force fail on e2e calendar tests to see content, determine what failed --- tests/e2e/modules/calendar_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index e88c003ad1..9ee48cfd0f 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -53,7 +53,7 @@ describe("Calendar module", () => { }); it("should show the custom maximumEntries of 5", async () => { - await expect(testElementLength(".calendar .event", 5)).resolves.toBe(true); + await expect(testElementLength(".calendar .event", 6)).resolves.toBe(true); }); it("should show the custom calendar symbol in four events", async () => { From 2956f3abf4325f5e63540afdfb9e5f45d403a7b5 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 13 Nov 2024 13:41:13 -0600 Subject: [PATCH 24/34] fix multi-cal and maxEntries by cal sorting and slicing --- modules/default/calendar/calendar.js | 34 ++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 35769f4c6e..a05fd4a67a 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -613,6 +613,7 @@ Module.register("calendar", { const calendar = this.calendarData[calendarUrl]; let remainingEntries = this.maximumEntriesForUrl(calendarUrl); let maxPastDaysCompare = now - this.maximumPastDaysForUrl(calendarUrl) * ONE_DAY; + let by_url_calevents = []; for (const e in calendar) { const event = JSON.parse(JSON.stringify(calendar[e])); // clone object @@ -630,9 +631,9 @@ Module.register("calendar", { if (this.config.hideDuplicates && this.listContainsEvent(events, event)) { continue; } - if (--remainingEntries < 0) { - break; - } + //if (--remainingEntries < 0) { + // break; + //} } event.url = calendarUrl; @@ -677,15 +678,21 @@ Module.register("calendar", { for (let splitEvent of splitEvents) { if (splitEvent.endDate > now && splitEvent.endDate <= future) { - events.push(splitEvent); + by_url_calevents = by_url_calevents.concat(splitEvent); } } } else { - events.push(event); + by_url_calevents.push(event); } } + by_url_calevents.sort(function (a, b) { + return a.startDate - b.startDate; + }); + Log.info(`pushing ${by_url_calevents.length} events to total with room for ${remainingEntries}`); + events = events.concat(by_url_calevents.slice(0, remainingEntries)); + Log.info(`events for calendar=${events.length}`); } - + Log.info(`sorting events count=${events.length}`); events.sort(function (a, b) { return a.startDate - b.startDate; }); @@ -725,10 +732,18 @@ Module.register("calendar", { } events = newEvents; } - + Log.info(`slicing events total maxcount=${this.config.maximumEntries}`); return events.slice(0, this.config.maximumEntries); }, + getMinNumberOfEventsforAll (events, max) { + let min = max; + events.forEach((event) => { + const urlmax = this.maximumEntriesForUrl(event.url); + min = Math.min(min, urlmax); + }); + return min; + }, listContainsEvent (eventList, event) { for (const evt of eventList) { if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate) && parseInt(evt.endDate) === parseInt(event.endDate)) { @@ -896,8 +911,9 @@ Module.register("calendar", { let p = this.getCalendarProperty(url, property, defaultValue); if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") { const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName); - if (p instanceof Array) p.push(className); - else p = className + p; + //if (p instanceof Array) p.push(className); + //else + p = className + p; } if (!(p instanceof Array)) p = [p]; From 768080c0e08692636ca0094da6c81d96b7130e58 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 13 Nov 2024 14:47:28 -0600 Subject: [PATCH 25/34] display raw time in events --- modules/default/calendar/calendar.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index a05fd4a67a..10c1da992d 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -25,7 +25,7 @@ Module.register("calendar", { fadePoint: 0.25, // Start on 1/4th of the list. urgency: 7, timeFormat: "relative", - dateFormat: "MMM Do", + dateFormat: "MMM Do MM:hh", dateEndFormat: "LT", fullDayEventDateFormat: "MMM Do", showEnd: false, @@ -470,16 +470,18 @@ Module.register("calendar", { if (event.startDate >= now || (event.fullDayEvent && this.eventEndingWithinNextFullTimeUnit(event, ONE_DAY))) { // Use relative time if (!this.config.hideTime && !event.fullDayEvent) { - timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat })); + Log.info("event not hidden and not fullday"); + timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))} A ${event.startDate}`; } else { - timeWrapper.innerHTML = CalendarUtils.capFirst( + Log.info("event full day or hidden"); + timeWrapper.innerText = `${CalendarUtils.capFirst( moment(event.startDate, "x").calendar(null, { sameDay: this.config.showTimeToday ? "LT" : `[${this.translate("TODAY")}]`, nextDay: `[${this.translate("TOMORROW")}]`, nextWeek: "dddd", sameElse: event.fullDayEvent ? this.config.fullDayEventDateFormat : this.config.dateFormat }) - ); + )} B ${event.startDate}`; } if (event.fullDayEvent) { // Full days events within the next two days @@ -498,9 +500,11 @@ Module.register("calendar", { timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYAFTERTOMORROW")); } } + Log.info("event fullday"); } else if (event.startDate - now < this.config.getRelative * ONE_HOUR) { + Log.info("not full day but within getrelative size"); // If event is within getRelative hours, display 'in xxx' time format or moment.fromNow() - timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").fromNow()); + timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())} C ${event.startDate}`; } } else { // Ongoing event @@ -678,7 +682,7 @@ Module.register("calendar", { for (let splitEvent of splitEvents) { if (splitEvent.endDate > now && splitEvent.endDate <= future) { - by_url_calevents = by_url_calevents.concat(splitEvent); + by_url_calevents.push(splitEvent); } } } else { @@ -736,14 +740,6 @@ Module.register("calendar", { return events.slice(0, this.config.maximumEntries); }, - getMinNumberOfEventsforAll (events, max) { - let min = max; - events.forEach((event) => { - const urlmax = this.maximumEntriesForUrl(event.url); - min = Math.min(min, urlmax); - }); - return min; - }, listContainsEvent (eventList, event) { for (const evt of eventList) { if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate) && parseInt(evt.endDate) === parseInt(event.endDate)) { From ff80356fa97b9d8eaf232eff52efd4512a721b77 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 14 Nov 2024 13:53:17 -0600 Subject: [PATCH 26/34] cleanup debug and limits per calendar --- modules/default/calendar/calendar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 10c1da992d..ab5dc94b4e 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -471,17 +471,17 @@ Module.register("calendar", { // Use relative time if (!this.config.hideTime && !event.fullDayEvent) { Log.info("event not hidden and not fullday"); - timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))} A ${event.startDate}`; + timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))}`; // A ${event.startDate}; } else { Log.info("event full day or hidden"); - timeWrapper.innerText = `${CalendarUtils.capFirst( + timeWrapper.innerHTML = `${CalendarUtils.capFirst( moment(event.startDate, "x").calendar(null, { sameDay: this.config.showTimeToday ? "LT" : `[${this.translate("TODAY")}]`, nextDay: `[${this.translate("TOMORROW")}]`, nextWeek: "dddd", sameElse: event.fullDayEvent ? this.config.fullDayEventDateFormat : this.config.dateFormat }) - )} B ${event.startDate}`; + )}`; // B ${event.startDate}; } if (event.fullDayEvent) { // Full days events within the next two days @@ -504,7 +504,7 @@ Module.register("calendar", { } else if (event.startDate - now < this.config.getRelative * ONE_HOUR) { Log.info("not full day but within getrelative size"); // If event is within getRelative hours, display 'in xxx' time format or moment.fromNow() - timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())} C ${event.startDate}`; + timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`; // C ${event.startDate}; } } else { // Ongoing event From 916bb3768965408711cf4dd1b2528187acf342b3 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 14 Nov 2024 13:59:20 -0600 Subject: [PATCH 27/34] fix one innerText add corrected e2e testcase --- modules/default/calendar/calendar.js | 2 +- tests/e2e/modules/calendar_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index ab5dc94b4e..48b4f2c96c 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -504,7 +504,7 @@ Module.register("calendar", { } else if (event.startDate - now < this.config.getRelative * ONE_HOUR) { Log.info("not full day but within getrelative size"); // If event is within getRelative hours, display 'in xxx' time format or moment.fromNow() - timeWrapper.innerText = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`; // C ${event.startDate}; + timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`; // C ${event.startDate}; } } else { // Ongoing event diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 9ee48cfd0f..e88c003ad1 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -53,7 +53,7 @@ describe("Calendar module", () => { }); it("should show the custom maximumEntries of 5", async () => { - await expect(testElementLength(".calendar .event", 6)).resolves.toBe(true); + await expect(testElementLength(".calendar .event", 5)).resolves.toBe(true); }); it("should show the custom calendar symbol in four events", async () => { From 9be553507b9e4143cb88d47fd0c67a741f849fb8 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 15 Nov 2024 15:05:49 +0000 Subject: [PATCH 28/34] fix lint error correction done wrong --- modules/default/calendar/calendarfetcherutils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 0bb11a3b81..b89f5962c2 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -618,7 +618,7 @@ const CalendarFetcherUtils = { getTimezoneOffsetFromTimezone (timeZone) { const str = new Date().toLocaleString("en", { timeZone, timeZoneName: "longOffset" }); Log.debug("tz offset=", str); - const [_, h, m] = str.match(/([+-]\d+):(\d+)$/) || ["+00", "00"]; + const [_, h, m] = str.match(/([+-]\d+):(\d+)$/) || ["", "+00", "00"]; return h * 60 + (h > 0 ? +m : -m); }, From 98ed468ad0d7e3a3b0ea3e526164e7a112f7d4fc Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 15 Nov 2024 17:47:12 +0000 Subject: [PATCH 29/34] add compliments_file tests to e2e, fix module for interval timer still running --- modules/default/compliments/compliments.js | 17 ++++++++--- tests/e2e/modules/compliments_spec.js | 33 ++++++++++++++++++++++ tests/electron/modules/compliments_spec.js | 2 +- tests/mocks/compliments_file.json | 4 +-- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 9f9270f199..b7dc1295b0 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -49,7 +49,12 @@ Module.register("compliments", { if ((this.config.remoteFileRefreshInterval >= this.refreshMinimumDelay) || window.mmTestMode === "true") { setInterval(async () => { const response = await this.loadComplimentFile(); - this.compliments_new = JSON.parse(response); + if (response) { + this.compliments_new = JSON.parse(response); + } + else { + Log.error(`${this.name} remoteFile refresh failed`); + } }, this.config.remoteFileRefreshInterval); } else { @@ -204,10 +209,14 @@ Module.register("compliments", { // we need to force the server to not give us the cached result // create an extra property (ignored by the server handler) just so the url string is different // that will never be the same, using the ms value of date - if (this.config.remoteFileRefreshInterval !== 0) this.urlSuffix = `?dummy=${Date.now()}`; + if (isRemote && this.config.remoteFileRefreshInterval !== 0) this.urlSuffix = `?dummy=${Date.now()}`; // - const response = await fetch(url + this.urlSuffix); - return await response.text(); + try { + const response = await fetch(url + this.urlSuffix); + return await response.text(); + } catch (error) { + Log.info(`${this.name} fetch failed error=`, error); + } }, /** diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index 7763dc689b..47214781ab 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -89,4 +89,37 @@ describe("Compliments module", () => { }); }); }); + + describe("Feature remote compliments file", () => { + describe("get list from remote file", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_file.js"); + await helpers.getDocument(); + }); + it("shows 'Remote compliment file works!' as only anytime list set", async () => { + //await helpers.startApplication("tests/configs/modules/compliments/compliments_file.js", "01 Jan 2022 10:00:00 GMT"); + await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); + }); + // afterAll(async () =>{ + // await helpers.stopApplication() + // }); + }); + + describe("get list from remote file w update", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_file_change.js"); + await helpers.getDocument(); + }); + it("shows 'test in morning' as test time set to 10am", async () => { + //await helpers.startApplication("tests/configs/modules/compliments/compliments_file_change.js", "01 Jan 2022 10:00:00 GMT"); + await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); + await new Promise((r) => setTimeout(r, 10000)); + await expect(doTest(["test in morning"])).resolves.toBe(true); + }); + // afterAll(async () =>{ + // await helpers.stopApplication() + // }); + }); + }); + }); diff --git a/tests/electron/modules/compliments_spec.js b/tests/electron/modules/compliments_spec.js index 8626b503c8..4555ffcbf7 100644 --- a/tests/electron/modules/compliments_spec.js +++ b/tests/electron/modules/compliments_spec.js @@ -88,7 +88,7 @@ describe("Compliments module", () => { }); }); describe("get updated list from remote file", () => { - it("shows 'test in morning' as test time set to 10am", async () => { + it("shows 'test in morning'", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_file_change.js", "01 Jan 2022 10:00:00 GMT"); await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); await new Promise((r) => setTimeout(r, 10000)); diff --git a/tests/mocks/compliments_file.json b/tests/mocks/compliments_file.json index 89171b16ed..03e2e8b109 100644 --- a/tests/mocks/compliments_file.json +++ b/tests/mocks/compliments_file.json @@ -1,5 +1,3 @@ { - "morning": ["test in morning"], - "afternoon": ["test in afternoon"], - "evening": ["test in evening"] + "anytime": ["test in morning"] } From 070a135b3da8b625491bf9d579cce87c80aca297 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 19 Nov 2024 20:42:15 -0600 Subject: [PATCH 30/34] fix sdate shown for in progress fullday multi-day events --- modules/default/calendar/calendar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 48b4f2c96c..bbd5408e2e 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -438,7 +438,10 @@ Module.register("calendar", { if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && moment(event.startDate, "x").format("YYYYMMDD") !== moment(event.endDate, "x").format("YYYYMMDD")) { timeWrapper.innerHTML += "-"; timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat)); - } + } else + if ((moment(event.startDate, "x").format("YYYYMMDD") !== moment(event.endDate, "x").format("YYYYMMDD")) && (moment(event.startDate, "x") < moment(now, "x"))) { + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(now, "x").format(this.config.fullDayEventDateFormat)); + } } else if (this.config.getRelative > 0 && event.startDate < now) { // Ongoing and getRelative is set timeWrapper.innerHTML = CalendarUtils.capFirst( From e43be28583742b41361133d5e72f277cee03dd4c Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 5 Dec 2024 09:15:59 -0600 Subject: [PATCH 31/34] fix review changes, mostly remove dead comments --- modules/default/calendar/calendar.js | 21 ++++++++------------- tests/electron/modules/calendar_spec.js | 3 --- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index bbd5408e2e..805f32f31b 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -422,7 +422,7 @@ Module.register("calendar", { timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); // Add end time if showEnd if (this.config.showEnd) { - // andhas a duation + // and has a duation if (event.startDate !== event.endDate) { timeWrapper.innerHTML += "-"; timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); @@ -473,10 +473,10 @@ Module.register("calendar", { if (event.startDate >= now || (event.fullDayEvent && this.eventEndingWithinNextFullTimeUnit(event, ONE_DAY))) { // Use relative time if (!this.config.hideTime && !event.fullDayEvent) { - Log.info("event not hidden and not fullday"); - timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))}`; // A ${event.startDate}; + Log.debug("event not hidden and not fullday"); + timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))}`; } else { - Log.info("event full day or hidden"); + Log.debug("event full day or hidden"); timeWrapper.innerHTML = `${CalendarUtils.capFirst( moment(event.startDate, "x").calendar(null, { sameDay: this.config.showTimeToday ? "LT" : `[${this.translate("TODAY")}]`, @@ -484,7 +484,7 @@ Module.register("calendar", { nextWeek: "dddd", sameElse: event.fullDayEvent ? this.config.fullDayEventDateFormat : this.config.dateFormat }) - )}`; // B ${event.startDate}; + )}`; } if (event.fullDayEvent) { // Full days events within the next two days @@ -507,7 +507,7 @@ Module.register("calendar", { } else if (event.startDate - now < this.config.getRelative * ONE_HOUR) { Log.info("not full day but within getrelative size"); // If event is within getRelative hours, display 'in xxx' time format or moment.fromNow() - timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`; // C ${event.startDate}; + timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`; } } else { // Ongoing event @@ -638,9 +638,6 @@ Module.register("calendar", { if (this.config.hideDuplicates && this.listContainsEvent(events, event)) { continue; } - //if (--remainingEntries < 0) { - // break; - //} } event.url = calendarUrl; @@ -695,9 +692,9 @@ Module.register("calendar", { by_url_calevents.sort(function (a, b) { return a.startDate - b.startDate; }); - Log.info(`pushing ${by_url_calevents.length} events to total with room for ${remainingEntries}`); + Log.debug(`pushing ${by_url_calevents.length} events to total with room for ${remainingEntries}`); events = events.concat(by_url_calevents.slice(0, remainingEntries)); - Log.info(`events for calendar=${events.length}`); + Log.debug(`events for calendar=${events.length}`); } Log.info(`sorting events count=${events.length}`); events.sort(function (a, b) { @@ -910,8 +907,6 @@ Module.register("calendar", { let p = this.getCalendarProperty(url, property, defaultValue); if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") { const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName); - //if (p instanceof Array) p.push(className); - //else p = className + p; } diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 21adffc4b4..7c869c350a 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -1,7 +1,4 @@ const helpers = require("../helpers/global-setup"); -//const stdMocks = require('std-mocks') - -//stdMocks.use(); describe("Calendar module", () => { From f9239a9b04e104899bdd60fd3626ff6f78c18b12 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 5 Dec 2024 09:28:36 -0600 Subject: [PATCH 32/34] restore changed default on dateFormat --- modules/default/calendar/calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 805f32f31b..6af652ba77 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -25,7 +25,7 @@ Module.register("calendar", { fadePoint: 0.25, // Start on 1/4th of the list. urgency: 7, timeFormat: "relative", - dateFormat: "MMM Do MM:hh", + dateFormat: "MMM Do", dateEndFormat: "LT", fullDayEventDateFormat: "MMM Do", showEnd: false, From 95a81d13a3bcceb95732a5bb1ab1b3dc7fb246d4 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 6 Dec 2024 06:47:42 -0600 Subject: [PATCH 33/34] Update show-duplicates-in-calendar.js Remove extra comment --- tests/configs/modules/calendar/show-duplicates-in-calendar.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/configs/modules/calendar/show-duplicates-in-calendar.js b/tests/configs/modules/calendar/show-duplicates-in-calendar.js index b5268d52ae..7ad420944d 100644 --- a/tests/configs/modules/calendar/show-duplicates-in-calendar.js +++ b/tests/configs/modules/calendar/show-duplicates-in-calendar.js @@ -27,7 +27,6 @@ let config = { ] }; -// adde here short term, was executed in the testcase runner helper, but failed?? /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") { module.exports = config; From bf1b1b3adad6cc59a427cad0696cad38b1c0d0e7 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 6 Dec 2024 06:50:11 -0600 Subject: [PATCH 34/34] Update calendar_spec.js --- tests/electron/modules/calendar_spec.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 7c869c350a..f73d673489 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -280,6 +280,3 @@ describe("Calendar module", () => { }); }); - -//var output = stdMocks.flush() -//console.log(output.stdout)