Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
tuj committed Aug 14, 2024
2 parents 9c9917c + 157997d commit 5c6c811
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 45 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.

## Unreleased

## [2.0.4] - 2024-08-14

- [#128](https://github.com/os2display/display-client/pull/128)
- Fixed rrule evaluation to handle local time correctly.

## [2.0.3] - 2024-05-21

- [#125](https://github.com/os2display/display-client/pull/125)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"react-dom": "^18.2.0",
"react-intl": "^5.20.2",
"react-transition-group": "^4.4.2",
"rrule": "^2.6.8",
"rrule": "^2.7.2",
"sass": "^1.37.5",
"styled-components": "^5.3.1",
"suncalc": "^1.9.0",
Expand Down
54 changes: 54 additions & 0 deletions src/schedule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { RRule } from 'rrule';

class ScheduleUtils {
static occursNow(rruleString, durationSeconds) {
const rrule = RRule.fromString(rruleString.replace("\\n", "\n"));
const duration = durationSeconds * 1000;

const now = new Date();

// From the RRULE docs:_ "Returned "UTC" dates are always meant to be
// interpreted as dates in your local timezone. This may mean you have to
// do additional conversion to get the "correct" local time with offset
// applied."
//
// We do the opposite to ensure that datetime comparisons works as expected.
// For evaluation with the RRule library we pretend that "now" is in UTC instead of the local timezone.
// That is 9:00 in Europe/Copenhagen time will be evaluated as if it was 9:00 in UTC.
// @see https://github.com/jkbrzt/rrule#important-use-utc-dates
const nowWithoutTimezone = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds()));

// Subtract duration from now to make sure all relevant occurrences are considered.
const from = new Date(nowWithoutTimezone.getTime() - duration);

let occurs = false;

// RRule.prototype.between(after, before, inc=false [, iterator])
// The between() function expects "after" and "before" to be in pretend UTC as
// described above.
rrule.between(
from,
nowWithoutTimezone,
true,
function iterator(occurrenceDate) {
// The "ccurrenceDate" we are iterating over contains a "pretend UTC" datetime
// object. As above, if the time for "occurrenceDate" is 09:00 UTC it should be
// treated as 09:00 local time regardsless of the actual local timezone
const end = new Date(occurrenceDate.getTime() + duration);

if (nowWithoutTimezone >= occurrenceDate && nowWithoutTimezone <= end) {
occurs = true;
// break iteration.
return false;
}

// continue iteration.
return true;
}
);

return occurs;
}
}

export default ScheduleUtils;
52 changes: 21 additions & 31 deletions src/service/scheduleService.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import cloneDeep from 'lodash.clonedeep';
import sha256 from 'crypto-js/sha256';
import Md5 from 'crypto-js/md5';
import RRule from 'rrule';
import { RRule, datetime } from 'rrule';
import Base64 from 'crypto-js/enc-base64';
import isPublished from '../util/isPublished';
import Logger from '../logger/logger';
import ConfigLoader from '../config-loader';
import ScheduleUtils from "../schedule";

/**
* ScheduleService.
Expand Down Expand Up @@ -174,49 +175,36 @@ class ScheduleService {
static findScheduledSlides(playlists, regionId) {
const slides = [];

const now = new Date();
const startOfDay = new Date();
startOfDay.setUTCHours(0, 0, 0, 0);

playlists.forEach((playlist) => {
const { schedules } = playlist;

if (!isPublished(playlist?.published)) {
return;
}

let occurs = true;
let active = true;

// If schedules are set for the playlist, do not show playlist unless a schedule is active.
if (schedules.length > 0) {
occurs = false;

schedules.forEach((schedule) => {
const rrule = RRule.fromString(schedule.rrule.replace('\\n', '\n'));
rrule.between(
// Subtract duration from now to make sure all relevant occurrences are considered.
new Date(
now.getTime() - (schedule.duration ? schedule.duration * 1000 : 0)
),
now,
true,
function iterator(occurrenceDate) {
const occurrenceEnd = new Date(
occurrenceDate.getTime() + schedule.duration * 1000
);

if (now >= occurrenceDate && now <= occurrenceEnd) {
occurs = true;
// Break the iteration.
return false;
}
return true;
}
);
active = false;

// Run through all schedule item and see if it occurs now. If one or more occur now, the playlist is active.
schedules.every((schedule) => {
const scheduleOccurs = ScheduleUtils.occursNow(schedule.rrule, schedule.duration);

if (scheduleOccurs) {
active = true;

// Break iteration.
return false;
}

// Continue iteration.
return true;
});
}

if (occurs) {
if (active) {
playlist?.slidesData?.forEach((slide) => {
if (!isPublished(slide.published)) {
return;
Expand All @@ -229,6 +217,8 @@ class ScheduleService {
newSlide.executionId = `EXE-ID-${executionId}`;
slides.push(newSlide);
});
} else {
Logger.log('info', `Playlist ${playlist['@id']} not scheduled for now`);
}
});

Expand Down
24 changes: 11 additions & 13 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7628,11 +7628,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"

luxon@^1.21.3:
version "1.28.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==

lz-string@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
Expand Down Expand Up @@ -10366,14 +10361,12 @@ rollup@^1.31.1:
"@types/node" "*"
acorn "^7.1.0"

rrule@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.6.8.tgz#c61714f246e7676e8efa16c2baabac199f20f6db"
integrity sha512-cUaXuUPrz9d1wdyzHsBfT1hptKlGgABeCINFXFvulEPqh9Np9BnF3C3lrv9uO54IIr8VDb58tsSF3LhsW+4VRw==
rrule@^2.7.2:
version "2.8.1"
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.8.1.tgz#e8341a9ce3e68ce5b8da4d502e893cd9f286805e"
integrity sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==
dependencies:
tslib "^1.10.0"
optionalDependencies:
luxon "^1.21.3"
tslib "^2.4.0"

rsvp@^4.8.4:
version "4.8.5"
Expand Down Expand Up @@ -11595,7 +11588,7 @@ tsconfig-paths@^3.9.0:
minimist "^1.2.0"
strip-bom "^3.0.0"

tslib@^1.10.0, tslib@^1.8.1:
tslib@^1.8.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
Expand All @@ -11605,6 +11598,11 @@ tslib@^2.0.3, tslib@^2.1.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==

tslib@^2.4.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==

tsutils@^3.17.1, tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
Expand Down

0 comments on commit 5c6c811

Please sign in to comment.