Skip to content

Schedule that crosses midnight shows shouldBeOn=false during AM hours (typo: ytimes.endTime mis-assigned to ytimes.startTime) #1190

@pawmmm

Description

@pawmmm

Summary

In controller/State.ts, the "still running from yesterday" branch of ScheduleTime.calcScheduleDate assigns times.endTime = ytimes.startTime instead of ytimes.endTime. The result is a window of zero duration ending in the past, which collapses shouldBeOn to false until tm finally enters today's ttimes window.

The symptom is most visible with a schedule whose run window spans local midnight (for example a 12:00 PM → 12:00 PM schedule, which the engine synthesizes to today-noon → tomorrow-noon via the endTime <= startTime wraparound at line 1276). In the AM hours, tm is past today's start-of-day but not yet inside today's ttimes; it does fall inside ytimes (yesterday-noon → today-noon-plus-59s). The yesterday-branch is taken and clobbers endTime with startTime.

Affected code

controller/State.ts, lines ~1307–1314 (commit 8961410):

// First check if we are still running yesterday.  This will ensure we have
// the first runtime.
if (fnInRange(tm, ytimes)) {
    // Check the dow.
    let sd = schedDays.find(elem => elem.dow === ytimes.startTime.getDay());
    if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
        times.startTime = ytimes.startTime;
        times.endTime = ytimes.startTime;   // ← should be ytimes.endTime
        return times;
    }
}

Compare with the today-branch (line 1301: times.endTime = ttimes.endTime) and the tomorrow-branch (line 1331: times.endTime = ntimes.endTime). Only the yesterday-branch is broken.

Trace (Schedule 12:00 PM → 12:00 PM, all 7 days, evaluated at Wed 09:28)

  1. calcSchedule recalcs because sod (Wed 00:00) !== dtCalc (Tue 00:00).
  2. calcScheduleDate(Wed 09:28, B) computes:
    • ttimes = Wed 12:00 → Thu 12:00:59.999
    • ytimes = Tue 12:00 → Wed 12:00:59.999
  3. Check 1 (tm in ttimes): Wed 09:28 < Wed 12:00 → miss.
  4. Check 2 (tm in ytimes): Tue 12:00 ≤ Wed 09:28 ≤ Wed 12:00:59.999 → hit. Bug fires: returns { Tue 12:00, Tue 12:00 }.
  5. Back in calcSchedule:1365, times.endTime (Tue 12:00) > currentTime (Wed 09:28) → false → falls into fast-forward else-branch.
  6. The else-branch eventually re-enters calcScheduleDate with tm = Thu 00:00; that call also lands in ytimes (Wed 12:00 → Thu 12:00:59.999) and the bug fires again, leaving this.startTime = this.endTime = Wed 12:00.
  7. _calcShouldBeOn(Wed 09:28): time < tmStart → returns false.

Result: the schedule shows greyed/inactive in dashPanel from local midnight until today's ttimes.startTime (noon in this example), at which point Check 1 finally succeeds and the window normalizes.

The bug is in the schedule-time math only; it does not by itself turn a circuit off (the trigger loop ORs shouldBeOn across all schedules on the circuit), but it makes overlapping/handoff schedules display wrong and silently disables single midnight-crossing schedules during the AM portion of their window.

Reproduction

  1. Create a single schedule with start 12:00 PM and end 12:00 PM (any day mask that includes the previous and current local day).
  2. Wait until the next morning (any time strictly between local midnight and noon).
  3. Observe in dashPanel: the schedule's tile shows inactive even though its synthesized 24-hour window covers the current moment.

Proposed fix

One-character correction on line 1312:

times.endTime = ytimes.endTime;

I'll open a PR with this fix.

Environment

  • njspc commit 8961410 (master, 2026-05-15)
  • Discovered while diagnosing related schedule rollover behavior with @pawmmm

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions