feat: ✨ Handle recurring tasks
Tim Izzo tim@5ika.ch
Wed, 10 Sep 2025 16:10:38 +0200
3 files changed,
96 insertions(+),
0 deletions(-)
M
README.md
→
README.md
@@ -50,6 +50,7 @@ - `z`: Clear priority of focused task
- `p`: Sort task by priority - `s`: Sort task by date - `u`: Clear done tasks (move them to *done.txt*) +- `?`: Show full help ## Features@@ -57,12 +58,32 @@ `todo-txt` is a TUI to manage todo list with [todo-txt format rules](https://github.com/todotxt/todo.txt)
stored in a plain text file. ### Due date + Additionnaly, it handles due dates as a [custom *key:value* metadata](https://github.com/todotxt/todo.txt?tab=readme-ov-file#additional-file-format-definitions). Example: ``` (B) Review pull request #123 @work +code due:2025-09-05 ``` + +### Reccurring task + +`todo-txt` handles reccuring task with flag `freq`. +Example: + +``` +(C) Update servers freq:month due:2025-09-05 +``` + +On check of this task, it will be duplicated with a new due date (`2025-10-05`). +If it doesn't have a due date, the next due date is calculated from today's date. + +Available frequency values are: +- `day`, `daily` +- `week`, `weekly` +- `month`, `monthly` +- `quarter`, `quarterly` +- `year`, `yearly` ### Clear tasks history
A
scheduling.go
@@ -0,0 +1,70 @@
+package main + +import ( + "strings" + "time" + + todo "github.com/1set/todotxt" +) + +type Frequency = int + +const ( + Day Frequency = iota + Week + Month + Quarter + Year +) + +var frequencyMap = map[string]Frequency{ + "day": Day, + "daily": Day, + "week": Week, + "weekly": Week, + "month": Month, + "monthly": Month, + "quarter": Quarter, + "quartery": Quarter, + "year": Year, + "yearly": Year, +} + +func handleRecurringTask(task todo.Task) *todo.Task { + freqValue := task.AdditionalTags["freq"] + + if freqValue != "" { + newTask, _ := todo.ParseTask(task.Original) + dueDate := getDueDate(task) + frequency := frequencyMap[strings.ToLower(freqValue)] + newTask.DueDate = getNextDueDate(dueDate, frequency) + return newTask + } + + return &todo.Task{} +} + +func getNextDueDate(dueDate time.Time, frequency Frequency) time.Time { + switch frequency { + case Day: + return dueDate.AddDate(0, 0, 1) + case Week: + return dueDate.AddDate(0, 0, 7) + case Month: + return dueDate.AddDate(0, 1, 0) + case Quarter: + return dueDate.AddDate(0, 3, 0) + case Year: + return dueDate.AddDate(1, 0, 0) + default: + return dueDate + } +} + +func getDueDate(task todo.Task) time.Time { + if task.HasDueDate() { + return task.DueDate + } else { + return time.Now() + } +}