From eabade9ba7ca345cff0f75bf45ea33c17f382c54 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Thu, 25 Sep 2025 15:57:13 +0200 Subject: [PATCH] fixing working hours if they go to next day --- lib/utils.js | 48 +++++++++++++++++++++++++++---------- test/provider/utils.test.js | 15 ++++++++++++ 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 7f9c3bd..e440796 100755 --- a/lib/utils.js +++ b/lib/utils.js @@ -109,11 +109,22 @@ function timeStringToMs(timeString, now) { } /** - * Check whether current time is within configured working hours, or no hours are set. - * If working hours are missing or incomplete, returns true. - * @param {{workingHours?: {from?: string, to?: string}}} config - * @param {number} now - Epoch ms - * @returns {boolean} + * Determine whether the given timestamp is within the configured working hours, or return true when the window is not set. + * - If workingHours is missing or either 'from' or 'to' is empty/null, returns true. + * - Supports windows that cross midnight (e.g., from '23:00' to '06:00'). + * + * Time parsing is based on the local timezone of the running process. + * + * @param {{workingHours?: {from?: string|null, to?: string|null}}} config - Configuration object containing working hours in 'HH:mm' format. + * @param {number} now - Epoch milliseconds to evaluate. + * @returns {boolean} True when execution is allowed at 'now'. + * @example + * // Same-day window + * duringWorkingHoursOrNotSet({ workingHours: { from: '08:00', to: '17:00' } }, someTime); + * @example + * // Window crossing midnight + * // For { from: '05:00', to: '00:30' } → 23:00 => true, 01:00 => false, 06:00 => true + * duringWorkingHoursOrNotSet({ workingHours: { from: '05:00', to: '00:30' } }, Date.now()); */ function duringWorkingHoursOrNotSet(config, now) { const { workingHours } = config; @@ -122,7 +133,20 @@ function duringWorkingHoursOrNotSet(config, now) { } const toDate = timeStringToMs(workingHours.to, now); const fromDate = timeStringToMs(workingHours.from, now); - return fromDate <= now && toDate >= now; + + // If parsing fails (e.g., malformed time), be lenient and allow. + if (isNaN(toDate) || isNaN(fromDate)) { + return true; + } + + if (toDate >= fromDate) { + // Same-day window (e.g., 08:00 - 17:00) + return now >= fromDate && now <= toDate; + } + + // Window crosses midnight (e.g., 05:00 -> 00:30 next day) + // Accept if we are after 'from' today OR before 'to' today (which represents next day's cutoff). + return now >= fromDate || now <= toDate; } /** @@ -244,13 +268,13 @@ function sleep(ms) { } /** - * returns a random into between start and end - * @param a start int - * @param b max int - * @returns {*} + * Return a random integer between min and max (inclusive). + * @param {number} min - Minimum integer value. + * @param {number} max - Maximum integer value. + * @returns {number} A random integer N where min <= N <= max. */ -function randomBetween(a, b) { - return Math.floor(Math.random() * (b - a + 1)) + a; +function randomBetween(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; } // Call refreshConfig() from the application entrypoint during startup to populate config. diff --git a/test/provider/utils.test.js b/test/provider/utils.test.js index 8dc9fb3..15fb16c 100644 --- a/test/provider/utils.test.js +++ b/test/provider/utils.test.js @@ -8,6 +8,7 @@ const fakeWorkingHoursConfig = (from, to) => ({ from, }, }); + describe('utils', () => { describe('#isOneOf()', () => { it('should be false', () => { @@ -33,5 +34,19 @@ describe('utils', () => { it('should be true if only from is set', () => { expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', null), 1622026740000)).to.be.true; }); + it('should handle working hours that cross midnight (e.g., 05:00 → 00:30)', () => { + const cfg = fakeWorkingHoursConfig('05:00', '00:30'); + const mkTs = (h, m = 0) => { + const d = new Date(); + d.setHours(h); + d.setMinutes(m); + d.setSeconds(0); + d.setMilliseconds(0); + return d.getTime(); + }; + expect(duringWorkingHoursOrNotSet(cfg, mkTs(23, 0))).to.be.true; // 23:00 => within window + expect(duringWorkingHoursOrNotSet(cfg, mkTs(1, 0))).to.be.false; // 01:00 => outside window + expect(duringWorkingHoursOrNotSet(cfg, mkTs(6, 0))).to.be.true; // 06:00 => within window + }); }); });