obsidian-tasks
obsidian-tasks copied to clipboard
Understand and document what recurrence does when advancing to non-existent dates
This arises from testing done, investigating improvements around #612
I added some experimental test cases exploring recurrence around month boundaries and leap years.
Expected Behavior
Based on no information whatsoever, I expected that if applying a recurrence rule resulted in an invalid date, the code would make small adjustments to the new date, to try to honour the rule.
So that, for example, if a due date was 31st Jan, and the recurrence was monthly, it would advance to 28th Feb (or 29th if in a leap year)
Current Behavior
These tests all initially failed, and now they pass, because I set nextDue to match the current behaviour.
(Note that they have a not-yet-committed description field to describe the purpose of the tests...)
// Cases where adding 1 increment falls on a date that does not exist
{
description:
'every month - due 31 March, and so 31 April would not exist: beware it skips forward 2 months',
interval: 'every month',
due: '2021-03-31',
// What I expected: nextDue: '2021-04-30',
nextDue: '2021-05-31',
},
{
description:
'every month - due 29 January, and so 29 February would not exist: beware it skips forward 2 months',
interval: 'every month',
due: '2021-01-29',
// What I expected: nextDue: '2021-02-28',
nextDue: '2021-03-29',
},
// Testing yearly repetition around leap days
{
description:
'yearly, due on leap day 29th February: beware it skips forward 4 years',
interval: 'every year',
due: '2020-02-29',
// What I expected: nextDue: '2021-02-28',
nextDue: '2024-02-29',
},
Steps to Reproduce
Context (Environment)
- Obsidian version: 0.14.6
- Tasks version: 1.5.1
- [x] ~~I have tried it with all other plugins disabled and the error still occurs~~ Not applicable
Possible Solution
In the first instance, I think some documentation is called far. I will basically review all my recurring tasks to check for ones after 28th of the month.
Maybe rules can be expressed such as every month on the last day ???
I will basically review all my recurring tasks to check for ones after 28th of the month.
It's easy to check one's due dates and make sure there are none at the end of the month.
I think a perhaps more serious manifestation of this is with when done as the next date is determined by the date when the task was completed.
So any monthly when done tasks completed on these dates will skip forward two months:
- 29, 30, 31 January
- 31 March
- 31 May
- 31 August
- 31 October
And any yearly when done task completed on this date will skip forward four years:
- 29 February
Sample test code:
{
description:
'every month when done - and completed 31st March: beware it skips forward 2 months',
interval: 'every month when done',
due: '2021-03-17',
today: '2021-03-31',
// What I expected: nextDue: '2021-04-30',
nextDue: '2021-05-31',
},
Good catch. Thank you @claremacrae. We should indeed add this to the documentation.
By the way, every month on the last day works (as does every month on the 2nd last Friday, etc.).
Again, I tried this in Google Calendar and if I set up an event as Monthly on day 31, it does indeed skip months with fewer days. That's per the specification:
Recurrence rules may generate recurrence instances with an invalid date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM on a day where the local time is moved forward by an hour at 1:00 AM). Such recurrence instances MUST be ignored and MUST NOT be counted as part of the recurrence set.
Thanks that's really helpful. It's the every month when done bit that will be hard to document and work around...
Thanks that's really helpful. It's the
every month when donebit that will be hard to document and work around...
Indeed. Maybe we should not allow every month when done and recommend every 4 weeks when done or whatever instead? I wish I still had my Todoist account. I would like to try every! month on the 31st to see how they implemented it :sweat_smile:
Indeed. Maybe we should not allow
every month when doneand recommendevery 4 weeks when doneor whatever instead?
I do think that is a really good idea. The more we can detect pitfalls and encourage users in safer directions, the better, I feel...
And for ‘every year when done’ we should recommend ‘every 52 weeks when done’
Oh, that's wild. Personally, I mostly set up recurrences measured in days/weeks, can confirm it works reliably at that granularity.
Is this fixed by #944 ?
Is this fixed by #944 ?
I don't think it is. For example, if every month when done is used, and the task is completed on the 31st, for several months of the year the new instance will skip a month.
I've added the Help Wanted label to this.
The place to document the issues and tips raised here is in: https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/docs/getting-started/recurring-tasks.md
Which is rendered here. https://obsidian-tasks-group.github.io/obsidian-tasks/getting-started/recurring-tasks/
Steps would be the following, which assume knowledge of creating git branches and GitHub pull requests:
- Fork this repository
- Check out the
gh-pagesbranch - Read the instructions on writing and viewing the docs at https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/docs/README.md
- Create a branch off of
gh-pages - Edit the file https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/docs/getting-started/recurring-tasks.md
- Add as much info as needed from the comments in this ticket to convey to users the severity of the issue, how it especially affects
when donesearches, and the workaround of using a fixed length interval, of a number of days or weeks - Ideally, generate the docs site locally and review your changes, including the formatting (optional, if you are unable to do this for some reason)
- Create a pull request, to merge your changes in to the
gh-pagesbranch
This has been significantly improved in release 1.15.1.
Note that if you say every month on the 31st, it will still skip months. Use every month on the last instead.