diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-28 09:59:06 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-28 12:50:12 +0200 |
| commit | dbf8980f1bc5428d1eb463505968469617dbf3fc (patch) | |
| tree | c94c057bfe6d2d8ff3763a64a30cefd59d76cd87 | |
| parent | d2cc2def17b58da762d88090c0c298dc2e87dd1d (diff) | |
refactor(task): centralize Taskwarrior date format as task.DateFormat
DRY/MEDIUM task (UUID 2c74960f): the format string "20060102T150405Z" was
hardcoded in 5+ places across the task and ui packages.
- Export task.DateFormat constant from internal/task/task.go
- Replace hardcoded string in task.go and stats.go with DateFormat
- Update internal/ui/helpers.go to alias the constant (taskDateFormat =
task.DateFormat) rather than repeating the string literal
- Replace all 6 raw string literals in table.go with task.DateFormat
- Remove duplicate dueText() from table.go; its only call site now uses
formatDueText() from helpers.go, which also normalises to midnight UTC
for more accurate day-difference calculations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| -rw-r--r-- | internal/task/stats.go | 2 | ||||
| -rw-r--r-- | internal/task/task.go | 7 | ||||
| -rw-r--r-- | internal/ui/helpers.go | 8 | ||||
| -rw-r--r-- | internal/ui/table.go | 32 |
4 files changed, 19 insertions, 30 deletions
diff --git a/internal/task/stats.go b/internal/task/stats.go index c516249..b306687 100644 --- a/internal/task/stats.go +++ b/internal/task/stats.go @@ -28,7 +28,7 @@ func DueTasks(tasks []Task, now time.Time) int { if t.Status == "completed" || t.Due == "" { continue } - ts, err := time.Parse("20060102T150405Z", t.Due) + ts, err := time.Parse(DateFormat, t.Due) if err != nil { continue } diff --git a/internal/task/task.go b/internal/task/task.go index 2c7f8e8..8eadd18 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -16,6 +16,11 @@ import ( "github.com/google/shlex" ) +// DateFormat is the date format used by Taskwarrior in all date fields +// (e.g. Entry, Due, Start). All date parsing and formatting in this +// package uses this constant. +const DateFormat = "20060102T150405Z" + // Task represents a taskwarrior task as returned by `task export`. type Annotation struct { Entry string `json:"entry"` @@ -421,7 +426,7 @@ func SortTasks(tasks []Task) { if s == "" { return time.Time{}, false } - t, err := time.Parse("20060102T150405Z", s) + t, err := time.Parse(DateFormat, s) if err != nil { return time.Time{}, false } diff --git a/internal/ui/helpers.go b/internal/ui/helpers.go index 71c21db..9846811 100644 --- a/internal/ui/helpers.go +++ b/internal/ui/helpers.go @@ -5,10 +5,14 @@ import ( "regexp" "strings" "time" + + "codeberg.org/snonux/tasksamurai/internal/task" ) -// Date format used by Taskwarrior -const taskDateFormat = "20060102T150405Z" +// taskDateFormat aliases task.DateFormat for use within this package. +// It is kept as a package-level constant so internal helpers don't need +// to qualify every parse/format call with the package name. +const taskDateFormat = task.DateFormat // parseTaskDate parses a date string in Taskwarrior format func parseTaskDate(dateStr string) (time.Time, error) { diff --git a/internal/ui/table.go b/internal/ui/table.go index 2cc2b3d..97011ff 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -927,7 +927,7 @@ func (m Model) taskToRow(t task.Task) atable.Row { } age := "" - if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { + if ts, err := time.Parse(task.DateFormat, t.Entry); err == nil { days := int(time.Since(ts).Hours() / 24) age = fmt.Sprintf("%dd", days) } @@ -967,7 +967,7 @@ func (m Model) formatDue(s string, width int) string { if s == "" { return "" } - ts, err := time.Parse("20060102T150405Z", s) + ts, err := time.Parse(task.DateFormat, s) if err != nil { return s } @@ -1078,7 +1078,7 @@ func (m Model) taskToRowSearch(t task.Task, re *regexp.Regexp, styles atable.Sty } age := "" - if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { + if ts, err := time.Parse(task.DateFormat, t.Entry); err == nil { days := int(time.Since(ts).Hours() / 24) age = fmt.Sprintf("%dd", days) } @@ -1146,7 +1146,7 @@ func (m Model) expandedCellView() string { case 1: val = strconv.Itoa(t.ID) case 2: - if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { + if ts, err := time.Parse(task.DateFormat, t.Entry); err == nil { days := int(time.Since(ts).Hours() / 24) val = fmt.Sprintf("%dd", days) } @@ -1223,26 +1223,6 @@ func (m *Model) updateTableHeight() { m.tbl.SetHeight(h) } -func dueText(s string) string { - if s == "" { - return "" - } - ts, err := time.Parse("20060102T150405Z", s) - if err != nil { - return s - } - days := int(time.Until(ts).Hours() / 24) - switch days { - case 0: - return "today" - case 1: - return "tomorrow" - case -1: - return "yesterday" - default: - return fmt.Sprintf("%dd", days) - } -} func (m *Model) computeColumnWidths() { maxID := 1 @@ -1258,7 +1238,7 @@ func (m *Model) computeColumnWidths() { maxID = l } age := "" - if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { + if ts, err := time.Parse(task.DateFormat, t.Entry); err == nil { age = fmt.Sprintf("%dd", int(time.Since(ts).Hours()/24)) } if l := len(age); l > maxAge { @@ -1268,7 +1248,7 @@ func (m *Model) computeColumnWidths() { if l := len(urg); l > maxUrg { maxUrg = l } - due := dueText(t.Due) + due := formatDueText(t.Due) if l := len(due); l > maxDue { maxDue = l } |
