fix: business hours closing for one minute a day
Proposed changes
Work hours are configured at minute granularity, but filterBusinessHoursThatMustBeOpened compared the finish time as exclusive. Consecutive daily windows therefore never met: a 00:00–23:59 business hour — the only way the admin UI can express "always open" — leaves the service closed for the whole 23:59 minute, every day.
During that minute:
- agents cannot become available (
error-business-hours-are-closedfromlivechat/agent.status) - saving a business hour strips
openBusinessHoursfrom every agent - the Livechat widget renders the offline form instead of the registration form
A work hour now stays open until the end of its finish minute:
const afterFinishMinute = localTimeFinish.clone().add(1, 'minute');
return currentTime.isSameOrAfter(localTimeStart) && currentTime.isBefore(afterFinishMinute);
The clone happens after the existing week-shift adjustments, so that logic is untouched.
How this was found
CI run 31729126298 failed on Test API Livechat (EE) and (FIPS), both on a single assertion in 19-business-hours.ts:154 (should allow agents to be available). The job timings pin it exactly:
| job | finished | result |
|---|---|---|
| Livechat API (CE) | 18:25:11 | :white_check_mark: |
| Livechat API (EE) | 18:29:58 | :x: |
| Livechat API (FIPS) | 18:30:06 | :x: |
That test inherits a default business hour saved as 00:00–23:59 in Asia/Kolkata, which on a UTC server runs Saturday 18:30 → Sunday 18:29. EE and FIPS ran the assertion inside the resulting 18:29:00–18:29:59 UTC hole; CE ran four minutes earlier and passed. Same commit, different wall clock.
Reviewer notes
This widens every business hour by one minute. The default 08:00–20:00 now stays open through 20:00:59. That is inherent to "finish is inclusive of its minute", and it is asserted explicitly by one of the new tests rather than left implicit. The alternative considered was special-casing finish.time === '23:59' as end-of-day — no collateral effect, but a magic constant. Happy to switch if reviewers prefer that.
This does not fully fix the 24/7 case. findHoursToScheduleJobs schedules cron jobs at exactly start.cron.time and finish.cron.time, so a 00:00–23:59 config still gets a close job at 18:29 and an open job at 18:30 — the cron will still strip openBusinessHours for that minute. This PR fixes the point-in-time check, which is what runs on business-hour save and drives agent.status. Making the cron skip a close that is immediately followed by an open is a separate, larger change and deserves its own issue.
Marginal effect on an existing test. should not allow a user to be available if BH are closed uses a 00:00–00:01 Monday window in America/Sao_Paulo; its open window grows from 03:00:00–03:00:59 to 03:00:00–03:01:59 UTC. Still a ~2-minutes-per-week exposure, same flake class as before.
Testing
Three tests added to filterBusinessHoursThatMustBeOpened.spec.ts, using the file's existing jest.useFakeTimers() style:
| test | before | after |
|---|---|---|
Kolkata 00:00–23:59 @ Sun 18:29:30 UTC (the hole) | :x: | :white_check_mark: |
| same window @ Sun 18:30:30 UTC (past the window) | :white_check_mark: | :white_check_mark: |
UTC 08:00–20:00 @ Mon 20:00:30 | :x: | :white_check_mark: |
All 6 tests in the file pass; the 3 pre-existing ones are unchanged. The two new positive tests were verified to fail against unmodified source — the middle test is the guard proving the window widens by exactly one minute and no more.
🤖 Generated with Claude Code
Summary by CodeRabbit
- Bug Fixes
- Business-hour schedules now remain open throughout their configured closing minute.
- Fixed all-day schedules, such as 00:00–23:59, incorrectly closing at 23:59.
- Added coverage for finish-time behavior across time zones and same-day schedules.