Skip to content

Commit 5c6c811

Browse files
committedAug 14, 2024
Merge branch 'develop'
2 parents 9c9917c + 157997d commit 5c6c811

File tree

5 files changed

+92
-45
lines changed

5 files changed

+92
-45
lines changed
 

‎CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44

55
## Unreleased
66

7+
## [2.0.4] - 2024-08-14
8+
9+
- [#128](https://github.com/os2display/display-client/pull/128)
10+
- Fixed rrule evaluation to handle local time correctly.
11+
712
## [2.0.3] - 2024-05-21
813

914
- [#125](https://github.com/os2display/display-client/pull/125)

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"react-dom": "^18.2.0",
2020
"react-intl": "^5.20.2",
2121
"react-transition-group": "^4.4.2",
22-
"rrule": "^2.6.8",
22+
"rrule": "^2.7.2",
2323
"sass": "^1.37.5",
2424
"styled-components": "^5.3.1",
2525
"suncalc": "^1.9.0",

‎src/schedule.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { RRule } from 'rrule';
2+
3+
class ScheduleUtils {
4+
static occursNow(rruleString, durationSeconds) {
5+
const rrule = RRule.fromString(rruleString.replace("\\n", "\n"));
6+
const duration = durationSeconds * 1000;
7+
8+
const now = new Date();
9+
10+
// From the RRULE docs:_ "Returned "UTC" dates are always meant to be
11+
// interpreted as dates in your local timezone. This may mean you have to
12+
// do additional conversion to get the "correct" local time with offset
13+
// applied."
14+
//
15+
// We do the opposite to ensure that datetime comparisons works as expected.
16+
// For evaluation with the RRule library we pretend that "now" is in UTC instead of the local timezone.
17+
// That is 9:00 in Europe/Copenhagen time will be evaluated as if it was 9:00 in UTC.
18+
// @see https://github.com/jkbrzt/rrule#important-use-utc-dates
19+
const nowWithoutTimezone = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds()));
20+
21+
// Subtract duration from now to make sure all relevant occurrences are considered.
22+
const from = new Date(nowWithoutTimezone.getTime() - duration);
23+
24+
let occurs = false;
25+
26+
// RRule.prototype.between(after, before, inc=false [, iterator])
27+
// The between() function expects "after" and "before" to be in pretend UTC as
28+
// described above.
29+
rrule.between(
30+
from,
31+
nowWithoutTimezone,
32+
true,
33+
function iterator(occurrenceDate) {
34+
// The "ccurrenceDate" we are iterating over contains a "pretend UTC" datetime
35+
// object. As above, if the time for "occurrenceDate" is 09:00 UTC it should be
36+
// treated as 09:00 local time regardsless of the actual local timezone
37+
const end = new Date(occurrenceDate.getTime() + duration);
38+
39+
if (nowWithoutTimezone >= occurrenceDate && nowWithoutTimezone <= end) {
40+
occurs = true;
41+
// break iteration.
42+
return false;
43+
}
44+
45+
// continue iteration.
46+
return true;
47+
}
48+
);
49+
50+
return occurs;
51+
}
52+
}
53+
54+
export default ScheduleUtils;

‎src/service/scheduleService.js

+21-31
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import cloneDeep from 'lodash.clonedeep';
22
import sha256 from 'crypto-js/sha256';
33
import Md5 from 'crypto-js/md5';
4-
import RRule from 'rrule';
4+
import { RRule, datetime } from 'rrule';
55
import Base64 from 'crypto-js/enc-base64';
66
import isPublished from '../util/isPublished';
77
import Logger from '../logger/logger';
88
import ConfigLoader from '../config-loader';
9+
import ScheduleUtils from "../schedule";
910

