Task recurrence

Created at
7 October 2025
Last modified
1 January 2026
Status
🌿 sapling
Tagged

In the context of task management, a recurring task (or "repeating task") is a task that happens more than once according to some pre-set pattern.

Recurring events are a thing, obviously. You do the same thing at 7pm each Friday. You have a recurring 10am-10:30am meeting. Whatever. But events will reliably happen at the same time every time, and if you miss them, you miss them. Recurring tasks are trickier because you may wish to alter when the next task occurs, based on when you completed the previous one.

This leads to two issues:

  • There are some tasks where the next task in the series is best defined based on when you completed the previous task.
    • For example, let's say you have a recurring task to water your house plants every three days. If one occurrence of these is scheduled on Tuesday, the next will be scheduled on Friday. If you don't get around to completing occurrence one until Thursday, there's no point potentially overwatering your plants by doing another water on Friday.
  • There are some tasks whose schedule is still rigid, but which are high-frequency enough that you risk ending up with "double-up" tasks if you don't complete one task by the time the second activates.

I'm currently looking at making the switch from OmniFocus to TaskWarrior, and while TaskWarrior is extensible, its existing task recurrence framework is very basic. In comparison, OmniFocus has a really robust task recurrence system.

In this node I'm building a conceptual description of a task recurrence system that should hopefully mirror the power and flexibility of OmniFocus' existing system. By writing it all out, I'll have the foundation for something similar in TaskWarrior.

Examining the case studies

OmniFocus: The gold standard

OmniFocus demostrates, to my mind, the best possible application of task recurrence. It offers a degree of granularity that most (all?) other task management apps fail to even approach. Between its extremely customisable recurrence frequencies, and its vary recurrence styles, you get something granular enough to cover almost all cases.

Demonstrations of many of the options OmniFocus allows for task recurrence.Demonstrations of many of the options OmniFocus allows for task recurrence.Demonstrations of many of the options OmniFocus allows for task recurrence.Demonstrations of many of the options OmniFocus allows for task recurrence.

Demonstrations of many of the options OmniFocus allows for task recurrence.

Importantly:

  • OmniFocus will make you choose whether you wish to recur based on the item's completion (and if you're repeating based on the previous item's completion, whether you're scheduling the defer or due date), or based on its assigned dates.
  • When setting up weekly or monthly recurrence, OmniFocus will give you a range of options including the ability to set weekly recurrence on specific days of the week or month (including, crucially, setting defer or due dates on, say, the first Monday of the month, or the second-to-last Sunday, or what-have-you).
Definitions

We're gonna be doing a lot of talking about tasks, dates, and the relationships between them, so let's get some terms set now.

  • Defer date: If a task has a defer date, it won't show up in your list of tasks until that date. Recurring tasks often benefit from a defer date as, otherwise, as soon as you complete a task, its next instance will pop up on your list.
  • Due date: This is the date the task should be done by. We're going to be encountering recurrence types more down the article, so I want to get these definitions straight now.
  • Schedule type: When you complete a recurring task, the schedule type determines how the following task in the series is scheduled. Possibilities include:
    • From assigned: the next task's defer and due dates are calculated based on the previous task's defer and due dates.
    • Defer from completed: the next task's defer date is calculated based on the previous task's completion date, and the due date (if any) is calculated relative to this.
    • Due from completed: the next task's due date is calculated based on the previous task's completion date, and the defer date (if any) is calculated relative to this.
  • Recurrence rule: This is the set of conditions that defines the dates set for the next instance of a recurring task.

Of note here: because OmniFocus has so many ways to set recurrence dates, it will only ever create the next instance of a recurring task once the previous instance has been completed.

OmniFocus does this hard work by implementing a subset of the recurrence rules found in the iCalendar RFC5545 standard for calendar events.

TaskWarrior: Rudimentary but extensible

TaskWarrior has a much simpler recurrence system. A task's recurrence is described as a simple duration, itself consisting of a number and a unit (eg day, week, month, year). A hidden parent task "spawns" visible child tasks, creating tasks as needed to populate your forward register of upcoming and due tasks.

