impl/typescript.ts
1,415 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { daysBetween } from "./dates_days_between.ts"; ← from dates.days-between ^1.0.0 · built alongside by fune
import { meterAdvance } from "./energy_meter_advance.ts"; ← from energy.meter-advance ^1.0.0 · built alongside by fune
import { type RoundingMode, roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { type EstimatedRead, type MeterRead } from "./energy_estimate_read_types.ts";
/**
* Carry the latest read forward to `onDate` at the average daily consumption
* between the two reads, rounding once, and wrap past all nines.
*/
export function estimateRead(
earlierRead: MeterRead,
latestRead: MeterRead,
onDate: string,
digits: number,
mode: RoundingMode
): EstimatedRead {
const consumption = meterAdvance(earlierRead.value, latestRead.value, digits);
const history = daysBetween(earlierRead.date, latestRead.date);
if (history <= 0) {
throw new RangeError(`the earlier read must be dated before the latest read, received ${earlierRead.date} and ${latestRead.date}`);
}
const ahead = daysBetween(latestRead.date, onDate);
if (ahead < 0) {
throw new RangeError(`onDate must not be before the latest read, received ${onDate} and ${latestRead.date}`);
}
const product = consumption * ahead;
if (product > Number.MAX_SAFE_INTEGER) {
throw new RangeError("estimate too large to calculate exactly");
}
const units = roundDiv(product, history, mode);
const span = 10 ** digits;
const raw = latestRead.value + units;
return { read: raw % span, units, rolledOver: raw >= span };
}