mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 17:03:53 +00:00
fix(timer): keep day offset on count-to-end scheduled end
getExpectedEnd compared the offset-laden expectedStart against a raw `timeStart + duration + delay` planned end. That planned end omits the `(dayOffset - currentDay) * dayInMs` day shift that expectedStart already carries, so for a count-to-end event on a later day the Math.max guard returned the day-shifted start instead of the day-shifted end (wrong by one duration). Compute the scheduled end in the same day-normalised space before the guard. The guard itself (finish on schedule unless the start is compromised) is unchanged. Add unit tests for the multi-day case, the compromised-start guard, and relative mode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ACGuFWt5aN7Fv3AkYXgxLm
This commit is contained in:
@@ -381,4 +381,57 @@ describe('getExpectedEnd()', () => {
|
||||
|
||||
expect(getExpectedEnd(testEvent, { ...baseState, offset: 0 })).toBe(timeStart + duration);
|
||||
});
|
||||
|
||||
test('a countToEnd event drifts when the start is compromised', () => {
|
||||
const testEvent = {
|
||||
timeStart: 100,
|
||||
duration: 50,
|
||||
delay: 0,
|
||||
dayOffset: 0 as Day,
|
||||
countToEnd: true,
|
||||
};
|
||||
|
||||
// the offset pushes the start (160) past the scheduled end (150) so it can no longer
|
||||
// finish on schedule - the end follows the compromised start
|
||||
expect(getExpectedEnd(testEvent, { ...baseState, offset: 60 })).toBe(160);
|
||||
});
|
||||
|
||||
test('a countToEnd event on a later day keeps the day offset on the end', () => {
|
||||
const testEvent = {
|
||||
timeStart: 100,
|
||||
duration: 50,
|
||||
delay: 0,
|
||||
dayOffset: 1 as Day,
|
||||
countToEnd: true,
|
||||
};
|
||||
|
||||
// the scheduled end must include the day offset (delayedStart + dayInMs + duration),
|
||||
// not collapse to the day-shifted start
|
||||
expect(getExpectedEnd(testEvent, { ...baseState, currentDay: 0, offset: 0 })).toBe(150 + dayInMs);
|
||||
// when the running event is already on the same day, no extra day is added
|
||||
expect(getExpectedEnd({ ...testEvent, dayOffset: 0 as Day }, { ...baseState, currentDay: 0, offset: 0 })).toBe(150);
|
||||
});
|
||||
|
||||
test('a countToEnd event is anchored to its wall-clock end in relative mode', () => {
|
||||
const testEvent = {
|
||||
timeStart: 100,
|
||||
duration: 50,
|
||||
delay: 0,
|
||||
dayOffset: 0 as Day,
|
||||
countToEnd: true,
|
||||
};
|
||||
|
||||
const relativeState = {
|
||||
...baseState,
|
||||
mode: OffsetMode.Relative,
|
||||
actualStart: 30,
|
||||
plannedStart: 0,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
// a regular event in the same state is shifted by the relative-start offset to 180
|
||||
expect(getExpectedEnd({ ...testEvent, countToEnd: false }, relativeState)).toBe(180);
|
||||
// the countToEnd event stays pinned to its wall-clock end (150), not shifted
|
||||
expect(getExpectedEnd(testEvent, relativeState)).toBe(150);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,9 +85,12 @@ export function getExpectedEnd(
|
||||
|
||||
// count to end events should finish on schedule unlesss the start is compromised
|
||||
if (event.countToEnd) {
|
||||
// count to end take scheduled delays into consideration
|
||||
const plannedEnd = event.timeStart + event.duration + event.delay;
|
||||
return Math.max(expectedStart, plannedEnd);
|
||||
// the scheduled end, normalised to the same day-space as expectedStart
|
||||
// (a raw timeStart + duration would miss the day offset on multi-day rundowns)
|
||||
const delayedStart = Math.max(0, event.timeStart + event.delay);
|
||||
const relativeDayOffset = event.dayOffset - state.currentDay;
|
||||
const scheduledEnd = delayedStart + relativeDayOffset * dayInMs + event.duration;
|
||||
return Math.max(expectedStart, scheduledEnd);
|
||||
}
|
||||
|
||||
return expectedStart + event.duration;
|
||||
|
||||
Reference in New Issue
Block a user