3

Q: How can I make org-read-date understand an abbreviation that calls a function that returns tomorrow's day of the week?

The super-short version: I'm trying to get the timestamp functionality of +1d when typing tom as a more finger-friendly way of saying "tomorrow."

This question follows up on this thread on dates and this thread on times.

I'm trying to train org-read-date to understand new, easier-to-type abbreviations when I'm entering timestamps, scheduled items, and deadlines. In particular, when entering a timestamp, I'd like to be able to enter tom and have org-read-date recognize it as "whatever day tomorrow is" (yes, I know I can type +1d, but it's an awfully awkward sequence of keys).

The parse-time functionality on which org-read-date relies expects an integer from 0 (Sunday) to 6 (Saturday). Fair enough, here's a little function that does it:

(defun tomorrow-day ()
  "Returns the day of the week for tomorrow."
  (let ((day (1+ (string-to-number (format-time-string "%w")))))
    (if (= day 7)
        0
      day)))

I'm stuck at the following point. Following this prior thread, I've pushed this function onto the parse-time-weekdays alist:

(push `("tom" . ,(tomorrow-day)) parse-time-weekdays)

Here's the problem: quasi-quoting pushes the literal value that tomorrow-day returns only at the time it was first evaluated (so, if today is Monday, tom refers to day number 2 from here to eternity). Instead, I need tomorrow-day evaluated each time I invoke tom when entering a timestamp. Why? Because I commonly leave an Emacs session running for days at a time, meaning that tom no longer refers to "tomorrow" after the day on which it was evaluated.

I've tried various forms of quoting and quasi-quoting, and eventually got quite silly with an intricate combination of funcalls and evals, all to no avail.

So: how do I get this function (tomorrow-day) evaluated every time I enter tom in a timestamp?

  • 2
    How about modifying org-read-date-analyze to transform your abreviation into whatever you want -- e.g., (when (string-match . . .? You'll probably be most interested in transforming the value of ans -- when ans is tom, make it +1d instead. You could also catch ans and modify it beforehand -- e.g., by modifying it inside org-read-date -- before final is set. – lawlist Nov 18 '14 at 2:38
  • @lawlist, thanks for the suggestion. org-read-date-analyze makes my head spin. Although I found an alternate way to get what I was after, I'll dig into that function for other modifications. – Dan Nov 18 '14 at 11:42
2

Seems like we can build on @lawlist's idea and do something similar to this:

(advice-add 'org-read-date-analyze :around
            (lambda (orig ans org-def org-defdecode)
              (funcall orig (if (string-match "tom" ans) "+1d" ans) org-def org-defdecode)))
  • Some whitespace is getting mixed in with the ans -- (if (string-match "tom" ans) "+1d" ans) should fix it. If you put in a message for ans, you'll see what I mean -- some stuff gets appended to the end that looks like whitespace. – lawlist Nov 18 '14 at 17:41
  • @lawlist: The unfortunate thing is that now "atom", "tom-tom" and so on also mean "tomorrow". – Constantine Nov 18 '14 at 17:51
  • Then we need a tighter regexp -- "^tom +$" or something like that. Or, we need to figure out exactly what type of whatspace is being inserted so a string= can work. – lawlist Nov 18 '14 at 18:00
  • Looks like org-read-date-display appends a space to the string typed in by the user until it gets something ending in four spaces. I wish I knew why it does that. Other callers of org-read-date-analyze don't do that (as far as I can tell), so I would use "^tom *$". – Constantine Nov 21 '14 at 20:20
1

I figured out a somewhat convoluted way to get the functionality without modifying org-read-date or org-read-date-analyze. It uses the same approach in the question, but defining a timer that runs every 24-hours at midnight that resets the value of tom:

(defun tomorrow-day ()
  "Returns the day of the week for tomorrow."
  (let ((day (1+ (string-to-number (format-time-string "%w")))))
    (if (= day 7)
        0
      day)))

(eval-after-load 'parse-time
   '(progn
     (setq parse-time-weekdays (nconc parse-time-weekdays
                                      `(("tom" . ,(tomorrow-day))))
     (defvar parse-time-tomorrow-timer
       (run-at-time "12am"
                    (* 24 60 60)
                    (lambda ()
                      (setf (cdr (assoc "tom" parse-time-weekdays))
                            (tomorrow-day))))
       "Timer to reset the day to which \"tom\" refers in timestamps."))) 

What it lacks in elegance, it makes up in obscurity!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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