feat(tasks): due dates with optional time; alarms at the right moment

Due dates can carry a time (YYYY-MM-DDTHH:MM). Alarms fire at that time,
or 09:00 for all-day tasks, minus the user's lead time, in TZ (default
Europe/Berlin, tzdata embedded). A new due date or reopening a task
re-arms the alarm. Recurrence keeps the time; Markdown export/import
carry it. The UI shows relative labels ("Heute, 14:30"), marks overdue
tasks to the minute and groups by local day instead of the UTC date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 07:48:37 +01:00
1 parent 300fd6a7be
commit 344f5e4ab2
21 files changed
+391 -50

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
import { describe, it, expect } from 'vitest';
import { formatDue, isOverdue, isDueToday, joinDue, splitDue } from '../due';
const now = new Date(2026, 9, 29, 14, 0); // Thu 29.10.2026, 14:00 local
describe('due dates', () => {
it('splits and joins date and time', () => {
expect(splitDue('2026-10-30T14:30')).toEqual({ date: '2026-10-30', time: '14:30' });
expect(splitDue('2026-10-30')).toEqual({ date: '2026-10-30', time: '' });
expect(joinDue('2026-10-30', '09:15')).toBe('2026-10-30T09:15');
expect(joinDue('2026-10-30', '')).toBe('2026-10-30');
expect(joinDue('', '09:15')).toBe('');
});
it('formats relative to today', () => {
expect(formatDue('2026-10-29T14:30', now)).toBe('Heute, 14:30');
expect(formatDue('2026-10-30', now)).toBe('Morgen');
expect(formatDue('2026-10-28', now)).toBe('Gestern');
expect(formatDue('2026-11-06T08:00', now)).toBe('Fr., 06.11., 08:00');
expect(formatDue('2027-01-02', now)).toBe('02.01.2027');
});
it('knows overdue to the minute', () => {
expect(isOverdue('2026-10-29T13:59', now)).toBe(true);
expect(isOverdue('2026-10-29T14:30', now)).toBe(false);
expect(isOverdue('2026-10-29', now)).toBe(false);
expect(isOverdue('2026-10-28', now)).toBe(true);
expect(isDueToday('2026-10-29T08:00', now)).toBe(true);
});
});