However, TaskWarrior has a powerful hook system, which allows the program to call external scripts as a result of various database events. Crucially, this includes modification of tasks, and by inspecting the parameters of the hook call, we can run code when a task is completed. This is going to be crucial.

We're not going to get into hooks in this page - that'll be for a follow-on.

So what makes RFC5545 recurrence so great?

For a task to recur it must have either a defer date or a due date. Otherwise it's just a task that continually pops up on your task list.

A task with a defer date can have the schedule type of from assigned or defer from completed. A task with a due date can have the schedule type of from assigned or due from completed. A task with both dates can have any schedule type.

A task's recurrence rule is defined as a series of key-value pairs. The possible valid keys are a subset of the reurrence rule keys from RFC 5545: this standard is designed for calendar events but works perfectly here. I say a subset because I'm thinking of developing this for myself, and not focussing too heavily on all possible applications (so it makes sense to pick the important stuff and implement that). As such, we're going to completely ignore all parts of this standard that deal with sub-day events (eg events which happen every hour or what-have-you).

The RFC5545 recurrence rule is defined by a set of key-value pairs. A task must have a FREQ key, and all the keys specified below are otherwise optional.

  • FREQ: How often the task occurs. I can't say I ever need a task to occur any more frequently than once a day (I don't need it to occur once a day, but let's hedge our bets here), so for my implementation I'll be allowing this to be DAILY, WEEKLY, MONTHLY, or YEARLY.
  • INTERVAL: The task recurs every INTERVAL FREQs - so for example, if INTERVAL were 2 and FREQ were WEEKLY, your task would occur every two weeks. This defaults to 1.
  • BYDAY, BYMONTHDAY, BYYEARDAY, BYWEEKNO, BYMONTH, BYSETPOS: A series of limiting factors (see below for much much more info). There's more than this, but we're going to focus just on the date-related recurrence flags.
    • BYDAY refers to a day of the week. It must a comma-separated list of days of the week (MO = Monday, TU = Tuesday, etc.). Optionally, each item in the list can be preceded by a positive or negative number (eg +1MO, -2TU) - at this point it's referring to the first Monday (or the second-to-last Tuesday, or whatever) of the month.
    • BYMONTHDAY refers to a day of the month. It must be a comma-separated list of positive or negative numbers, with valid values being 1 to 31 or -1 to -31.
    • BYYEARDAY refers to a given day of the year. It must be a comma-separated list of positive or negative numbers, with valid values being 1 to 366 or -1 to -366.
    • BYWEEKNO refers to a given week of the year. It must be a comma-separated list of positive or negative numbers, with valid values being 1 to 53 or -1 to -53.
    • BYMONTH refers to a given month of the year. It must be a comma-separated list of positive numbers, with valid values being 1 to 12.

BYxxx - RFC5545's secret sauce

You'll notice I've lumped a bunch of fields together at the end of the above list. These are basically a bunch of limiting factors that make the generic recurrence much more powerful. The RFC states:

BYxxx rule parts modify the recurrence in some manner. BYxxx rule parts for a period of time that is the same or greater than the frequency generally reduce or limit the number of occurrences of the recurrence generated. For example, "FREQ=DAILY;BYMONTH=1" reduces the number of recurrence instances from all days (if BYMONTH rule part is not present) to all days in January. BYxxx rule parts for a period of time less than the frequency generally increase or expand the number of occurrences of the recurrence. For example, "FREQ=YEARLY;BYMONTH=1,2" increases the number of days within the yearly recurrence set from 1 (if BYMONTH rule part is not present) to 2.

The iCalendar spec actually provides this information as a table. I've provided the relevant ones below:

DAILY WEEKLY MONTHLY YEARLY
BYMONTH Limit Limit Limit Expand
BYWEEKNO N/A N/A N/A Expand
BYYEARDAY N/A N/A N/A Expand
BYMONTHDAY Limit N/A Expand Expand
BYDAY Limit Expand * *
BYSETPOS Limit Limit Limit Limit

(BYDAY has some special rules when applied to MONTHLY or YEARLY recurrence rules, depending on what other BYxxx rules are present.)

