Use unix timestamps in timetable logic

Parse the iso date strings into millseconds from the unix epoch and use
that through the timetable logic instead of reparsing the strings over
and over.
This commit is contained in:
Hornwitser 2025-03-09 16:49:57 +01:00
parent c4a6f6b3f9
commit 1ac607a712

View file

@ -75,7 +75,7 @@ const oneMinMs = 60 * 1000;
type Edge = { type: "start" | "end", slot: TimeSlot }; type Edge = { type: "start" | "end", slot: TimeSlot };
/** Point in time where multiple edges meet. */ /** Point in time where multiple edges meet. */
type Junction = { ts: string, edges: Edge[] }; type Junction = { ts: number, edges: Edge[] };
/** Span of time between two adjacent junctions */ /** Span of time between two adjacent junctions */
type Span = { type Span = {
@ -90,8 +90,8 @@ type Span = {
and the endpoint spans are always empty and at least one hour. and the endpoint spans are always empty and at least one hour.
*/ */
type Stretch = { type Stretch = {
start: string, start: number,
end: string, end: number,
spans: Span[]; spans: Span[];
} }
@ -156,9 +156,9 @@ function* edgesFromEvents(events: Iterable<ScheduleEvent>): Generator<Edge> {
} }
function junctionsFromEdges(edges: Iterable<Edge>) { function junctionsFromEdges(edges: Iterable<Edge>) {
const junctions = new Map<string, Junction>(); const junctions = new Map<number, Junction>();
for (const edge of edges) { for (const edge of edges) {
const ts = edge.slot[edge.type]; const ts = Date.parse(edge.slot[edge.type]);
const junction = junctions.get(ts); const junction = junctions.get(ts);
if (junction) { if (junction) {
junction.edges.push(edge); junction.edges.push(edge);
@ -204,14 +204,11 @@ function* spansFromJunctions(
} }
function createStretch(spans: Span[]): Stretch { function createStretch(spans: Span[]): Stretch {
let startTs = Date.parse(spans[0].start.ts) - oneHourMs; let start = spans[0].start.ts - oneHourMs;
let endTs = Date.parse(spans[spans.length - 1].end.ts) + oneHourMs; let end = spans[spans.length - 1].end.ts + oneHourMs;
// Extend stretch to nearest whole hours // Extend stretch to nearest whole hours
startTs = Math.floor(startTs / oneHourMs) * oneHourMs; start = Math.floor(start / oneHourMs) * oneHourMs;
endTs = Math.ceil(endTs / oneHourMs) * oneHourMs; end = Math.ceil(end / oneHourMs) * oneHourMs;
// Convert back to ISO date string
let start = isoStringFromTs(startTs);
let end = isoStringFromTs(endTs);
return { return {
spans: [ spans: [
{ {
@ -237,7 +234,7 @@ function* stretchesFromSpans(spans: Iterable<Span>, minSeparation: number): Gene
// Based on how spans are generated I can assume that an empty span // Based on how spans are generated I can assume that an empty span
// will only occur between two spans with timeslots in them. // will only occur between two spans with timeslots in them.
if (span.locations.size === 0 if (span.locations.size === 0
&& Date.parse(span.end.ts) - Date.parse(span.start.ts) >= minSeparation && span.end.ts - span.start.ts >= minSeparation
) { ) {
yield createStretch(currentSpans); yield createStretch(currentSpans);
currentSpans = []; currentSpans = [];
@ -251,8 +248,8 @@ function* stretchesFromSpans(spans: Iterable<Span>, minSeparation: number): Gene
/** Cuts up a span by whole hours that crosses it */ /** Cuts up a span by whole hours that crosses it */
function* cutSpansByHours(span: Span): Generator<Span> { function* cutSpansByHours(span: Span): Generator<Span> {
const startHour = Date.parse(span.start.ts) / oneHourMs; const startHour = span.start.ts / oneHourMs;
const endHour = Date.parse(span.end.ts) / oneHourMs; const endHour = span.end.ts / oneHourMs;
let currentStart = startHour; let currentStart = startHour;
let currentEnd = Math.min(Math.floor(startHour + 1), endHour); let currentEnd = Math.min(Math.floor(startHour + 1), endHour);
if (currentEnd === endHour) { if (currentEnd === endHour) {
@ -262,22 +259,22 @@ function* cutSpansByHours(span: Span): Generator<Span> {
yield { yield {
start: span.start, start: span.start,
end: { ts: isoStringFromTs(currentEnd * oneHourMs), edges: [] }, end: { ts: currentEnd * oneHourMs, edges: [] },
locations: span.locations, locations: span.locations,
} }
currentStart = currentEnd; currentStart = currentEnd;
while (++currentEnd < endHour) { while (++currentEnd < endHour) {
yield { yield {
start: { ts: isoStringFromTs(currentStart * oneHourMs), edges: [] }, start: { ts: currentStart * oneHourMs, edges: [] },
end: { ts: isoStringFromTs(currentEnd * oneHourMs), edges: [] }, end: { ts: currentEnd * oneHourMs, edges: [] },
locations: span.locations, locations: span.locations,
} }
currentStart += 1; currentStart += 1;
} }
yield { yield {
start: { ts: isoStringFromTs(currentStart * oneHourMs), edges: [] }, start: { ts: currentStart * oneHourMs, edges: [] },
end: span.end, end: span.end,
locations: span.locations, locations: span.locations,
} }
@ -322,14 +319,14 @@ function tableElementsFromStretches(
if (first) { if (first) {
first = false; first = false;
startColumnGroup(); startColumnGroup();
startDay(stretch.start.slice(0, 10)); startDay(isoStringFromTs(stretch.start).slice(0, 10));
startHour(stretch.start.slice(11, 16)); startHour(isoStringFromTs(stretch.start).slice(11, 16));
for(const location of locations) { for(const location of locations) {
startLocation(location.id); startLocation(location.id);
} }
} else { } else {
startColumnGroup("break"); startColumnGroup("break");
const dayName = stretch.start.slice(0, 10) const dayName = isoStringFromTs(stretch.start).slice(0, 10)
const lastDayHeader = dayHeaders[dayHeaders.length - 1] const lastDayHeader = dayHeaders[dayHeaders.length - 1]
const sameDay = dayName === lastDayHeader.content && lastDayHeader.span; const sameDay = dayName === lastDayHeader.content && lastDayHeader.span;
if (!sameDay) if (!sameDay)
@ -343,7 +340,7 @@ function tableElementsFromStretches(
startColumnGroup(); startColumnGroup();
if (!sameDay) if (!sameDay)
startDay(dayName); startDay(dayName);
startHour(stretch.start.slice(11, 16)); startHour(isoStringFromTs(stretch.start).slice(11, 16));
for(const location of locations) { for(const location of locations) {
startLocation(location.id); startLocation(location.id);
} }
@ -351,9 +348,8 @@ function tableElementsFromStretches(
for (const span of stretch.spans) { for (const span of stretch.spans) {
for (const cutSpan of cutSpansByHours(span)) { for (const cutSpan of cutSpansByHours(span)) {
const startTs = Date.parse(cutSpan.start.ts); const end = cutSpan.end.ts;
const endTs = Date.parse(cutSpan.end.ts); const durationMs = end - cutSpan.start.ts;
const durationMs = endTs - startTs;
for (const location of locations) { for (const location of locations) {
const rows = locationRows.get(location.id)!; const rows = locationRows.get(location.id)!;
@ -365,11 +361,11 @@ function tableElementsFromStretches(
} }
pushColumn(durationMs / oneMinMs); pushColumn(durationMs / oneMinMs);
if (endTs % oneDayMs === 0) { if (end % oneDayMs === 0) {
startDay(cutSpan.end.ts.slice(0, 10)); startDay(isoStringFromTs(cutSpan.end.ts).slice(0, 10));
} }
if (endTs % oneHourMs === 0) { if (end % oneHourMs === 0) {
startHour(cutSpan.end.ts.slice(11, 16)); startHour(isoStringFromTs(cutSpan.end.ts).slice(11, 16));
} }
} }
} }