Update a field (#+LAST_MODIFIED: ) at save

Sure.

I added this part to my user-config in the init.el file.
(for spacemacs users that have their configuration in a .spacemacs file it should be in the user-config part of .spacemacs)

(add-hook 'before-save-hook #'zp/org-set-last-modified)

  (defun zp/org-find-time-file-property (property &optional anywhere)
    "Return the position of the time file PROPERTY if it exists.
When ANYWHERE is non-nil, search beyond the preamble."
    (save-excursion
      (goto-char (point-min))
      (let ((first-heading
             (save-excursion
               (re-search-forward org-outline-regexp-bol nil t))))
        (when (re-search-forward (format "^#\\+%s:" property)
                                 (if anywhere nil first-heading)
                                 t)
          (point)))))

  (defun zp/org-has-time-file-property-p (property &optional anywhere)
    "Return the position of time file PROPERTY if it is defined.
As a special case, return -1 if the time file PROPERTY exists but
is not defined."
    (when-let ((pos (zp/org-find-time-file-property property anywhere)))
      (save-excursion
        (goto-char pos)
        (if (and (looking-at-p " ")
                 (progn (forward-char)
                        (org-at-timestamp-p 'lax)))
            pos
          -1))))

  (defun zp/org-set-time-file-property (property &optional anywhere pos)
    "Set the time file PROPERTY in the preamble.
When ANYWHERE is non-nil, search beyond the preamble.
If the position of the file PROPERTY has already been computed,
it can be passed in POS."
    (when-let ((pos (or pos
                        (zp/org-find-time-file-property property))))
      (save-excursion
        (goto-char pos)
        (if (looking-at-p " ")
            (forward-char)
          (insert " "))
        (delete-region (point) (line-end-position))
        (let* ((now (format-time-string "[%Y-%m-%d %a %H:%M]")))
          (insert now)))))

  (defun zp/org-set-last-modified ()
    "Update the LAST_MODIFIED file property in the preamble."
    (when (derived-mode-p 'org-mode)
      (zp/org-set-time-file-property "LAST_MODIFIED")))
3 Likes

That’s great. Thank you !

Yes! This is a nice addition. It should be included in org-roam. Very useful.

A much simpler option is to use emacs’ in-built timestaamp functionality:

2 Likes

Could you show a example of config ?

that would simply be putting:

(require 'time-stamp)
(add-hook 'write-file-functions 'time-stamp)

in your config.

2 Likes

Is #+LAST_MODIFIED recognized by some other functions? I have tried to make time-stamp use the standard Org-mode #+date (I like lower case) keyword and only run it for Org mode files but I am not sure if #+date is allowed to have time.

(add-hook 'org-mode-hook
          (lambda () (add-hook 'before-save-hook 'time-stamp nil 'local)))

(add-hook 'org-mode-hook
          (lambda ()
            (set (make-local-variable 'time-stamp-pattern)
                 "8/^#\\+date: %%$")))

I just watched your tutorials, thanks for making them!

One of the first things I noticed was the LAST_MODIFIED property in your file headers, and immediately searched out your config to steal your code. I’ve wanted this for a long time, so thank you.

Using doom I do the following to update my timestamps:

(after! org
  (setq time-stamp-active t
    time-stamp-start "#\\+modified:[ \t]*"
    time-stamp-end "$"
    time-stamp-format "\[%04y-%02m-%02d %3a %02H:%02M\]")
(add-hook 'before-save-hook 'time-stamp))

I’m trying @alexv suggestion which works very well with default time-stamp-format:

  (add-hook 'org-mode-hook
    (lambda () (add-hook 'before-save-hook 'time-stamp nil 'local)))

  (add-hook 'org-mode-hook
    (lambda ()
      (set (make-local-variable 'time-stamp-pattern)
     	   "7/^#\\+EDITED: %%$"
	   )))

but does not work if changing time-stamp-format in last but one line:

   	        "7/^#\\+EDITED: [%04y-%02m-%02d %3a %02H:%02M]"

Any idea how to make it work?

(add-hook 'org-mode-hook (lambda ()
                             (setq-local time-stamp-active t
                                         time-stamp-start "#\\+EDITED:[ \t]*"
                                         time-stamp-end "$"
                                         time-stamp-format "\[%04y-%02m-%02d %3a %02H:%02M\]")
                             (add-hook 'before-save-hook 'time-stamp nil 'local)))

There’s a comment in the source code saying not to change time-stamp-pattern.

Thanks, but it does not work this way.

I know very little about elisp but I don’t understand what part says not to change time-stamp-pattern in original code?

@tea4or2wo

Mine is this based on Jethro’s suggestion, and works perfectly in my markdown files (it should work on any other text file types, like org)

(add-hook 'after-init-hook
          (lambda ()
            (my/load-md-roam)
            ;; time stamp
            (setq time-stamp-start "modified:[       ]+\\\\?")
            (setq time-stamp-end "$")
            (setq time-stamp-format "%Y-%m-%dT%H%M%S")
            (add-hook 'before-save-hook #'time-stamp)))

For where you can see the recommendation not to change the variable:

The documentation of the variable time-stamp-pattern – you should be able to view it via C-h v (describe-variable)

@nobiot Thanks, I understand now what @jethro meant, I have read time-stamp-pattern doc already.

I was presuming make-local-variable (Alex) or setq-local (Jethro) allows to edit the variable locally only.

Yes. I would use setq-local (just personal convenience; never used the other one).

Tried all the modification in this thread. Only @zaeph method works, no luck with all the time-stamp methods.

The time-stamp seems working though with the following:

  (add-hook 'before-save-hook 'time-stamp)

it will fill the date following Time-stamp: <> or Time-stamp: " ", and I can change the time-stamp-format too.

It just does not work with any other keywords. Did anybody else have this issue? I am using Emacs 27.1 in Arch Linux.

What’s the value of time-stamp-start?

If Time-stamp: <> works, then I suspect it’s the matter of configuring this variable – it’s a regex. Have a look at this documentation from Emacs.

This is what worked for me:

I was trying to use time-stamp and had issues as well.
turns out the issue was time-stamp wasn’t searching far enough into the file.
set time-stamp-line-limit to something higher than 8 (default). I set it to 18.
also I am using last_modified and added a space between last_modified and time-stamp.
the following matches

#+last_modified: [2020-12-28 Mon 02:58:51]

on line 9.

(add-hook 'org-mode-hook (lambda ()
                             (setq-local time-stamp-active t
                                         time-stamp-line-limit 18
                                         time-stamp-start "^#\\+last_modified: [ \t]*"
                                         time-stamp-end "$"
                                         time-stamp-format "\[%Y-%m-%d %a %H:%M:%S\]")
                             (add-hook 'before-save-hook 'time-stamp nil 'local)))

Hi, I’m new in org-roam, and stumbled upon this thread wanting to just update my #+LAST_MODIFIED: field in my org-roam files. I have seen many responses, which one would do the job at its simplest? Thanks!