From a7352758649608c393cb4b7915668ecdaa3a0c37 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Wed, 28 Oct 2020 09:31:05 -0500 Subject: [PATCH] fix full day recurring events east of UTC for RRULE/Luxon confusion, also update windows zones to include old offset based strings (UTC+05:00) US Eastern and Canada --- modules/default/calendar/calendarfetcher.js | 148 +++++++++++++------- modules/default/calendar/windowsZones.json | 100 ++++++++++++- 2 files changed, 196 insertions(+), 52 deletions(-) diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index f29dc9e0..1d0e24e0 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -32,6 +32,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn let reloadTimer = null; let events = []; + let debug = false; let fetchFailedCallback = function () {}; let eventsReceivedCallback = function () {}; @@ -215,7 +216,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn } const dates = rule.between(pastLocal, futureLocal, true, limitFunction); - // console.log("title="+event.summary.val+" dates="+JSON.stringify(dates)) + if (debug) console.log("title=" + event.summary.val + " dates=" + JSON.stringify(dates)); // 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 // had its date changed from outside the range to inside the range. For the time being, @@ -243,54 +244,25 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn let curEvent = event; let showRecurrence = true; + // for full day events, the time might be off from RRULE/Luxon problem + if (isFullDayEvent(event)) { + if (debug) console.log("fullday"); + // if the offset is negative, east of GMT where the problem is + if (date.getTimezoneOffset() < 0) { + // get the offset of today when we are processing + // this will be the correction we need to apply + let nowOffset = new Date().getTimezoneOffset(); + if (debug) console.log("now offset is " + nowOffset); + // reduce the time by the offset + if (debug) console.log(" recurring date is " + date + " offset is " + date.getTimezoneOffset()); + // apply the correction to the date/time to get it UTC relative + date = new Date(date.getTime() - Math.abs(nowOffset) * 60000); + if (debug) console.log("new recurring date is " + date); + } + } startDate = moment(date); - // console.log("now timezone="+ moment.tz.guess()); - // whether we need to adjust for RRULE returning the wrong date, with the right time (forward of URC timezones) - let adjustDays = 0; - // if a timezone was specified - if (!event.start.tz) { - event.start.tz = moment.tz.guess(); - } - // console.log("tz="+event.start.tz) - if (event.start.tz) { - // if this is a windows timezone - if (event.start.tz.indexOf(" ") > 0) { - // use the lookup table to get theIANA name as moment and date don't know MS timezones - let tz = getIanaTZFromMS(event.start.tz); - // watch out for unregistered windows timezone names - // if we had a successfule lookup - if (tz) { - // change the timezone to the IANA name - event.start.tz = getIanaTZFromMS(event.start.tz); - // console.log("corrected timezone="+event.start.tz) - } - } - // get the start time in that timezone - let mms = moment.tz(moment(event.start), event.start.tz).utcOffset(); - // console.log("ms offset="+mms) - // get the specified date in that timezone - let mm = moment.tz(moment(date), event.start.tz); - let mmo = mm.utcOffset(); - // console.log("mm ofset="+ mmo+" hour="+mm.format("H")+" event date="+mm.toDate()) - // if the offset is greater than 0, east of london - if (mmo > 0) { - 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 - adjustDays = 24; - // console.log("adjusting date") - } - if (mmo > mms) { - adjustDays += 1; - // console.log("adjust up 1 hour dst change") - } else if (mmo < mms) { - adjustDays -= 1; - //console.log("adjust down 1 hour dst change") - } - } - } + + let adjustDays = getCorrection(event, date); // 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) { @@ -380,12 +352,13 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn //console.log("end same as start") endDate = endDate.endOf("day"); } - + // get correction for date saving and dst change between now and then + let adjustDays = getCorrection(event, startDate.toDate()); // Every thing is good. Add it to the list. newEvents.push({ title: title, - startDate: startDate.format("x"), - endDate: endDate.format("x"), + startDate: (adjustDays ? startDate.subtract(adjustDays, "hours") : startDate).format("x"), + endDate: (adjustDays ? endDate.subtract(adjustDays, "hours") : endDate).format("x"), fullDayEvent: fullDayEvent, class: event.class, location: location, @@ -407,6 +380,79 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }); }; + /* + * + * get the time correction, either dst/std or full day in cases where utc time is day before plus offset + * + */ + const getCorrection = function (event, date) { + let adjustHours = 0; + // if a timezone was specified + if (!event.start.tz) { + if (debug) console.log(" if no tz, guess based on now"); + event.start.tz = moment.tz.guess(); + } + if (debug) console.log("initial tz=" + event.start.tz); + + // if there is a start date specified + if (event.start.tz) { + // if this is a windows timezone + if (event.start.tz.includes(" ")) { + // use the lookup table to get theIANA name as moment and date don't know MS timezones + let tz = getIanaTZFromMS(event.start.tz); + // watch out for unregistered windows timezone names + // if we had a successfule lookup + if (tz) { + // change the timezone to the IANA name + event.start.tz = tz; + // if(debug) console.log("corrected timezone="+event.start.tz) + } + } + if (debug) console.log("corrected tz=" + event.start.tz); + // if there is still an offset, lookup failed, use it + if (event.start.tz.startsWith("(")) { + const regex = /[+|-]\d*:\d*/; + offset = event.start.tz.match(regex).toString(); + if (debug) console.log("ical offset=" + offset); + } + + // get the start time in that timezone + if (debug) console.log("ttttttt=" + moment(event.start).toDate()); + let mms = moment.tz(moment(event.start), event.start.tz).utcOffset(); + if (debug) console.log("ms offset=" + mms); + + if (debug) console.log("start date =" + moment.tz(moment(event.start), event.start.tz).toDate()); + + // get the specified date in that timezone + let mm = moment.tz(moment(date), event.start.tz); + if (debug) console.log("mm=" + mm.toDate()); + let mmo = mm.utcOffset(); + if (debug) console.log("mm ofset=" + mmo + " hour=" + mm.format("H") + " event date=" + mm.toDate()); + // if the offset is greater than 0, east of london + if (mmo > 0) { + // big offset + if (debug) console.log("offset"); + let h = parseInt(mm.format("H")); + // check if the event time is less than the offset + if (h > 0 && h < Math.abs(mmo) / 60) { + // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) + // we need to fix that + adjustHours = 24; + // if(debug) console.log("adjusting date") + } + if (mmo > mms) { + adjustHours += 1; + if (debug) console.log("adjust up 1 hour dst change"); + } else if (mmo < mms) { + adjustHours -= 1; + if (debug) console.log("adjust down 1 hour dst change"); + } + } + } + if (debug) console.log("adjustHours=" + adjustHours); + return adjustHours; + }; + /** * * lookup iana tz from windows diff --git a/modules/default/calendar/windowsZones.json b/modules/default/calendar/windowsZones.json index 2e6bd6b3..cad82bb9 100644 --- a/modules/default/calendar/windowsZones.json +++ b/modules/default/calendar/windowsZones.json @@ -135,5 +135,103 @@ "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"] } + "Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] }, + "(UTC-12:00) International Date Line West": { "iana": ["Etc/GMT+12"] }, + "(UTC-11:00) Midway Island, Samoa": { "iana": ["Pacific/Apia"] }, + "(UTC-10:00) Hawaii": { "iana": ["Pacific/Honolulu"] }, + "(UTC-09:00) Alaska": { "iana": ["America/Anchorage"] }, + "(UTC-08:00) Pacific Time (US & Canada); Tijuana": { "iana": ["America/Los_Angeles"] }, + "(UTC-08:00) Pacific Time (US and Canada); Tijuana": { "iana": ["America/Los_Angeles"] }, + "(UTC-07:00) Mountain Time (US & Canada)": { "iana": ["America/Denver"] }, + "(UTC-07:00) Mountain Time (US and Canada)": { "iana": ["America/Denver"] }, + "(UTC-07:00) Chihuahua, La Paz, Mazatlan": { "iana": [null] }, + "(UTC-07:00) Arizona": { "iana": ["America/Phoenix"] }, + "(UTC-06:00) Central Time (US & Canada)": { "iana": ["America/Chicago"] }, + "(UTC-06:00) Central Time (US and Canada)": { "iana": ["America/Chicago"] }, + "(UTC-06:00) Saskatchewan": { "iana": ["America/Regina"] }, + "(UTC-06:00) Guadalajara, Mexico City, Monterrey": { "iana": [null] }, + "(UTC-06:00) Central America": { "iana": ["America/Guatemala"] }, + "(UTC-05:00) Eastern Time (US & Canada)": { "iana": ["America/New_York"] }, + "(UTC-05:00) Eastern Time (US and Canada)": { "iana": ["America/New_York"] }, + "(UTC-05:00) Indiana (East)": { "iana": ["America/Indianapolis"] }, + "(UTC-05:00) Bogota, Lima, Quito": { "iana": ["America/Bogota"] }, + "(UTC-04:00) Atlantic Time (Canada)": { "iana": ["America/Halifax"] }, + "(UTC-04:00) Georgetown, La Paz, San Juan": { "iana": ["America/La_Paz"] }, + "(UTC-04:00) Santiago": { "iana": ["America/Santiago"] }, + "(UTC-03:30) Newfoundland": { "iana": [null] }, + "(UTC-03:00) Brasilia": { "iana": ["America/Sao_Paulo"] }, + "(UTC-03:00) Georgetown": { "iana": ["America/Cayenne"] }, + "(UTC-03:00) Greenland": { "iana": ["America/Godthab"] }, + "(UTC-02:00) Mid-Atlantic": { "iana": [null] }, + "(UTC-01:00) Azores": { "iana": ["Atlantic/Azores"] }, + "(UTC-01:00) Cape Verde Islands": { "iana": ["Atlantic/Cape_Verde"] }, + "(UTC) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London": { "iana": [null] }, + "(UTC) Monrovia, Reykjavik": { "iana": ["Atlantic/Reykjavik"] }, + "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague": { "iana": ["Europe/Budapest"] }, + "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb": { "iana": ["Europe/Warsaw"] }, + "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris": { "iana": ["Europe/Paris"] }, + "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna": { "iana": ["Europe/Berlin"] }, + "(UTC+01:00) West Central Africa": { "iana": ["Africa/Lagos"] }, + "(UTC+02:00) Minsk": { "iana": ["Europe/Chisinau"] }, + "(UTC+02:00) Cairo": { "iana": ["Africa/Cairo"] }, + "(UTC+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius": { "iana": ["Europe/Kiev"] }, + "(UTC+02:00) Athens, Bucharest, Istanbul": { "iana": ["Europe/Bucharest"] }, + "(UTC+02:00) Jerusalem": { "iana": ["Asia/Jerusalem"] }, + "(UTC+02:00) Harare, Pretoria": { "iana": ["Africa/Johannesburg"] }, + "(UTC+03:00) Moscow, St. Petersburg, Volgograd": { "iana": ["Europe/Moscow"] }, + "(UTC+03:00) Kuwait, Riyadh": { "iana": ["Asia/Riyadh"] }, + "(UTC+03:00) Nairobi": { "iana": ["Africa/Nairobi"] }, + "(UTC+03:00) Baghdad": { "iana": ["Asia/Baghdad"] }, + "(UTC+03:30) Tehran": { "iana": ["Asia/Tehran"] }, + "(UTC+04:00) Abu Dhabi, Muscat": { "iana": ["Asia/Dubai"] }, + "(UTC+04:00) Baku, Tbilisi, Yerevan": { "iana": ["Asia/Yerevan"] }, + "(UTC+04:30) Kabul": { "iana": [null] }, + "(UTC+05:00) Ekaterinburg": { "iana": ["Asia/Yekaterinburg"] }, + "(UTC+05:00) Tashkent": { "iana": ["Asia/Tashkent"] }, + "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi": { "iana": ["Asia/Calcutta"] }, + "(UTC+05:45) Kathmandu": { "iana": ["Asia/Katmandu"] }, + "(UTC+06:00) Astana, Dhaka": { "iana": ["Asia/Almaty"] }, + "(UTC+06:00) Sri Jayawardenepura": { "iana": ["Asia/Colombo"] }, + "(UTC+06:00) Almaty, Novosibirsk": { "iana": ["Asia/Novosibirsk"] }, + "(UTC+06:30) Yangon (Rangoon)": { "iana": ["Asia/Rangoon"] }, + "(UTC+07:00) Bangkok, Hanoi, Jakarta": { "iana": ["Asia/Bangkok"] }, + "(UTC+07:00) Krasnoyarsk": { "iana": ["Asia/Krasnoyarsk"] }, + "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi": { "iana": ["Asia/Shanghai"] }, + "(UTC+08:00) Kuala Lumpur, Singapore": { "iana": ["Asia/Singapore"] }, + "(UTC+08:00) Taipei": { "iana": ["Asia/Taipei"] }, + "(UTC+08:00) Perth": { "iana": ["Australia/Perth"] }, + "(UTC+08:00) Irkutsk, Ulaanbaatar": { "iana": ["Asia/Irkutsk"] }, + "(UTC+09:00) Seoul": { "iana": ["Asia/Seoul"] }, + "(UTC+09:00) Osaka, Sapporo, Tokyo": { "iana": ["Asia/Tokyo"] }, + "(UTC+09:00) Yakutsk": { "iana": ["Asia/Yakutsk"] }, + "(UTC+09:30) Darwin": { "iana": ["Australia/Darwin"] }, + "(UTC+09:30) Adelaide": { "iana": ["Australia/Adelaide"] }, + "(UTC+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] }, + "(GMT+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] }, + "(UTC+10:00) Brisbane": { "iana": ["Australia/Brisbane"] }, + "(UTC+10:00) Hobart": { "iana": ["Australia/Hobart"] }, + "(UTC+10:00) Vladivostok": { "iana": ["Asia/Vladivostok"] }, + "(UTC+10:00) Guam, Port Moresby": { "iana": ["Pacific/Port_Moresby"] }, + "(UTC+11:00) Magadan, Solomon Islands, New Caledonia": { "iana": ["Pacific/Guadalcanal"] }, + "(UTC+12:00) Fiji, Kamchatka, Marshall Is.": { "iana": [null] }, + "(UTC+12:00) Auckland, Wellington": { "iana": ["Pacific/Auckland"] }, + "(UTC+13:00) Nuku'alofa": { "iana": ["Pacific/Tongatapu"] }, + "(UTC-03:00) Buenos Aires": { "iana": ["America/Buenos_Aires"] }, + "(UTC+02:00) Beirut": { "iana": ["Asia/Beirut"] }, + "(UTC+02:00) Amman": { "iana": ["Asia/Amman"] }, + "(UTC-06:00) Guadalajara, Mexico City, Monterrey - New": { "iana": ["America/Mexico_City"] }, + "(UTC-07:00) Chihuahua, La Paz, Mazatlan - New": { "iana": ["America/Chihuahua"] }, + "(UTC-08:00) Tijuana, Baja California": { "iana": ["America/Tijuana"] }, + "(UTC+02:00) Windhoek": { "iana": ["Africa/Windhoek"] }, + "(UTC+03:00) Tbilisi": { "iana": ["Asia/Tbilisi"] }, + "(UTC-04:00) Manaus": { "iana": ["America/Cuiaba"] }, + "(UTC-03:00) Montevideo": { "iana": ["America/Montevideo"] }, + "(UTC+04:00) Yerevan": { "iana": [null] }, + "(UTC-04:30) Caracas": { "iana": ["America/Caracas"] }, + "(UTC) Casablanca": { "iana": ["Africa/Casablanca"] }, + "(UTC+05:00) Islamabad, Karachi": { "iana": ["Asia/Karachi"] }, + "(UTC+04:00) Port Louis": { "iana": ["Indian/Mauritius"] }, + "(UTC) Coordinated Universal Time": { "iana": ["Etc/GMT"] }, + "(UTC-04:00) Asuncion": { "iana": ["America/Asuncion"] }, + "(UTC+12:00) Petropavlovsk-Kamchatsky": { "iana": [null] } }