Skip to the content.

Draft

This is a draft design document. Your feedback is welcomed.

Recurrence

Recurrence needs an overhaul to improve weaknesses and add new features.

Terminology

Criticism of Current Implementation

Proposals

Proposal: Eliminate mask, imaѕk Attributes

The mask attribute in the template is replaced by last, which indicates the most recent instance index synthesized. Because instances are never synthesized out of order, we only need to store the most recent index. The imask attribute in the instance is no longer needed.

Proposal: Rename parent to template

The name parent implies subtasks, and confuses those who inspect the internals. The value remains the UUID of the template. This frees up the namespace for future use with subtasks.

Proposal: New ‘rtype’ attribute

To indicate the flavor of recurrence, support the following values:

Proposal: Use relative offsets

The delta between wait and due date in the template should be reflected in the delta between wait and due date in the instance. Similarly, ‘scheduled’ must be handled the same way.

Proposal: On load, auto-upgrade legacy tasks

Upgrade template:

Upgrade instance:

Proposal: Deleting a chained instance

Deleting a rtype:chained instance causes the next chained instance to be synthesized. This gives the illusion that the due date is simply pushed out to (now + template.recur).

Proposal: Modification Propagation

TBD

Proposal: Exotic Dates

Expand date specifications to use pattern phrases:

Got suggestions?

Proposal: User-Defined Week Start

TBD

Implementation

Implementation: Adding a new periodic template

When adding a new periodic template:

task add ... due:D recur:R wait:D-1wk scheduled:D-1wk until:U

Creates:

template.uuid:        NEW_UUID
template.description: ...
template.entry:       now
template.modified:    now
template.due:         D
template.recur:       R       (stored in raw form, ie 'P14D')
template.wait:        D-1wk
template.scheduled:   D-1wk
template.until:       U
template.rtype:       periodic
template.last:

Creating the Nth instance (index N):

Clone instance from template.

instance.uuid:        NEW_UUID
instance.modified:    now
instance.due:         template.due + (N * template.recur)
instance.wait:        instance.due + (template.due - template.wait)
instance.scheduled:   instance.due + (template.due - template.scheduled)
instance.start:

template.last:        N

Implementation: Adding a new chained template

When adding a new chained template:

task add ... due:D recur:R wait:D-1wk scheduled:D-1wk until:U rtype:chained

Creates:

template.uuid:        NEW_UUID
template.description: ...
template.entry:       now
template.modified:    now
template.due:         D
template.recur:       R       (stored in raw form, ie 'P14D')
template.wait:        D-1wk
template.scheduled:   D-1wk
template.until:       U
template.rtype:       chained

Creating the Nth instance (index N):

Clone instance from template.

instance.uui  d:        NEW_UUID
instance.mod  ified:    now
instance.due  :         instance[N-1].end + template.recur
instance.wai  t:        instance.due + (template.due - template.wait)
instance.sch  eduled:   instance.due + (template.due - template.scheduled)
instance.sta  rt:

Chained tasks do not obey rc.recurrence.limit, and show only one pending task at a time.

Implementation: Special handling for months

Certain recurrence periods are inexact:

When the recurrence period is P1M the number of days in a month varies and causes drift.

When the recurrence period is P1Y the number of days in a year varies and causes drift.

When the recurrence period is P1D the number of hours in a day varies due to daylight savings, and causes drift.

Drift should be avoided by carefully implementing:

instance.due: template.due + (N * template.recur)