73 lines
2.3 KiB
TypeScript
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));
|
|
}
|