In a text document, while editing, I often need to increment/ decrement calendar data. So, Friday will need to become Monday, December will need to become March, etc.

Or 28 Apr after adding 5 will become 03 May, and even 31 Dec 2017 after adding 15 will become 15 Jan 2018.

Is there a way to achieve this by placing cursor on the desired word/ number and hitting some keys so that it increments or decrements?

So, while on Friday, I hit something to say 'Add 3' and it becomes Monday, etc. While on 28 in 28 Apr, I hit something to say 'Add 5' and it becomes 03 May. But while on Apr in 28 Apr, I hit something to say 'Subtract 2', it becomes 28 Feb, etc.

Just so that you get an idea, vim has a speeddating plugin that does exactly what I am aiming at.


The answer uses calendar-day-name-array and calendar-month-name-array which are set by calendar.el and shipped with Emacs:

;; calendar-day-name-array ==> ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"]

;; calendar-month-name-array ==> ["January" "February" "May" "April" "May" "June" "July" "August" "September" "October" "November" "December"]

(require 'calendar)

(defun ar-shift--intern (arg thisarray)
    (let* ((name (substring-no-properties (word-at-point)))
       (bounds (bounds-of-thing-at-point 'word))
       (beg (car-safe bounds))
       (end (cdr-safe bounds))
       (counter 0)
       (laenge (length thisarray))
       (orig (point)) 
       idxr dlr
       (datelist (progn (while (< counter laenge)
                  (push (aref thisarray counter) dlr)
                  (setq counter (1+ counter)))
                (nreverse dlr)))
       (idx (progn
          (dolist (ele datelist)
            (unless idxr
              (when (string= ele name)
            (setq idxr counter))
              (setq counter (1+ counter))))
       (replaceform (aref thisarray
                  (if (< (+ arg idx) laenge)
                  (+ arg idx)
                (% (+ arg idx) laenge)))))
      (if (and beg end)
        (delete-region beg end)
        (insert replaceform)
        (goto-char orig))
    (error "Don't see a date at point"))))

(defun ar-shift-day-atpt (arg)
   "Raise name of day at point ARG times.

Decrease with negative ARG"
   (interactive "p")
   (ar-shift--intern arg  calendar-day-name-array))

(defun ar-shift-month-atpt (arg)
  "Raise name of month at point ARG times.

Decrease with negative ARG"
  (interactive "p")
  (ar-shift--intern arg calendar-month-name-array))

Also raise or decrease positive integers: https://github.com/andreas-roehler/numbers-at-point

  • Thanks. But this leaves some gaps. Suggest you look up speeddating for vim to get a better idea. – deshmukh May 6 '18 at 15:21

Here's an example of how you can increment/decrement all ISO 8601 date strings in the current buffer by N days. M-x shift-dates will prompt you to enter the number of days to shift (enter a negative value to decrement). BTW I know this doesn't quite answer your question, but I think it's close.

(defun shift-date (date days)
   (time-add (time-to-seconds (days-to-time days))
             (time-to-seconds (org-time-string-to-time date)))))

(defun shift-dates (days)
  (interactive "nDays: ")
    (goto-char (point-min))
    (while (not (eobp))
      (forward-char 1)
      (when (looking-at iso8601--full-date-match)
        (let ((date (shift-date (match-string 0) days)))
            (while (looking-at "[^\s\\|\n]")
              (delete-char 1))
            (insert date)))))))

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.