Whenever I need to get Emacs to prompt me for a date or time (even for non-Org things), I use
org-read-date. I love its flexibility. It’s great to be able to say things like
+3 for three days from now,
fri for next Friday,
+2tue for two Tuesdays from now,
+1w for next week, and
+1m for next month. It’s easy to use
org-read-date in Emacs Lisp. Calling it with
(org-read-date) (usually as an interactive argument, like so:
(interactive (list (org-read-date)))) gives me a date like
2015-08-06 depending on what I type in.
org-read-date for non-interactive date calculations, too. For example, if I want to quickly get the Org-style date for tomorrow, I can use
org-read-date‘s third parameter (
from-string) like this:
(org-read-date nil nil "+1")
Here’s how to calculate relative dates based on a specified date. You can hard-code the base date or use another
org-read-date to get it. In this example, I’m getting the Monday after
2015-08-31. Note the use of two
+ signs instead of just one.
(org-read-date nil nil "++mon" nil (org-time-string-to-time "2015-08-31"))
org-time-string-to-time converts a date or time string into the internal representation for time. You can then extract individual components (ex: month) with
decode-time, or convert it to the number of seconds since the epoch with
time-to-seconds. Alternatively, you can convert Org time strings directly to seconds with
If you’re working with days, you can convert time strings with
org-time-string-to-absolute. For example, you can use this to calculate the number of days between two dates (including the first day but excluding the last day), like this:
(let ((start-date (org-read-date)) (end-date (org-read-date))) (- (org-time-string-to-absolute end-date) (org-time-string-to-absolute start-date)))
To get the month, day, and year, you can use
decode-time, or you can use
To convert internal time representations into Org-style dates, I tend to use
(format-time-string "%Y-%m-%d" ...).
encode-time is useful for converting something to the internal time representation. If you’re working with absolute days, you can convert them to Gregorian, and then format the string.
So, to loop over all the days between the start and end date, you could use a pattern like this:
(let* ((start-date (org-read-date)) (end-date (org-read-date)) (current (org-time-string-to-absolute start-date)) (end (org-time-string-to-absolute end-date)) gregorian-date formatted-date) (while (< current end) (setq gregorian-date (calendar-gregorian-from-absolute current)) (setq formatted-date (format "%04d-%02d-%02d" (elt gregorian-date 2) ; month (elt gregorian-date 0) ; day (elt gregorian-date 1))) ; year ;; Do something here; ex: (message "%s" formatted-date) ;; Move to the next date (setq current (1+ current))))
Alternatively, you could use
org-read-date with a default date, like this:
(let* ((start-date (org-read-date)) (end-date (org-read-date)) (current start-date)) (while (string< current end-date) ;; Do something here; ex: (message "%s" current) ;; Move to the next date (setq current (org-read-date nil nil "++1" nil (org-time-string-to-time current)))))
There are probably more elegant ways to write this code, so if you can think of improvements, please feel free to share.
Anyway, hope that helps!