1011
/**
1112
* ScheduleService.
@@ -174,49 +175,36 @@ class ScheduleService {
174175
static findScheduledSlides(playlists, regionId) {
175176
const slides = [];
176177

177-
const now = new Date();
178-
const startOfDay = new Date();
179-
startOfDay.setUTCHours(0, 0, 0, 0);
180-
181178
playlists.forEach((playlist) => {
182179
const { schedules } = playlist;
183180

184181
if (!isPublished(playlist?.published)) {
185182
return;
186183
}
187184

188-
let occurs = true;
185+
let active = true;
189186

190187
// If schedules are set for the playlist, do not show playlist unless a schedule is active.
191188
if (schedules.length > 0) {
192-
occurs = false;
193-
194-
schedules.forEach((schedule) => {
195-
const rrule = RRule.fromString(schedule.rrule.replace('\\n', '\n'));
196-
rrule.between(
197-
// Subtract duration from now to make sure all relevant occurrences are considered.
198-
new Date(
199-
now.getTime() - (schedule.duration ? schedule.duration * 1000 : 0)
200-
),
201-
now,
202-
true,
203-
function iterator(occurrenceDate) {
204-
const occurrenceEnd = new Date(
205-
occurrenceDate.getTime() + schedule.duration * 1000
206-
);
207-
208-
if (now >= occurrenceDate && now <= occurrenceEnd) {
209-
occurs = true;
210-
// Break the iteration.
211-
return false;
212-
}
213-
return true;
214-
}
215-
);
189+
active = false;
190+
191+
// Run through all schedule item and see if it occurs now. If one or more occur now, the playlist is active.
192+
schedules.every((schedule) => {
193+
const scheduleOccurs = ScheduleUtils.occursNow(schedule.rrule, schedule.duration);
194+
195+
if (scheduleOccurs) {
196+
active = true;
197+
198+
// Break iteration.
199+
return false;
200+
}
201+
202+
// Continue iteration.
203+
return true;
216204
});
217205
}
218206

219-
if (occurs) {
207+
if (active) {
220208
playlist?.slidesData?.forEach((slide) => {
221209
if (!isPublished(slide.published)) {
222210
return;
@@ -229,6 +217,8 @@ class ScheduleService {
229217
newSlide.executionId = `EXE-ID-${executionId}`;
230218
slides.push(newSlide);
231219
});
220+
} else {
221+
Logger.log('info', `Playlist ${playlist['@id']} not scheduled for now`);
232222
}
233223
});
234224

‎yarn.lock

+11-13
Original file line numberDiff line numberDiff line change
@@ -7628,11 +7628,6 @@ lru-cache@^6.0.0:
76287628
dependencies:
76297629
yallist "^4.0.0"
76307630

7631-
luxon@^1.21.3:
7632-
version "1.28.0"
7633-
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
7634-
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==
7635-
76367631
lz-string@^1.4.4:
76377632
version "1.4.4"
76387633
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
@@ -10366,14 +10361,12 @@ rollup@^1.31.1:
1036610361
"@types/node" "*"
1036710362
acorn "^7.1.0"
1036810363

10369-
rrule@^2.6.8:
10370-
version "2.6.8"
10371-
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.6.8.tgz#c61714f246e7676e8efa16c2baabac199f20f6db"
10372-
integrity sha512-cUaXuUPrz9d1wdyzHsBfT1hptKlGgABeCINFXFvulEPqh9Np9BnF3C3lrv9uO54IIr8VDb58tsSF3LhsW+4VRw==
10364+
rrule@^2.7.2:
10365+
version "2.8.1"
10366+
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.8.1.tgz#e8341a9ce3e68ce5b8da4d502e893cd9f286805e"
10367+
integrity sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==
1037310368
dependencies:
10374-
tslib "^1.10.0"
10375-
optionalDependencies:
10376-
luxon "^1.21.3"
10369+
tslib "^2.4.0"
1037710370

1037810371
rsvp@^4.8.4:
1037910372
version "4.8.5"
@@ -11595,7 +11588,7 @@ tsconfig-paths@^3.9.0:
1159511588
minimist "^1.2.0"
1159611589
strip-bom "^3.0.0"
1159711590

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

11601+
tslib@^2.4.0:
11602+
version "2.6.3"
11603+
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
11604+
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
11605+
1160811606
tsutils@^3.17.1, tsutils@^3.21.0:
1160911607
version "3.21.0"
1161011608
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"

0 commit comments

Comments
 (0)
Please sign in to comment.