fix(zeit): über Mitternacht laufende Timer bleiben sichtbar (ENTWICKLUN-14)
Ein um 23:00 gestarteter, noch laufender Timer hatte Start-Datum = gestern und fiel nach Mitternacht aus listToday/teamToday (Filter nach Start-Datum=heute) — nur die TimerPill zeigte ihn noch. - listToday/teamToday: Filter „overlapsToday" = heute gestartet ODER heute beendet (Mitternacht überschritten) ODER laufend → Eintrag bleibt sichtbar. - Tages-Summen clampen den Anteil auf den heutigen Tag (berlinDayStartMs, DST-korrekt) → über Mitternacht gelaufene Blöcke werden nicht falsch zugezählt. - ToDo-Titel werden tagesübergreifend serverseitig aufgelöst (time.now + listToday) → Task bleibt benannt, auch wenn das ToDo von gestern ist. - Timeline zeigt das Start-Datum, wenn ein Eintrag nicht heute begonnen hat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
040dd62735
commit
8fd72bcb69
5 files changed
+107
-34
No files matched your search
@@ -1,5 +1,12 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatIsoDate, isWerktag, nextWerktag, previousWerktag, werktageBetween } from './werktag';
|
||||
import {
|
||||
formatIsoDate,
|
||||
isWerktag,
|
||||
nextWerktag,
|
||||
previousWerktag,
|
||||
werktageBetween,
|
||||
berlinDayStartMs,
|
||||
} from './werktag';
|
||||
|
||||
describe('werktag', () => {
|
||||
it('formatIsoDate liefert YYYY-MM-DD in Berlin TZ', () => {
|
||||
@@ -27,4 +34,11 @@ describe('werktag', () => {
|
||||
// Mo 2026-06-08 bis Mo 2026-06-15 (exkl.): 5 Werktage
|
||||
expect(werktageBetween('2026-06-08', '2026-06-15')).toBe(5);
|
||||
});
|
||||
|
||||
it('berlinDayStartMs ist DST-korrekt', () => {
|
||||
// Sommer (CEST, +2): 00:00 Berlin = 22:00 UTC am Vortag
|
||||
expect(berlinDayStartMs('2026-06-06')).toBe(Date.parse('2026-06-05T22:00:00Z'));
|
||||
// Winter (CET, +1): 00:00 Berlin = 23:00 UTC am Vortag
|
||||
expect(berlinDayStartMs('2026-01-15')).toBe(Date.parse('2026-01-14T23:00:00Z'));
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,29 @@ export function formatIsoDate(d: Date): string {
|
||||
return fmt.format(d); // en-CA liefert ISO YYYY-MM-DD
|
||||
}
|
||||
|
||||
/**
|
||||
* Epoch-ms von 00:00 Europe/Berlin am gegebenen Tag (DST-korrekt). Genutzt, um
|
||||
* Tagesgrenzen für Zeit-Auswertungen zu bilden (z. B. Tagesanteil eines über
|
||||
* Mitternacht laufenden Timers).
|
||||
*/
|
||||
export function berlinDayStartMs(iso: string): number {
|
||||
const utcMidnight = Date.parse(`${iso}T00:00:00Z`);
|
||||
const parts = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: TIMEZONE,
|
||||
hourCycle: 'h23',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}).formatToParts(new Date(utcMidnight));
|
||||
const get = (t: string) => Number(parts.find((p) => p.type === t)?.value ?? '0');
|
||||
const asIfUtc = Date.UTC(get('year'), get('month') - 1, get('day'), get('hour'), get('minute'), get('second'));
|
||||
const offset = asIfUtc - utcMidnight; // ms, die Berlin der UTC voraus ist
|
||||
return utcMidnight - offset;
|
||||
}
|
||||
|
||||
export function parseIsoDate(s: string): Date {
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) {
|
||||
throw new Error(`Ungültiges ISO-Datum: ${s}`);
|
||||
|
||||
Reference in new issue
Block a user