Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ _This release is scheduled to be released on 2021-01-01._
- Fix calendar fetcher subsequent timing (#2160)
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
- Add a space after icons of sunrise and sunset (#2169)
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set (#2177)

## [2.13.0] - 2020-10-01

Expand Down
171 changes: 112 additions & 59 deletions modules/default/calendar/calendarfetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn

let reloadTimer = null;
let events = [];
let debug = false;
Comment thread
sdetweil marked this conversation as resolved.

let fetchFailedCallback = function () {};
let eventsReceivedCallback = function () {};
Expand Down Expand Up @@ -110,14 +111,15 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
if (event.type === "VEVENT") {
let startDate = eventDate(event, "start");
let endDate;
// console.log("\nevent="+JSON.stringify(event))
// Log.log("\nevent="+JSON.stringify(event))
if (typeof event.end !== "undefined") {
endDate = eventDate(event, "end");
} else if (typeof event.duration !== "undefined") {
endDate = startDate.clone().add(moment.duration(event.duration));
} else {
if (!isFacebookBirthday) {
endDate = startDate;
// make copy of start date, separate storage area
endDate = moment(startDate.format("x"), "x");
Comment thread
sdetweil marked this conversation as resolved.
} else {
endDate = moment(startDate).add(1, "days");
}
Expand Down Expand Up @@ -214,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) Log.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,
Expand All @@ -234,62 +236,33 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn

// Loop through the set of date entries to see which recurrences should be added to our event list.
for (let d in dates) {
const date = dates[d];
let date = dates[d];
// ical.js started returning recurrences and exdates as ISOStrings without time information.
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
// (see https://github.com/peterbraden/ical.js/pull/84 )
const dateKey = date.toISOString().substring(0, 10);
let curEvent = event;
let showRecurrence = true;

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")
}
// for full day events, the time might be off from RRULE/Luxon problem
if (isFullDayEvent(event)) {
if (debug) Log.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) Log.log("now offset is " + nowOffset);
// reduce the time by the offset
if (debug) Log.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) Log.log("new recurring date is " + date);
}
}
startDate = moment(date);

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) {
Expand All @@ -304,7 +277,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
showRecurrence = false;
}

//console.log("duration="+duration)
//Log.log("duration="+duration)

endDate = moment(parseInt(startDate.format("x")) + duration, "x");
if (startDate.format("x") === endDate.format("x")) {
Expand All @@ -327,8 +300,8 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
addedEvents++;
newEvents.push({
title: recurrenceTitle,
startDate: (adjustDays ? startDate.subtract(adjustDays, "hours") : startDate).format("x"),
endDate: (adjustDays ? endDate.subtract(adjustDays, "hours") : endDate).format("x"),
startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
fullDayEvent: isFullDayEvent(event),
recurringEvent: true,
class: event.class,
Expand All @@ -343,7 +316,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
} else {
// Single event.
const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
// console.log("full day event")
// Log.log("full day event")
if (includePastEvents) {
// Past event is too far in the past, so skip.
if (endDate < past) {
Expand Down Expand Up @@ -376,15 +349,16 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
}
// 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")) {
//console.log("end same as start")
//Log.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 ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
fullDayEvent: fullDayEvent,
class: event.class,
location: location,
Expand All @@ -406,6 +380,85 @@ 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) Log.log(" if no tz, guess based on now");
event.start.tz = moment.tz.guess();
}
if (debug) Log.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) Log.log("corrected timezone="+event.start.tz)
}
}
if (debug) Log.log("corrected tz=" + event.start.tz);
let mmo = 0; // offset from TZ string or calculated
let mm = 0; // date with tz or offset
let mms = 0; // utc offset of created with tz
// if there is still an offset, lookup failed, use it
if (event.start.tz.startsWith("(")) {
const regex = /[+|-]\d*:\d*/;
mmo = event.start.tz.match(regex).toString();
mms = mmo;
if (debug) Log.log("ical offset=" + mmo + " date=" + date);
mm = moment(date);
mm = mm.utcOffset(mmo);
} else {
// get the start time in that timezone
if (debug) Log.log("ttttttt=" + moment(event.start).toDate());
mms = moment.tz(moment(event.start), event.start.tz).utcOffset();
if (debug) Log.log("ms offset=" + mms);

if (debug) Log.log("start date =" + moment.tz(moment(event.start), event.start.tz).toDate());

// get the specified date in that timezone
mm = moment.tz(moment(date), event.start.tz);
if (debug) Log.log("mm=" + mm.toDate());
mmo = mm.utcOffset();
}
if (debug) Log.log("mm ofset=" + mmo + " hour=" + mm.format("H") + " event date=" + mm.toDate());
// if the offset is greater than 0, east of london
if (mmo !== mms) {
// big offset
if (debug) Log.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) Log.log("adjusting date")
}
if (Math.abs(mmo) > Math.abs(mms)) {
adjustHours += 1;
if (debug) Log.log("adjust up 1 hour dst change");
} else if (Math.abs(mmo) < Math.abs(mms)) {
adjustHours -= 1;
if (debug) Log.log("adjust down 1 hour dst change");
}
}
}
if (debug) Log.log("adjustHours=" + adjustHours);
return adjustHours;
};

/**
*
* lookup iana tz from windows
Expand Down Expand Up @@ -439,7 +492,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
* @returns {boolean} True if the event is a fullday event, false otherwise
*/
const isFullDayEvent = function (event) {
if (event.start.length === 8 || event.start.dateOnly) {
if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") {
return true;
}

Expand Down
100 changes: 99 additions & 1 deletion modules/default/calendar/windowsZones.json
Original file line number Diff line number Diff line change
Expand Up @@ -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] }
}
Loading