logseq-timetracker/src/aggregation.ts
2026-06-26 10:04:47 +02:00

73 lines
2.3 KiB
TypeScript

import type { DaySummary, TimeEntry, WeekSummary } from "./types";
const DAY_MS = 24 * 60 * 60 * 1000;
function dayStartMs(value: number): number {
const d = new Date(value);
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
}
function dayKey(value: number): string {
const d = new Date(value);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
function isoWeekKey(value: number): string {
const d = new Date(value);
const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
const dayNum = date.getUTCDay() || 7;
date.setUTCDate(date.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
const weekNo = Math.ceil((((date.getTime() - yearStart.getTime()) / DAY_MS) + 1) / 7);
return `${date.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
}
export function splitEntryAcrossDays(entry: TimeEntry): DaySummary[] {
const result: DaySummary[] = [];
let cursor = entry.startedAtEpochMs;
const end = entry.endedAtEpochMs;
while (cursor < end) {
const startOfDay = dayStartMs(cursor);
const nextDay = startOfDay + DAY_MS;
const segmentEnd = Math.min(end, nextDay);
result.push({
day: dayKey(cursor),
durationMs: segmentEnd - cursor
});
cursor = segmentEnd;
}
return result;
}
export function computeDaySummaries(entries: TimeEntry[]): DaySummary[] {
const map = new Map<string, number>();
for (const entry of entries) {
for (const segment of splitEntryAcrossDays(entry)) {
map.set(segment.day, (map.get(segment.day) ?? 0) + segment.durationMs);
}
}
return [...map.entries()]
.map(([day, durationMs]) => ({ day, durationMs }))
.sort((a, b) => a.day.localeCompare(b.day));
}
export function computeWeekSummaries(entries: TimeEntry[]): WeekSummary[] {
const daySummaries = computeDaySummaries(entries);
const map = new Map<string, number>();
for (const day of daySummaries) {
const week = isoWeekKey(new Date(`${day.day}T12:00:00`).getTime());
map.set(week, (map.get(week) ?? 0) + day.durationMs);
}
return [...map.entries()]
.map(([week, durationMs]) => ({ week, durationMs }))
.sort((a, b) => a.week.localeCompare(b.week));
}