7

Admitting up front that I am not capable of much Lisp, I have found ways to produce the current date or a number of seconds from a date, but I am looking for a simple equivalent to shell command like date -d "next wed" I believe that with the output fed to (format-time-string ... I can get what I need.

2 Answers 2

9

Emacs's built-in date parser is parse-time-string in parse-time.el, called by date-to-time. It understands English month and weekday names and several combinations of elements in various orders, but not expressions like “next Wednesday”.

The date input formats in the GNU date command provided on non-embedded Linux and Cygwin are implemented in the source of the date utility, they are not available as a standalone library.

If the GNU date utility is available on your system, you can call it. The following snippet returns a time parsed by date as a list (SEC MIN HOUR DAY MON YEAR DOW DST TZ).

(let ((human-time "next Wednesday"))
  (parse-time-string (with-temp-buffer
                       (call-process "env" nil t nil "LC_ALL=C" "LANGUAGE=" "date" "-d" human-time)
                       (or (bobp) (delete-backward-char 1))
                       (buffer-string)))
3
  • Extremely helpful! I was thinking that I was just missing some internal call that might use the same function style as localtime() would.
    – David D.
    Oct 1, 2014 at 21:11
  • 1
    @Gilles I am curious why you used env. This also works: (call-process "date" nil t nil "-d" human-time). Jul 1, 2016 at 21:15
  • 2
    @KaushalModi This only works if your environment specifies the US date format. Otherwise the output of date is not something parse-time-string understands. Jul 1, 2016 at 21:46
4

I made an Elisp only solution:

You can call it using M-x dow-time and in code using (dow-time 3)

(defvar dow-time-days '(("Monday" . 1)
                        ("Tuesday" . 2)
                        ("Wednesday" . 3)
                        ("Thursday" . 4)
                        ("Friday" . 5)
                        ("Saturday" . 6)
                        ("Sunday" . 0))
  "Days of week alist used by `dow-time'")

(defun dow-time (day-input)
  "Get time for next day of week, 0 is Sunday
Also accepts human readable strings"
  (interactive (list
                (completing-read "Day of week: " dow-time-days nil t)))
  (let* ((day (if (numberp day-input) day-input
                (cdr (assoc day-input dow-time-days))))
         (time (decode-time))
         (dow (nth 6 time))
         (day-of-month (nth 3 time))
         (new-dow (if (> day dow)        ; Check what's the new dow's index
                       (- day dow)       ; In the same week
                     (+ (- 7 dow) day))) ; In the next week
         (new-time (encode-time 0 0 0
                                (+ new-dow day-of-month)
                                (nth 4 time)
                                (nth 5 time))))
    (if (called-interactively-p 'any)
        (message (current-time-string new-time)) ; Print nicely if interactive
      new-time)))
1
  • 1
    This is more precisely what I asked after, but doesn't address the bigger picture of handling more generic "adjust" function (c.f. BSD -v) or the -d functionality in the localtime call. It is extremely helpful in my case though.
    – David D.
    Oct 1, 2014 at 23:11

Your Answer

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

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