From 04dae52a6ca82a0d754a788b5576e7d9c77ee290 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Sun, 4 Oct 2020 12:10:19 -0500 Subject: [PATCH] correct daylight/standard adjustments, windows timezones and east of london rrule bug --- modules/default/calendar/calendarfetcher.js | 63 ++++++++- modules/default/calendar/windowsZones.json | 139 ++++++++++++++++++++ 2 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 modules/default/calendar/windowsZones.json diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 964cb955..bb3c4daa 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -242,6 +242,45 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn let showRecurrence = true; startDate = moment(date); + // whether we need to adjust for RRULE returning the wrong date, with the right time (forward of URC timezones) + let adjustHours = 0; + // if a timezone was specified + if (!event.start.tz) { + // guess at one + event.start.tz = moment.tz.guess(); + } + // if there is a timezone + if (event.start.tz) { + // if it contains a space, it is windows, + if (event.start.tz.indexOf(" ") > 0) { + // get the IANA name string from hash + event.start.tz = getIanaTZFromMS(event.start.tz); + } + // get the offset of the start date/time + let mms = moment.tz(moment(event.start), event.start.tz).utcOffset(); + // get the specified recurring date/time in that timezone + let mm = moment.tz(moment(date), event.start.tz); + // and its offset + let mmo = mm.utcOffset(); + // if the offset is greater than 0, east of london + if (mmo > 0) { + // get the hour for the recurring date/time + let h = parseInt(mm.format("H")); + // check if the event time is less than the offset + if (h > 0 && h < mmo / 60) { + // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) + // we need to fix that + adjustHours = 24; + } + // did the daylight savings offset change from start to now? + // fix it + if (mmo > mms) { + adjustHours += 1; + } else if (mmo < mms) { + adjustHours -= 1; + } + } + } // 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) { @@ -277,8 +316,8 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn addedEvents++; newEvents.push({ title: recurrenceTitle, - startDate: startDate.format("x"), - endDate: endDate.format("x"), + startDate: (adjustHours ? startDate.subtract(adjustHours, "hours") : startDate).format("x"), + endDate: (adjustHours ? endDate.subtract(adjustHours, "hours") : endDate).format("x"), fullDayEvent: isFullDayEvent(event), recurringEvent: true, class: event.class, @@ -324,6 +363,10 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn if (fullDayEvent && startDate <= today) { startDate = moment(today); } + // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) + if (fullDayEvent && startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } // Every thing is good. Add it to the list. newEvents.push({ @@ -351,6 +394,22 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }); }; + /** + * + * lookup iana tz from windows + */ + let zoneTable = null; + const getIanaTZFromMS = function (msTZName) { + if (!zoneTable) { + const p = require("path"); + zoneTable = require(p.join(__dirname, "windowsZones.json")); + } + // Get hash entry + const he = zoneTable[msTZName]; + // If found return iana name, else null + return he ? he.iana[0] : null; + }; + /** * Schedule the timer for the next update. */ diff --git a/modules/default/calendar/windowsZones.json b/modules/default/calendar/windowsZones.json new file mode 100644 index 00000000..2e6bd6b3 --- /dev/null +++ b/modules/default/calendar/windowsZones.json @@ -0,0 +1,139 @@ +{ + "Dateline Standard Time": { "iana": ["Etc/GMT+12"] }, + "UTC-11": { "iana": ["Etc/GMT+11"] }, + "Aleutian Standard Time": { "iana": ["America/Adak"] }, + "Hawaiian Standard Time": { "iana": ["Pacific/Honolulu"] }, + "Marquesas Standard Time": { "iana": ["Pacific/Marquesas"] }, + "Alaskan Standard Time": { "iana": ["America/Anchorage"] }, + "UTC-09": { "iana": ["Etc/GMT+9"] }, + "Pacific Standard Time (Mexico)": { "iana": ["America/Tijuana"] }, + "UTC-08": { "iana": ["Etc/GMT+8"] }, + "Pacific Standard Time": { "iana": ["America/Los_Angeles"] }, + "US Mountain Standard Time": { "iana": ["America/Phoenix"] }, + "Mountain Standard Time (Mexico)": { "iana": ["America/Chihuahua"] }, + "Mountain Standard Time": { "iana": ["America/Denver"] }, + "Central America Standard Time": { "iana": ["America/Guatemala"] }, + "Central Standard Time": { "iana": ["America/Chicago"] }, + "Easter Island Standard Time": { "iana": ["Pacific/Easter"] }, + "Central Standard Time (Mexico)": { "iana": ["America/Mexico_City"] }, + "Canada Central Standard Time": { "iana": ["America/Regina"] }, + "SA Pacific Standard Time": { "iana": ["America/Bogota"] }, + "Eastern Standard Time (Mexico)": { "iana": ["America/Cancun"] }, + "Eastern Standard Time": { "iana": ["America/New_York"] }, + "Haiti Standard Time": { "iana": ["America/Port-au-Prince"] }, + "Cuba Standard Time": { "iana": ["America/Havana"] }, + "US Eastern Standard Time": { "iana": ["America/Indianapolis"] }, + "Turks And Caicos Standard Time": { "iana": ["America/Grand_Turk"] }, + "Paraguay Standard Time": { "iana": ["America/Asuncion"] }, + "Atlantic Standard Time": { "iana": ["America/Halifax"] }, + "Venezuela Standard Time": { "iana": ["America/Caracas"] }, + "Central Brazilian Standard Time": { "iana": ["America/Cuiaba"] }, + "SA Western Standard Time": { "iana": ["America/La_Paz"] }, + "Pacific SA Standard Time": { "iana": ["America/Santiago"] }, + "Newfoundland Standard Time": { "iana": ["America/St_Johns"] }, + "Tocantins Standard Time": { "iana": ["America/Araguaina"] }, + "E. South America Standard Time": { "iana": ["America/Sao_Paulo"] }, + "SA Eastern Standard Time": { "iana": ["America/Cayenne"] }, + "Argentina Standard Time": { "iana": ["America/Buenos_Aires"] }, + "Greenland Standard Time": { "iana": ["America/Godthab"] }, + "Montevideo Standard Time": { "iana": ["America/Montevideo"] }, + "Magallanes Standard Time": { "iana": ["America/Punta_Arenas"] }, + "Saint Pierre Standard Time": { "iana": ["America/Miquelon"] }, + "Bahia Standard Time": { "iana": ["America/Bahia"] }, + "UTC-02": { "iana": ["Etc/GMT+2"] }, + "Azores Standard Time": { "iana": ["Atlantic/Azores"] }, + "Cape Verde Standard Time": { "iana": ["Atlantic/Cape_Verde"] }, + "UTC": { "iana": ["Etc/GMT"] }, + "GMT Standard Time": { "iana": ["Europe/London"] }, + "Greenwich Standard Time": { "iana": ["Atlantic/Reykjavik"] }, + "Sao Tome Standard Time": { "iana": ["Africa/Sao_Tome"] }, + "Morocco Standard Time": { "iana": ["Africa/Casablanca"] }, + "W. Europe Standard Time": { "iana": ["Europe/Berlin"] }, + "Central Europe Standard Time": { "iana": ["Europe/Budapest"] }, + "Romance Standard Time": { "iana": ["Europe/Paris"] }, + "Central European Standard Time": { "iana": ["Europe/Warsaw"] }, + "W. Central Africa Standard Time": { "iana": ["Africa/Lagos"] }, + "Jordan Standard Time": { "iana": ["Asia/Amman"] }, + "GTB Standard Time": { "iana": ["Europe/Bucharest"] }, + "Middle East Standard Time": { "iana": ["Asia/Beirut"] }, + "Egypt Standard Time": { "iana": ["Africa/Cairo"] }, + "E. Europe Standard Time": { "iana": ["Europe/Chisinau"] }, + "Syria Standard Time": { "iana": ["Asia/Damascus"] }, + "West Bank Standard Time": { "iana": ["Asia/Hebron"] }, + "South Africa Standard Time": { "iana": ["Africa/Johannesburg"] }, + "FLE Standard Time": { "iana": ["Europe/Kiev"] }, + "Israel Standard Time": { "iana": ["Asia/Jerusalem"] }, + "Kaliningrad Standard Time": { "iana": ["Europe/Kaliningrad"] }, + "Sudan Standard Time": { "iana": ["Africa/Khartoum"] }, + "Libya Standard Time": { "iana": ["Africa/Tripoli"] }, + "Namibia Standard Time": { "iana": ["Africa/Windhoek"] }, + "Arabic Standard Time": { "iana": ["Asia/Baghdad"] }, + "Turkey Standard Time": { "iana": ["Europe/Istanbul"] }, + "Arab Standard Time": { "iana": ["Asia/Riyadh"] }, + "Belarus Standard Time": { "iana": ["Europe/Minsk"] }, + "Russian Standard Time": { "iana": ["Europe/Moscow"] }, + "E. Africa Standard Time": { "iana": ["Africa/Nairobi"] }, + "Iran Standard Time": { "iana": ["Asia/Tehran"] }, + "Arabian Standard Time": { "iana": ["Asia/Dubai"] }, + "Astrakhan Standard Time": { "iana": ["Europe/Astrakhan"] }, + "Azerbaijan Standard Time": { "iana": ["Asia/Baku"] }, + "Russia Time Zone 3": { "iana": ["Europe/Samara"] }, + "Mauritius Standard Time": { "iana": ["Indian/Mauritius"] }, + "Saratov Standard Time": { "iana": ["Europe/Saratov"] }, + "Georgian Standard Time": { "iana": ["Asia/Tbilisi"] }, + "Volgograd Standard Time": { "iana": ["Europe/Volgograd"] }, + "Caucasus Standard Time": { "iana": ["Asia/Yerevan"] }, + "Afghanistan Standard Time": { "iana": ["Asia/Kabul"] }, + "West Asia Standard Time": { "iana": ["Asia/Tashkent"] }, + "Ekaterinburg Standard Time": { "iana": ["Asia/Yekaterinburg"] }, + "Pakistan Standard Time": { "iana": ["Asia/Karachi"] }, + "Qyzylorda Standard Time": { "iana": ["Asia/Qyzylorda"] }, + "India Standard Time": { "iana": ["Asia/Calcutta"] }, + "Sri Lanka Standard Time": { "iana": ["Asia/Colombo"] }, + "Nepal Standard Time": { "iana": ["Asia/Katmandu"] }, + "Central Asia Standard Time": { "iana": ["Asia/Almaty"] }, + "Bangladesh Standard Time": { "iana": ["Asia/Dhaka"] }, + "Omsk Standard Time": { "iana": ["Asia/Omsk"] }, + "Myanmar Standard Time": { "iana": ["Asia/Rangoon"] }, + "SE Asia Standard Time": { "iana": ["Asia/Bangkok"] }, + "Altai Standard Time": { "iana": ["Asia/Barnaul"] }, + "W. Mongolia Standard Time": { "iana": ["Asia/Hovd"] }, + "North Asia Standard Time": { "iana": ["Asia/Krasnoyarsk"] }, + "N. Central Asia Standard Time": { "iana": ["Asia/Novosibirsk"] }, + "Tomsk Standard Time": { "iana": ["Asia/Tomsk"] }, + "China Standard Time": { "iana": ["Asia/Shanghai"] }, + "North Asia East Standard Time": { "iana": ["Asia/Irkutsk"] }, + "Singapore Standard Time": { "iana": ["Asia/Singapore"] }, + "W. Australia Standard Time": { "iana": ["Australia/Perth"] }, + "Taipei Standard Time": { "iana": ["Asia/Taipei"] }, + "Ulaanbaatar Standard Time": { "iana": ["Asia/Ulaanbaatar"] }, + "Aus Central W. Standard Time": { "iana": ["Australia/Eucla"] }, + "Transbaikal Standard Time": { "iana": ["Asia/Chita"] }, + "Tokyo Standard Time": { "iana": ["Asia/Tokyo"] }, + "North Korea Standard Time": { "iana": ["Asia/Pyongyang"] }, + "Korea Standard Time": { "iana": ["Asia/Seoul"] }, + "Yakutsk Standard Time": { "iana": ["Asia/Yakutsk"] }, + "Cen. Australia Standard Time": { "iana": ["Australia/Adelaide"] }, + "AUS Central Standard Time": { "iana": ["Australia/Darwin"] }, + "E. Australia Standard Time": { "iana": ["Australia/Brisbane"] }, + "AUS Eastern Standard Time": { "iana": ["Australia/Sydney"] }, + "West Pacific Standard Time": { "iana": ["Pacific/Port_Moresby"] }, + "Tasmania Standard Time": { "iana": ["Australia/Hobart"] }, + "Vladivostok Standard Time": { "iana": ["Asia/Vladivostok"] }, + "Lord Howe Standard Time": { "iana": ["Australia/Lord_Howe"] }, + "Bougainville Standard Time": { "iana": ["Pacific/Bougainville"] }, + "Russia Time Zone 10": { "iana": ["Asia/Srednekolymsk"] }, + "Magadan Standard Time": { "iana": ["Asia/Magadan"] }, + "Norfolk Standard Time": { "iana": ["Pacific/Norfolk"] }, + "Sakhalin Standard Time": { "iana": ["Asia/Sakhalin"] }, + "Central Pacific Standard Time": { "iana": ["Pacific/Guadalcanal"] }, + "Russia Time Zone 11": { "iana": ["Asia/Kamchatka"] }, + "New Zealand Standard Time": { "iana": ["Pacific/Auckland"] }, + "UTC+12": { "iana": ["Etc/GMT-12"] }, + "Fiji Standard Time": { "iana": ["Pacific/Fiji"] }, + "Chatham Islands Standard Time": { "iana": ["Pacific/Chatham"] }, + "UTC+13": { "iana": ["Etc/GMT-13"] }, + "Tonga Standard Time": { "iana": ["Pacific/Tongatapu"] }, + "Samoa Standard Time": { "iana": ["Pacific/Apia"] }, + "Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] } +}