Files
everythingbutwork/apps/api/src/lib/streak.ts
T
dejanandClaude Opus 4.8 bcae44eeb3
ci / quality (push) Failing after 38s
ci / build (push) Has been skipped
deploy / deploy (push) Successful in 55s
fix(streak): Feiertage, Wochenend-Edge & Lookback-Deckel
- Feiertage: isWerktag berücksichtigt jetzt die 9 bundesweiten gesetzlichen
  Feiertage (Oster-Computus + feste Tage) → ein Feiertag bricht die Streak
  nicht mehr (zählt wie ein Wochenende neutral). Landesspezifische Feiertage
  bewusst (noch) nicht enthalten.
- Wochenend-Edge: Reflexion an Sa/So setzte die angezeigte Streak auf 0
  (Schleife brach am Nicht-Werktag ab). Jetzt wird übersprungen statt gebrochen.
- Lookback von 120 auf 400 Tage erhöht, damit lange Streaks nicht
  abgeschnitten werden.
- Streak-Zähllogik in reine, getestete countStreakDays() ausgelagert
  (8 Streak- + 5 Feiertags-Tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 15:42:25 +02:00

48 lines
1.5 KiB
TypeScript

import { isWerktag, previousWerktag } from './werktag.js';
/**
* Zählt aufeinanderfolgende Werktage (Mo-Fr, ohne Feiertage) mit voller
* Reflexion, rückwärts ab `today`.
*
* Regeln:
* - Nur Tage in `fullDates` (status = 'full') zählen.
* - Tage in `absenceDates` (Urlaub/Krank/…) sind neutral: überspringen, nicht
* zählen, nicht brechen.
* - Wochenenden und Feiertage sind keine Werktage → übersprungen.
* - Heute ist Schonfrist: ist heute noch nicht voll, wird ab dem vorherigen
* Werktag gezählt (die laufende Streak bleibt über den Tag erhalten).
* - Bricht beim ersten Werktag, der weder voll noch Abwesenheit ist.
*/
export function countStreakDays(opts: {
today: string;
fullDates: Set<string>;
absenceDates: Set<string>;
}): number {
const { today, fullDates, absenceDates } = opts;
let cursor = today;
let streak = 0;
// Heute noch nicht reflektiert → ab vorherigem Werktag zählen.
if (!fullDates.has(cursor)) cursor = previousWerktag(today);
while (true) {
// Nicht-Werktag (z. B. an einem Wochenende/Feiertag reflektiert) überspringen,
// statt die Streak fälschlich abzubrechen.
if (!isWerktag(cursor)) {
cursor = previousWerktag(cursor);
continue;
}
if (absenceDates.has(cursor)) {
cursor = previousWerktag(cursor);
continue;
}
if (fullDates.has(cursor)) {
streak++;
cursor = previousWerktag(cursor);
continue;
}
break;
}
return streak;
}