Resources Numerical accuracy Calculation policy

Shift calendar working time

The hours a production line is on in a weekly shift calendar, used by the material flow simulation.

How the precision is kept

Times are whole seconds (hours and minutes converted to integers), so every sum and comparison is exact in 64-bit numbers up to the integers involved.

Calculations under this policy

3 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

addons/flow/lib/calendar.js ModelCraft 0.8.1 64-bit 3 calculations Reviewed

Tests None found by the review

Time of day to seconds

Exact

Turns HH:MM text into seconds.

seconds = h × 3600 + m × 60

Integer arithmetic on whole hours and minutes. 24:00 is allowed and means the end of the day.

  • addons/flow/lib/calendar.js
  • lines 24 to 29
  • ModelCraft 0.8.1
export function parseHHMM(text) {
  const m = /^(\d{1,2}):(\d{2})$/.exec(String(text ?? '').trim());
  if (!m) return null;
  const h = Number(m[1]), mi = Number(m[2]);
  return h <= 24 && mi < 60 && !(h === 24 && mi) ? h * 3600 + mi * 60 : null;
}

Weekly on-intervals

Exact

The merged list of on-intervals in a week, with windows that pass midnight wrapped.

length = to − from (+ one day if the end is not after the start)

All values are whole seconds. An interval running past the end of Sunday is split at the end of the week and the rest starts at 0.

  • addons/flow/lib/calendar.js
  • lines 46 to 63
  • ModelCraft 0.8.1
/** The weekly "on" intervals in seconds, merged, within [0, WEEK] (a window past Sunday midnight wraps to the start). */
function weekIntervals(spec) {
  const iv = [];
  for (const w of spec.windows || []) {
    const a = parseHHMM(w.from), b = parseHHMM(w.to);
    if (a === null || b === null) continue;
    for (const d of w.days) {
      const s = d * DAY + a;
      const len = b > a ? b - a : b === a ? DAY : DAY - a + b;
      const e = s + len;
      if (e <= WEEK) iv.push([s, e]); else { iv.push([s, WEEK]); iv.push([0, e - WEEK]); }
    }
  }
  iv.sort((x, y) => x[0] - y[0]);
  const out = [];
  for (const x of iv) { const l = out[out.length - 1]; if (l && x[0] <= l[1]) l[1] = Math.max(l[1], x[1]); else out.push([x[0], x[1]]); }
  return out;
}

On time between two instants

64-bit

The total seconds the calendar is on between two times.

Σ (off − on) over the on-intervals inside [a, b]

Each term is a difference of two interval ends. With integer seconds in a, b and the intervals the sum is exact; a fractional simulation time gives an ordinary 64-bit sum.

  • addons/flow/lib/calendar.js
  • lines 93 to 105
  • ModelCraft 0.8.1
const onTime = (a, b) => {
  if (b <= a) return 0;
  if (always) return b - a;
  if (never) return 0;
  let sum = 0, t = a;
  for (let guard = 0; t < b && guard < 1e6; guard++) {
    const on = nextOn(t);
    if (on >= b) break;
    const off = Math.min(nextOff(on), b);
    sum += off - on; t = off;
  }
  return sum;
};

In detail

Method
Weekly on-intervals merged and unrolled over two weeks; working time between two times is summed interval by interval.
Number types
Integer seconds held in 64-bit numbers; Infinity for never.
Tolerances
None; the loop in onTime stops after 1e6 steps.
Limits
If the loop in onTime reaches 1e6 steps the sum so far is returned.

Approach: Exact values

Constants and factors are held as exact fractions from their definitions. The arithmetic is ordered so that no rounded factor is ever used.

Every policy that takes this approach