12

How do I schedule a repeating org-mode task for the last day of every month?

As is, when I schedule a task like DEADLINE: <2017-03-31 Fri +1m>, marking it done shifts the next date to DEADLINE: <2017-05-01 Fri +1m> which would be a day too late if the deadline is the end of the month. Dates scheduled for the beginning of the month properly advance to the first of the following month, regardless of number of days in the month, so this problem only occurs at the end of the month.

I would prefer to do this using pure org-mode and avoid diary dates if possible since some commands and views can't handle diary dates. That said, a diary solution is better than nothing!

This question is distinct from Schedule repeating Org event for day of month since scheduling for the end of the month is a common edge case which requires special handling to account for the variable number of days in each month.

6
  • 1
    Possible duplicate of Schedule repeating Org event for day of month See also the link in the linked-answer for additional solutions.
    – lawlist
    Mar 24, 2017 at 21:40
  • None of those are solutions to my problem. Please see edit. Mar 25, 2017 at 23:03
  • I've posted an answer for both the diary and/or the agenda buffer. The methodology in the link to the potential duplicate [?] thread is the same in that this question calls for a sexp and the linked question states that a sexp can be used to achieve the desired result.
    – lawlist
    Mar 26, 2017 at 1:56
  • 1
    @lawlist The edit was based on a slightly improved version of the question I sent to the org-mode mailing list. I will accept your answer in a day or two if no one provides a native solution; I am already using yours in my own config. =) Mar 27, 2017 at 20:09
  • 1
    This is very much a hack, but try setting your deadline on the 30th: it will stay on the 30th (until next February - that's why it's a hack :-)) Then if you meet the deadline and the month has 31 days, take the next day off!
    – NickD
    Mar 27, 2017 at 22:08

2 Answers 2

12

The custom function diary-list-day-of-month (set forth below) can be used to create a repeating org-mode task or to create a diary entry that will appear on the *Calendar* and/or in the *Org Agenda* buffer (if org-agenda-include-diary is t).

;;; ORG-MODE:  * My Task
;;;              SCHEDULED: <%%(diary-last-day-of-month date)>
;;; DIARY:  %%(diary-last-day-of-month date) Last Day of the Month
;;; See also:  (setq org-agenda-include-diary t)
;;; (diary-last-day-of-month '(2 28 2017))
(defun diary-last-day-of-month (date)
"Return `t` if DATE is the last day of the month."
  (let* ((day (calendar-extract-day date))
         (month (calendar-extract-month date))
         (year (calendar-extract-year date))
         (last-day-of-month
            (calendar-last-day-of-month month year)))
    (= day last-day-of-month)))

Here is a sexp for the diary file, which can appear in the *Calendar* and/or in the *Org Agenda* buffer -- see org-agenda-include-diary:

%%(diary-last-day-of-month date) Last Day of the Month

Here is an example org-mode entry that can appear in the *Org Agenda* -- e.g., M-x org-agenda RET followed by the letter a:

* My Task
  SCHEDULED: <%%(diary-last-day-of-month date)>
3
  • It is a shame a custom function is needed, but at least it works and is easy to read! Do you know if org is able to create advanced warnings for dates of this form? My understanding from the docs is it cannot. Mar 26, 2017 at 6:22
  • I read about the same limitation earlier today, but have not yet had an occasion to experiment with the advance warning feature in conjunction with an org-mode timestamp containing a sexp. If you believe a last day of the month feature would be helpful and want to see it built-in someday, the org-mode team has a mailing list where they accept feature requests and bug reports.
    – lawlist
    Mar 26, 2017 at 7:24
  • 2
    I was reading this on account of wanting something a bit different, which turned out to exist already: Scheduling the first/last Wednesday of every month" with SCHEDULED: <%%(diary-float t 3 1)> or SCHEDULED: <%%(diary-float t 3 -1)> respectively.
    – phils
    Mar 25, 2019 at 1:55
3

Wow I never realized such a basic feature didn't exist. Unfortunate...

At first I thought you would be able to set up a warning, so that you would have the deadline for the 1st of every month and then warn you the day prior but that didn't work either.

As far as I can tell your best option would be to use a subtask for each month of the year. Each of those subtasks would have a 1y repeat. This isn't actually so bad being you can just collapse it anyways!

You can use M-x org-clone-subtree-with-time-shift to generate duplicated tasks and then update each deadline make sure the dates are correct.

If you were not going to use TODO's and mark them complete you could use multiple DEADLINES for one task but I think multiple tasks is a better solution.

You should be able to just copy/paste this and change the titles.

* TODO Monthly Task
** TODO Monthly Task
   DEADLINE: <2017-01-31 Tue +1y>

** TODO Monthly Task
   DEADLINE: <2017-02-28 Tue +1y>

** TODO Monthly Task
   DEADLINE: <2017-03-31 Fri +1y>

** TODO Monthly Task
   DEADLINE: <2017-04-30 Sun +1y>

** TODO Monthly Task
   DEADLINE: <2017-05-31 Wed +1y>

** TODO Monthly Task
   DEADLINE: <2017-06-30 Fri +1y>

** TODO Monthly Task
   DEADLINE: <2017-07-31 Mon +1y>

** TODO Monthly Task
   DEADLINE: <2017-08-31 Thu +1y>

** TODO Monthly Task
   DEADLINE: <2017-09-30 Sat +1y>

** TODO Monthly Task
   DEADLINE: <2017-10-31 Tue +1y>

** TODO Monthly Task
   DEADLINE: <2017-11-30 Thu +1y>

** TODO Monthly Task
   DEADLINE: <2017-12-31 Sun +1y>

It isn't the most elegant solution, but it does work.

2
  • 5
    February has 29 days sometimes...
    – user14082
    Mar 25, 2017 at 12:50
  • Yep, this is exactly what I have been doing without an automated solution. Alas, I have so many tasks due at the end of the month that it is becoming burdensome. Mar 25, 2017 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.