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>
41 lines
1.1 KiB
Go
41 lines
1.1 KiB
Go
package domain
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNormalizeDue(t *testing.T) {
|
|
cases := map[string]string{
|
|
"2026-10-30": "2026-10-30",
|
|
"2026-10-30T14:30": "2026-10-30T14:30",
|
|
"2026-10-30 14:30": "2026-10-30T14:30",
|
|
"2026-10-30T14:30:59": "2026-10-30T14:30",
|
|
" ": "",
|
|
"nächste Woche": "nächste Woche",
|
|
}
|
|
for in, want := range cases {
|
|
if got := NormalizeDue(in); got != want {
|
|
t.Errorf("NormalizeDue(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDueAlarmAt(t *testing.T) {
|
|
berlin, err := time.LoadLocation("Europe/Berlin")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
at, ok := DueAlarmAt("2026-10-30", 0, berlin)
|
|
if !ok || !at.Equal(time.Date(2026, 10, 30, 9, 0, 0, 0, berlin)) {
|
|
t.Errorf("all-day alarm should fire at 09:00 local, got %v", at)
|
|
}
|
|
at, _ = DueAlarmAt("2026-10-30T14:30", 15, berlin)
|
|
if !at.Equal(time.Date(2026, 10, 30, 14, 15, 0, 0, berlin)) {
|
|
t.Errorf("lead time not applied: %v", at)
|
|
}
|
|
if _, ok := DueAlarmAt("irgendwann", 0, berlin); ok {
|
|
t.Errorf("unreadable due date must not produce an alarm")
|
|
}
|
|
}
|