I do not like AI, but the sheer convenience of it has grabbed my attention. For tweaking my GNU/Linux system, the answers come in a few seconds, saving me sometimes hours of searching.
Today I realized I had copied files from another system without specifying to save file metadata. Could I set the file times of files with timestamps using the "Time-stamp: < >" utility in Emacs? I searched, and, I must freely admit, I asked for more info from the AI helper of Google Search. After about 7 or more rounds of providing feedback about a regular expression that did not work, these functions were provided. Wow. The last two of these is probably sufficient for my needs. I will change the names.
(defun my/set-file-time-final ()
"Sets file time by finding 'Time-stamp' and extracting only the date/time. From Google AI."
(interactive)
(save-excursion
(goto-char (point-min))
;; 1. Find the literal word "Time-stamp" anywhere (ignores #, brackets, etc.)
(if (re-search-forward "Time-stamp" nil t)
;; 2. Look for the first date pattern (YYYY-MM-DD HH:MM:SS) that follows it
(if (re-search-forward "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}[ T][0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)" nil t)
(let* ((ts-string (match-string 1))
(parsed (parse-time-string ts-string))
(lisp-time (when parsed (encode-time parsed))))
(if (and (buffer-file-name) lisp-time)
(progn
(set-file-times (buffer-file-name) lisp-time)
(message "Success! Mtime set to %s (Username ignored)" ts-string))
(error "Parsed '%s' but system couldn't use it" ts-string)))
(error "Found 'Time-stamp' but no YYYY-MM-DD HH:MM:SS format found after it"))
(error "The word 'Time-stamp' is missing from this file"))))
(defun my/dired-set-times-from-stamps ()
"In Dired, run 'my/set-file-time-final' on all marked files. From Google AI."
(interactive)
(let ((files (dired-get-marked-files)))
(dolist (file files)
(with-current-buffer (find-file-noselect file)
(condition-case err
(progn
(my/set-file-time-final)
(save-buffer))
(error (message "Skipping %s: %s" file (error-message-string err))))))
(revert-buffer) ; Refresh the Dired buffer to show the new times
(message "Done processing marked files.")))
(defun my/dired-set-times-from-stamps ()
"In Dired, sync system times for marked files. Files without stamps are ignored."
(interactive)
(let ((files (dired-get-marked-files))
(skipped 0)
(updated 0))
(dolist (file files)
(with-current-buffer (find-file-noselect file)
;; Use condition-case to catch files that don't have the stamp
(condition-case nil
(progn
(my/set-file-time-final)
(save-buffer)
(setq updated (1+ updated)))
(error (setq skipped (1+ skipped))))))
(revert-buffer)
(message "Done! Updated: %d | Skipped (no stamp): %d" updated skipped)))
(defun my/set-file-time-final ()
"From Google AI summary session after Google search. Sets file time from header. Handles: Created YYYY-MM-DD Day HH:MM:"
(interactive)
(save-excursion
(goto-char (point-min))
;; 1. Search for 'Time-stamp' or 'Created'
(if (re-search-forward "\\(Time-stamp\\|Created\\):?" nil t)
;; 2. Match the date (YYYY-MM-DD)
;; Then skip any day-of-week (like Tue)
;; Then grab the time (HH:MM or HH:MM:SS)
(if (re-search-forward "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)[^0-9]*\\([0-9]\\{2\\}:[0-9]\\{2\\}\\(?::[0-9]\\{2\\}\\)?\\)" nil t)
(let* ((date-part (match-string 1))
(time-part (match-string 2))
(ts-string (concat date-part " " time-part))
(parsed (parse-time-string ts-string))
(lisp-time (when parsed (encode-time parsed))))
(if (and (buffer-file-name) lisp-time)
(progn
(set-file-times (buffer-file-name) lisp-time)
(message "Success! Mtime set to %s" ts-string))
(error "Parsed '%s' but couldn't use it" ts-string)))
(error "Found keyword but date/time format was unexpected"))
(error "No 'Time-stamp' or 'Created' header found"))))
(defun my/dired-set-times-from-stamps ()
"From Google AI summary session. This calls the function my/set-file-time-final. In Dired, sync system modification times for all marked files
based on the 'Created' or 'Time-stamp' lines inside them."
(interactive)
(let ((files (dired-get-marked-files))
(updated 0)
(skipped 0))
(dolist (file files)
;; Open the file in a buffer without switching to it
(with-current-buffer (find-file-noselect file)
(condition-case nil
(progn
;; Call your working 'within-the-file' function
(my/set-file-time-final)
;; Save the buffer to ensure metadata is flushed to disk
(save-buffer)
(setq updated (1+ updated)))
(error
(setq skipped (1+ skipped))))))
;; Refresh the Dired buffer so you see the new dates immediately
(revert-buffer)
(message "Batch complete: %d updated, %d skipped (no stamp found)." updated skipped)))
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "T") 'my/dired-set-times-from-stamps))