6

Q: how can I make org-mode read the time in timestamps intelligently?

org-mode uses org-read-date (which in turn relies on parse-time-string) to create/modify timestamps, scheduled items, and deadlines. I'm trying to figure out how to make it smarter when it reads in the time of an event.

One can enter times:

  • on a 24-hour clock (9:00 => 9:00AM, 21:00 => 9:00PM), or
  • with AM or PM suffixes (9am => 9:00AM, 9pm => 9:00PM).

That's all well and good, but here's where I'd like it to be smarter in a DWIM fashion. There are whole stretches of the day during which, on a 12-hour clock, I always mean PM (ie, for times in the middle of the night). For example, if I enter 2:00, I always mean "2:00PM", but on the 24-hour clock it means "2:00AM". The problem is that I commonly forget to append the pm part, and then my agenda informs me that the meeting I have for 2:00 in the afternoon is actually scheduled for 2:00 in the morning.

So: how can I convince org-read-date to accept a range of times to mean PM unless I explicitly add the am suffix? I'd like to be able to enter any time from, say, 1:00 to 6:59 and have it mean 1:00PM to 6:59PM unless I explicitly enter something like 1:00am.

NB: see this related thread on getting org-mode to understand alternative day/month abbreviations.

Dan
  • 32,584
  • 6
  • 98
  • 168

1 Answers1

8

org-read-date-analyze is the underlying function for parsing the input with org-mode time format, e.g., understanding AM/PM suffixes. It is a complicated function containing many parts, and IMO advice is the easiest way. Below is an example. This advice adds 12 hours to the final time when the input doesn't contain am and the final time is between 1:00 and 6:59.

For the new advice system in Emacs 24.4:

(defvar time-range-with-pm-suffix '("1:00" . "6:59"))

(defun org-analyze-date-dwim (original-fun ans org-def org-defdecode)
  (let* ((time (funcall original-fun ans org-def org-defdecode))
         (minute (nth 1 time))
         (hour (nth 2 time))
         (minutes (+ minute (* 60 hour)))
         s)
    (when (and (< hour 12)
               (not (string-match "am" ans))
               (>= minutes (org-hh:mm-string-to-minutes (car time-range-with-pm-suffix)))
               (<= minutes (org-hh:mm-string-to-minutes (cdr time-range-with-pm-suffix))))
      (setf (nth 2 time) (+ hour 12))
      (when (boundp 'org-end-time-was-given)
        (setq s org-end-time-was-given)
        (if (and s (string-match "^\\([0-9]+\\)\\(:[0-9]+\\)$" s))
            (setq org-end-time-was-given
                  (concat (number-to-string (+ 12 (string-to-number (match-string 1 s))))
                          (match-string 2 s))))))
    time))

(advice-add 'org-read-date-analyze :around #'org-analyze-date-dwim)

For the old defadvice:

(defvar time-range-with-pm-suffix '("1:00" . "6:59"))

(defadvice org-read-date-analyze (around org-analyze-date-dwim activate)
  (let ((ans (ad-get-arg 0))
        minute hour minutes s)
    ad-do-it
    (setq minute (nth 1 ad-return-value)
          hour (nth 2 ad-return-value)
          minutes (+ minute (* 60 hour)))
    (when (and ans
               (< hour 12)
               (not (string-match "am" ans))
               (>= minutes (org-hh:mm-string-to-minutes (car time-range-with-pm-suffix)))
               (<= minutes (org-hh:mm-string-to-minutes (cdr time-range-with-pm-suffix))))
      (setf (nth 2 ad-return-value) (+ hour 12))
      (when (boundp 'org-end-time-was-given)
        (setq s org-end-time-was-given)
        (if (and s (string-match "^\\([0-9]+\\)\\(:[0-9]+\\)$" s))
            (setq org-end-time-was-given
                  (concat (number-to-string (+ 12 (string-to-number (match-string 1 s))))
                          (match-string 2 s))))))))
liuhui
  • 141
  • 4