Skip to content

Commit affc719

Browse files
committed
Refactored calendar.js aswell to convert the unix UTC start and end date of an event to the proper timezone. This should now display the correct times in the DOM
1 parent d4cd5a1 commit affc719

File tree

3 files changed

+79
-81
lines changed

3 files changed

+79
-81
lines changed

modules/default/calendar/calendar.js

Lines changed: 69 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* global CalendarUtils */
22

3+
const moment = require("moment-timezone");
4+
35
Module.register("calendar", {
46
// Define module defaults
57
defaults: {
@@ -77,7 +79,7 @@ Module.register("calendar", {
7779

7880
// Define required scripts.
7981
getScripts () {
80-
return ["calendarutils.js", "moment.js"];
82+
return ["calendarutils.js", "moment-timezone.js"];
8183
},
8284

8385
// Define required translations.
@@ -215,11 +217,6 @@ Module.register("calendar", {
215217
this.updateDom(this.config.animationSpeed);
216218
},
217219

218-
eventEndingWithinNextFullTimeUnit (event, ONE_DAY) {
219-
const now = new Date();
220-
return event.endDate - now <= ONE_DAY;
221-
},
222-
223220
// Override dom generator.
224221
getDom () {
225222
const ONE_SECOND = 1000; // 1,000 milliseconds
@@ -258,7 +255,9 @@ Module.register("calendar", {
258255
let lastSeenDate = "";
259256

260257
events.forEach((event, index) => {
261-
const dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
258+
const eventStartDateMoment = this.timestampToMoment(event.startDate);
259+
const eventEndDateMoment = this.timestampToMoment(event.endDate);
260+
const dateAsString = eventStartDateMoment.format(this.config.dateFormat);
262261
if (this.config.timeFormat === "dateheaders") {
263262
if (lastSeenDate !== dateAsString) {
264263
const dateRow = document.createElement("tr");
@@ -340,7 +339,7 @@ Module.register("calendar", {
340339
repeatingCountTitle = this.countTitleForUrl(event.url);
341340

342341
if (repeatingCountTitle !== "") {
343-
const thisYear = new Date(parseInt(event.startDate)).getFullYear(),
342+
const thisYear = eventStartDateMoment.year(),
344343
yearDiff = thisYear - event.firstYear;
345344

346345
repeatingCountTitle = `, ${yearDiff} ${repeatingCountTitle}`;
@@ -395,14 +394,14 @@ Module.register("calendar", {
395394
timeWrapper.className = `time light ${this.config.flipDateHeaderTitle ? "align-right " : "align-left "}${this.timeClassForUrl(event.url)}`;
396395
timeWrapper.style.paddingLeft = "2px";
397396
timeWrapper.style.textAlign = this.config.flipDateHeaderTitle ? "right" : "left";
398-
timeWrapper.innerHTML = moment(event.startDate, "x").format("LT");
397+
timeWrapper.innerHTML = eventStartDateMoment.format("LT");
399398

400399
// Add endDate to dataheaders if showEnd is enabled
401400
if (this.config.showEnd) {
402401
if (this.config.showEndsOnlyWithDuration && event.startDate === event.endDate) {
403402
// no duration here, don't display end
404403
} else {
405-
timeWrapper.innerHTML += ` - ${CalendarUtils.capFirst(moment(event.endDate, "x").format("LT"))}`;
404+
timeWrapper.innerHTML += ` - ${CalendarUtils.capFirst(eventEndDateMoment.format("LT"))}`;
406405
}
407406
}
408407

@@ -415,70 +414,70 @@ Module.register("calendar", {
415414
const timeWrapper = document.createElement("td");
416415

417416
eventWrapper.appendChild(titleWrapper);
418-
const now = new Date();
417+
const now = moment();
419418

420419
if (this.config.timeFormat === "absolute") {
421420
// Use dateFormat
422-
timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
421+
timeWrapper.innerHTML = CalendarUtils.capFirst(eventStartDateMoment.format(this.config.dateFormat));
423422
// Add end time if showEnd
424423
if (this.config.showEnd) {
425424
// and has a duation
426425
if (event.startDate !== event.endDate) {
427426
timeWrapper.innerHTML += "-";
428-
timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
427+
timeWrapper.innerHTML += CalendarUtils.capFirst(eventEndDateMoment.format(this.config.dateEndFormat));
429428
}
430429
}
431430

432431
// For full day events we use the fullDayEventDateFormat
433432
if (event.fullDayEvent) {
434433
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
435434
event.endDate -= ONE_SECOND;
436-
timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
435+
timeWrapper.innerHTML = CalendarUtils.capFirst(eventStartDateMoment.format(this.config.fullDayEventDateFormat));
437436
// only show end if requested and allowed and the dates are different
438-
if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && moment(event.startDate, "x").format("YYYYMMDD") !== moment(event.endDate, "x").format("YYYYMMDD")) {
437+
if (this.config.showEnd && !this.config.showEndsOnlyWithDuration && !eventStartDateMoment.isSame(eventEndDateMoment, "d")) {
439438
timeWrapper.innerHTML += "-";
440-
timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat));
439+
timeWrapper.innerHTML += CalendarUtils.capFirst(eventEndDateMoment.format(this.config.fullDayEventDateFormat));
441440
} else
442-
if ((moment(event.startDate, "x").format("YYYYMMDD") !== moment(event.endDate, "x").format("YYYYMMDD")) && (moment(event.startDate, "x") < moment(now, "x"))) {
443-
timeWrapper.innerHTML = CalendarUtils.capFirst(moment(now, "x").format(this.config.fullDayEventDateFormat));
441+
if (!eventStartDateMoment.isSame(eventEndDateMoment, "d") && eventStartDateMoment.isBefore(now)) {
442+
timeWrapper.innerHTML = CalendarUtils.capFirst(now.format(this.config.fullDayEventDateFormat));
444443
}
445-
} else if (this.config.getRelative > 0 && event.startDate < now) {
444+
} else if (this.config.getRelative > 0 && eventStartDateMoment.isBefore(now)) {
446445
// Ongoing and getRelative is set
447446
timeWrapper.innerHTML = CalendarUtils.capFirst(
448447
this.translate("RUNNING", {
449448
fallback: `${this.translate("RUNNING")} {timeUntilEnd}`,
450-
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
449+
timeUntilEnd: eventEndDateMoment.fromNow(true)
451450
})
452451
);
453-
} else if (this.config.urgency > 0 && event.startDate - now < this.config.urgency * ONE_DAY) {
452+
} else if (this.config.urgency > 0 && eventStartDateMoment.diff(now, "d") < this.config.urgency) {
454453
// Within urgency days
455-
timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").fromNow());
454+
timeWrapper.innerHTML = CalendarUtils.capFirst(eventStartDateMoment.fromNow());
456455
}
457456
if (event.fullDayEvent && this.config.nextDaysRelative) {
458457
// Full days events within the next two days
459458
if (event.today) {
460459
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TODAY"));
461460
} else if (event.yesterday) {
462461
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("YESTERDAY"));
463-
} else if (event.startDate - now < ONE_DAY && event.startDate - now > 0) {
462+
} else if (event.tomorrow) {
464463
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TOMORROW"));
465-
} else if (event.startDate - now < 2 * ONE_DAY && event.startDate - now > 0) {
464+
} else if (event.dayAfterTomorrow) {
466465
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
467466
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYAFTERTOMORROW"));
468467
}
469468
}
470469
}
471470
} else {
472471
// Show relative times
473-
if (event.startDate >= now || (event.fullDayEvent && this.eventEndingWithinNextFullTimeUnit(event, ONE_DAY))) {
472+
if (eventStartDateMoment.isSameOrAfter(now) || (event.fullDayEvent && eventEndDateMoment.diff(now, "days") === 0)) {
474473
// Use relative time
475474
if (!this.config.hideTime && !event.fullDayEvent) {
476475
Log.debug("event not hidden and not fullday");
477-
timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat }))}`;
476+
timeWrapper.innerHTML = `${CalendarUtils.capFirst(eventStartDateMoment.calendar(null, { sameElse: this.config.dateFormat }))}`;
478477
} else {
479478
Log.debug("event full day or hidden");
480479
timeWrapper.innerHTML = `${CalendarUtils.capFirst(
481-
moment(event.startDate, "x").calendar(null, {
480+
eventStartDateMoment.calendar(null, {
482481
sameDay: this.config.showTimeToday ? "LT" : `[${this.translate("TODAY")}]`,
483482
nextDay: `[${this.translate("TOMORROW")}]`,
484483
nextWeek: "dddd",
@@ -488,33 +487,33 @@ Module.register("calendar", {
488487
}
489488
if (event.fullDayEvent) {
490489
// Full days events within the next two days
491-
if (event.today || (event.fullDayEvent && this.eventEndingWithinNextFullTimeUnit(event, ONE_DAY))) {
490+
if (event.today || (event.fullDayEvent && eventEndDateMoment.diff(now, "days") === 0)) {
492491
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TODAY"));
493492
} else if (event.dayBeforeYesterday) {
494493
if (this.translate("DAYBEFOREYESTERDAY") !== "DAYBEFOREYESTERDAY") {
495494
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYBEFOREYESTERDAY"));
496495
}
497496
} else if (event.yesterday) {
498497
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("YESTERDAY"));
499-
} else if (event.startDate - now < ONE_DAY && event.startDate - now > 0) {
498+
} else if (event.tomorrow) {
500499
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TOMORROW"));
501-
} else if (event.startDate - now < 2 * ONE_DAY && event.startDate - now > 0) {
500+
} else if (event.dayAfterTomorrow) {
502501
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
503502
timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYAFTERTOMORROW"));
504503
}
505504
}
506505
Log.info("event fullday");
507-
} else if (event.startDate - now < this.config.getRelative * ONE_HOUR) {
506+
} else if (eventStartDateMoment.diff(now, "h") < this.config.getRelative) {
508507
Log.info("not full day but within getrelative size");
509508
// If event is within getRelative hours, display 'in xxx' time format or moment.fromNow()
510-
timeWrapper.innerHTML = `${CalendarUtils.capFirst(moment(event.startDate, "x").fromNow())}`;
509+
timeWrapper.innerHTML = `${CalendarUtils.capFirst(eventStartDateMoment.fromNow())}`;
511510
}
512511
} else {
513512
// Ongoing event
514513
timeWrapper.innerHTML = CalendarUtils.capFirst(
515514
this.translate("RUNNING", {
516515
fallback: `${this.translate("RUNNING")} {timeUntilEnd}`,
517-
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
516+
timeUntilEnd: eventEndDateMoment.fromNow(true)
518517
})
519518
);
520519
}
@@ -593,46 +592,46 @@ Module.register("calendar", {
593592
return false;
594593
},
595594

595+
/**
596+
* converts the given timestamp to a moment with a timezone
597+
* @param timestamp timestamp from an event
598+
* @returns {moment.Moment} moment with a timezone
599+
*/
600+
timestampToMoment (timestamp) {
601+
return moment.unix(timestamp).tz(moment.tz.guess());
602+
},
603+
596604
/**
597605
* Creates the sorted list of all events.
598606
* @param {boolean} limitNumberOfEntries Whether to filter returned events for display.
599607
* @returns {object[]} Array with events.
600608
*/
601609
createEventList (limitNumberOfEntries) {
602-
const ONE_SECOND = 1000; // 1,000 milliseconds
603-
const ONE_MINUTE = ONE_SECOND * 60;
604-
const ONE_HOUR = ONE_MINUTE * 60;
605-
const ONE_DAY = ONE_HOUR * 24;
610+
let now = moment();
611+
let today = now.clone().startOf("day");
612+
let future = now.clone().startOf("day").add(this.config.maximumNumberOfDays, "days");
606613

607-
let now, today, future;
608-
if (this.config.forceUseCurrentTime || this.defaults.forceUseCurrentTime) {
609-
now = new Date();
610-
today = moment().startOf("day");
611-
future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate();
612-
} else {
613-
now = new Date(Date.now()); // Can use overridden time
614-
today = moment(now).startOf("day");
615-
future = moment(now).startOf("day").add(this.config.maximumNumberOfDays, "days").toDate();
616-
}
617614
let events = [];
618615

619616
for (const calendarUrl in this.calendarData) {
620617
const calendar = this.calendarData[calendarUrl];
621618
let remainingEntries = this.maximumEntriesForUrl(calendarUrl);
622-
let maxPastDaysCompare = now - this.maximumPastDaysForUrl(calendarUrl) * ONE_DAY;
619+
let maxPastDaysCompare = now.clone().subtract(this.maximumPastDaysForUrl(calendarUrl), "days");
623620
let by_url_calevents = [];
624621
for (const e in calendar) {
625622
const event = JSON.parse(JSON.stringify(calendar[e])); // clone object
623+
const eventStartDateMoment = this.timestampToMoment(event.startDate);
624+
const eventEndDateMoment = this.timestampToMoment(event.endDate);
626625

627626
if (this.config.hidePrivate && event.class === "PRIVATE") {
628627
// do not add the current event, skip it
629628
continue;
630629
}
631630
if (limitNumberOfEntries) {
632-
if (event.endDate < maxPastDaysCompare) {
631+
if (eventEndDateMoment.isBefore(maxPastDaysCompare)) {
633632
continue;
634633
}
635-
if (this.config.hideOngoing && event.startDate < now) {
634+
if (this.config.hideOngoing && eventStartDateMoment.isBefore(now)) {
636635
continue;
637636
}
638637
if (this.config.hideDuplicates && this.listContainsEvent(events, event)) {
@@ -641,47 +640,46 @@ Module.register("calendar", {
641640
}
642641

643642
event.url = calendarUrl;
644-
event.today = event.startDate >= today && event.startDate < today + ONE_DAY;
645-
event.dayBeforeYesterday = event.startDate >= today - ONE_DAY * 2 && event.startDate < today - ONE_DAY;
646-
event.yesterday = event.startDate >= today - ONE_DAY && event.startDate < today;
647-
event.tomorrow = !event.today && event.startDate >= today + ONE_DAY && event.startDate < today + 2 * ONE_DAY;
648-
event.dayAfterTomorrow = !event.tomorrow && event.startDate >= today + ONE_DAY * 2 && event.startDate < today + 3 * ONE_DAY;
643+
event.today = eventStartDateMoment.isSame(now, "d");
644+
event.dayBeforeYesterday = eventStartDateMoment.isSame(now.clone().subtract(2, "days"), "d");
645+
event.yesterday = eventStartDateMoment.isSame(now.clone().subtract(1, "days"), "d");
646+
event.tomorrow = eventStartDateMoment.isSame(now.clone().add(1, "days"), "d");
647+
event.dayAfterTomorrow = eventStartDateMoment.isSame(now.clone().add(2, "days"), "d");
649648

650649
/*
651650
* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
652651
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
653652
*/
654-
const maxCount = Math.round((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / ONE_DAY) + 1;
653+
const maxCount = eventEndDateMoment.diff(eventStartDateMoment, "days") + 1;
655654
if (this.config.sliceMultiDayEvents && maxCount > 1) {
656655
const splitEvents = [];
657656
let midnight
658-
= moment(event.startDate, "x")
657+
= eventStartDateMoment
659658
.clone()
660659
.startOf("day")
661660
.add(1, "day")
662-
.endOf("day")
663-
.format("x");
661+
.endOf("day");
664662
let count = 1;
665-
while (event.endDate > midnight) {
663+
while (eventEndDateMoment.isAfter(midnight)) {
666664
const thisEvent = JSON.parse(JSON.stringify(event)); // clone object
667-
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + ONE_DAY;
668-
thisEvent.tomorrow = !thisEvent.today && thisEvent.startDate >= today + ONE_DAY && thisEvent.startDate < today + 2 * ONE_DAY;
669-
thisEvent.endDate = moment(midnight, "x").clone().subtract(1, "day").format("x");
665+
thisEvent.today = this.timestampToMoment(thisEvent.startDate).isSame(now, "d");
666+
thisEvent.tomorrow = this.timestampToMoment(thisEvent.startDate).isSame(now.clone().add(1, "days"), "d");
667+
thisEvent.endDate = midnight.clone().subtract(1, "day").unix();
670668
thisEvent.title += ` (${count}/${maxCount})`;
671669
splitEvents.push(thisEvent);
672670

673-
event.startDate = midnight;
671+
event.startDate = midnight.unix();
674672
count += 1;
675-
midnight = moment(midnight, "x").add(1, "day").endOf("day").format("x"); // next day
673+
midnight = midnight.clone().add(1, "day").endOf("day").unix(); // next day
676674
}
677675
// Last day
678676
event.title += ` (${count}/${maxCount})`;
679-
event.today += event.startDate >= today && event.startDate < today + ONE_DAY;
680-
event.tomorrow = !event.today && event.startDate >= today + ONE_DAY && event.startDate < today + 2 * ONE_DAY;
677+
event.today += this.timestampToMoment(event.startDate).isSame(now, "d");
678+
event.tomorrow = this.timestampToMoment(event.startDate).isSame(now.clone().add(1, "days"), "d");
681679
splitEvents.push(event);
682680

683681
for (let splitEvent of splitEvents) {
684-
if (splitEvent.endDate > now && splitEvent.endDate <= future) {
682+
if (this.timestampToMoment(splitEvent.endDate).isAfter(now) && this.timestampToMoment(splitEvent.endDate).isSameOrBefore(future)) {
685683
by_url_calevents.push(splitEvent);
686684
}
687685
}
@@ -716,16 +714,16 @@ Module.register("calendar", {
716714
*/
717715
if (this.config.limitDays > 0) {
718716
let newEvents = [];
719-
let lastDate = today.clone().subtract(1, "days").format("YYYYMMDD");
717+
let lastDate = today.clone().subtract(1, "days");
720718
let days = 0;
721719
for (const ev of events) {
722-
let eventDate = moment(ev.startDate, "x").format("YYYYMMDD");
720+
let eventDate = this.timestampToMoment(ev.startDate);
723721

724722
/*
725723
* if date of event is later than lastdate
726724
* check if we already are showing max unique days
727725
*/
728-
if (eventDate > lastDate) {
726+
if (eventDate.isAfter(lastDate)) {
729727
// if the only entry in the first day is a full day event that day is not counted as unique
730728
if (!this.config.limitDaysNeverSkip && newEvents.length === 1 && days === 1 && newEvents[0].fullDayEvent) {
731729
days--;

modules/default/calendar/calendarfetcherutils.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ const CalendarFetcherUtils = {
253253
Log.debug(`saving event: ${recurrenceTitle}`);
254254
newEvents.push({
255255
title: recurrenceTitle,
256-
startDate: recurringEventStartMoment.format("x"),
257-
endDate: recurringEventEndMoment.format("x"),
256+
startDate: recurringEventStartMoment.unix(),
257+
endDate: recurringEventEndMoment.unix(),
258258
fullDayEvent: CalendarFetcherUtils.isFullDayEvent(event),
259259
recurringEvent: true,
260260
class: event.class,
@@ -308,8 +308,8 @@ const CalendarFetcherUtils = {
308308
// Every thing is good. Add it to the list.
309309
newEvents.push({
310310
title: title,
311-
startDate: eventStartMoment.format("x"),
312-
endDate: eventEndMoment.format("x"),
311+
startDate: eventStartMoment.unix(),
312+
endDate: eventEndMoment.unix(),
313313
fullDayEvent: fullDayEvent,
314314
recurringEvent: false,
315315
class: event.class,

tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,19 @@ END:VEVENT`);
7272

7373
const filteredEvents = CalendarFetcherUtils.filterEvents(data, defaultConfig);
7474

75-
const januaryFirst = filteredEvents.filter((event) => moment.unix(event.startDate / 1000).format("MM-DD") === "01-01");
76-
const julyFirst = filteredEvents.filter((event) => moment.unix(event.startDate / 1000).format("MM-DD") === "07-01");
75+
const januaryFirst = filteredEvents.filter((event) => moment.unix(event.startDate).format("MM-DD") === "01-01");
76+
const julyFirst = filteredEvents.filter((event) => moment.unix(event.startDate).format("MM-DD") === "07-01");
7777

78-
let januaryMoment = moment(`${moment.unix(januaryFirst[0].startDate / 1000).format("YYYY")}-01-01T09:00:00`)
78+
let januaryMoment = moment(`${moment.unix(januaryFirst[0].startDate).format("YYYY")}-01-01T09:00:00`)
7979
.tz("Europe/Amsterdam", true) // Convert to Europe/Amsterdam timezone (see event ical) but keep 9 o'clock
8080
.tz(moment.tz.guess()); // Convert to guessed timezone as that is used in the filterEvents
8181

82-
let julyMoment = moment(`${moment.unix(julyFirst[0].startDate / 1000).format("YYYY")}-07-01T09:00:00`)
82+
let julyMoment = moment(`${moment.unix(julyFirst[0].startDate).format("YYYY")}-07-01T09:00:00`)
8383
.tz("Europe/Amsterdam", true) // Convert to Europe/Amsterdam timezone (see event ical) but keep 9 o'clock
8484
.tz(moment.tz.guess()); // Convert to guessed timezone as that is used in the filterEvents
8585

86-
expect(januaryFirst[0].startDate).toEqual(januaryMoment.format("x"));
87-
expect(julyFirst[0].startDate).toEqual(julyMoment.format("x"));
86+
expect(januaryFirst[0].startDate).toEqual(januaryMoment.unix());
87+
expect(julyFirst[0].startDate).toEqual(julyMoment.unix());
8888
});
8989

9090
it("should return the correct moments based on the timezone given", () => {

0 commit comments

Comments
 (0)