Do we need to account for all of these permutations?

Probably not! By my reckoning, here are the recurrence rules that OmniFocus supports:

  • FREQ=DAILY: None
  • FREQ=WEEKLY: BYDAY (weekly form)
  • FREQ=MONTHLY: BYMONTHDAY or BYDAY (monthly form) (not both)
  • FREQ=YEARLY: None

This still give us a pretty wide gamut of options, without making things too difficult to implement. Given this set of recurrence intervals has worked well for me in the past, I'm going to keep at it for now. Handily, these all expand the range of dates - none of these are limiters.

Rules for implementing

When we're using these recurrence rules with events we often have the advantage of being able to plan far in advance - but with tasks (especially when events are being scheduled based on completion times) things get more complex.

For this reason, we're going to copy OmniFocus' strategy of only ever mapping out the next task in the sequence, and then only when the previous task has been marked as complete. At that point, we follow the steps below. The task we're just completed will be referred to as the previous task, while the task we're about to create will be referred to as the next task.

1. Determine anchor date. The anchor date is the date that anchoring the recurrence against - it'll usually be something related to the previous task. For tasks whose schedule type is from assigned, the anchor date is the previous task's due date (unless the task has no due date, in which case it'll be the previous task's defer date); for other tasks, the anchor date it the previous task's completion date (ie when the user marked it completed).

2. Determine the target date. The target date is the date which will be set based on the recurrence rule. For tasks whose schedule type is from assigned, the target date is the same as the anchor date (ie the next task's due date if the previous task had a due date, and the next task's defer date if not). For tasks whose schedule type is defer from completed, the target date is the next task's defer date, and for tasks whose schedule type is due from completed, the target date is the next task's due date.

3. Calculate the target date. This one is more complex, but is basically calculated by using the anchor date as the start date for the recurrence, and picking the next valid date based on the recurrence rules. I'll go into this in more detail in the following section.

4. Set the other date, if necessary. If the previous task had both a defer and due date, we need to set both on the next task too. We'll do this by measuring the distance between defer and due date on the previous task, and using the same interval on the next task.

OK but how do you calculate that target date?

Step three up there is definitely doing a lot of heavily lifting.

1. Work out the basic recurrence interval using FREQ. We're not going to bother with the INTERVAL key yet - we just want to know what our basic unit of recurrence is: day, week, month, or year.

2. Calculate all possible target dates within the current interval. If FREQ=WEEKLY, use BYDAY to calculate all occurrences within the current week. If FREQ=MONTHLY, use BYMONTHDAY or BYDAY to calculate all occurences within the current month. If there is no BYxxx rule, then the target date is determined by the anchor date (so, for example, for a weekly occurrence, if the anchor date is a Thursday, the target date is a Thursday).

3. Select the next occurrence within the current interval. If any of the above recurrences in the current interval is greater than the anchor date, we select the next one to be the new target date.

4. If there is no next occurrence within the current interval, select the first occurrence within the next interval. The next interval is determined by INTERVAL - so if your task is FREQ=WEEKLY;INTERVAL=2, we'll look two weeks into the future and pick the first occurrence then.

This is all very complicated, and I've only come to this by thinking about it a lot over multiple evenings, so let's look at some examples:

Example 1: A simple daily task.
  • Defer date: Friday 3 October
  • Schedule type: Defer from completed
  • Recurrence rule: FREQ=DAILY
  • Completed on: Thursday 9 October

A simple example to start us off. In this case, our next task's defer date will be set to the following day, ie Friday 10 October.

Example 2: Weekly task
  • Defer date: Friday 3 October
  • Schedule type:

External links

OmniFocus - Repeating Tasks

A description of how OmniFocus handles repeating tasks.

iCalendar RFC 5545

"This document defines the iCalendar data format for representing and exchanging calendaring and scheduling information such as events, to-dos, journal entries, and free/busy information, independent of any particular calendar service or protocol." This page has a whole host of info on how to do calendar events, but of particular importance to us is 3.3.10 Recurrence Rule.

How recurrence works - TaskWarrior

A description of how TaskWarrior handles recurring tasks.