reddit / emacs

Format parsed time string?

pragmat1c1 · 2022-06-13 18:12
`parse-time-string` takes a time string and parses the constituents of the date/time string: (parse-time-string "Sunday, 12 June 2022") (nil nil nil 12 6 2022 0 -1 nil) Now is there a way to format this into a string again? I could take each part and generate a new string, but there might be a function that I am not aware of? ## Edit: My solution for this ;; (parse-time-string STRING) return a list like this: ;; (SEC MIN HOUR DAY MON YEAR DOW DST TZ) (defun ugt-convert-time-string (time-string) "Converts TIME-STRING into one with this format: yyyy-mm-dd Day" (cl-flet ((get-day (dow) (pcase dow (0 "Sun") (1 "Mon") (2 "Tue") (3 "Wed") (4 "Thu") (5 "Fri") (6 "Sat")))) (pcase-let ((`(,sec ,min ,hour ,day ,mon ,year ,dow ,dst ,tz) (parse-time-string time-string))) (format "%04d-%02d-%02d %s" year mon day (get-day dow))))) (ugt-convert-time-string "Sunday, 12 June 2022") "2022-06-12 Sun" ## Edit 2: Here's a better solution with the help of u/flyin1501 ;;;;; convert marked (arbitrary) date string into canonical form yyyy-dd-mm Wkd HH:MM:SS ;; `encode-time' ;; TIME is a list (SEC MIN HOUR DAY MON YEAR IGNORED DST ZONE). ;; `parse-time-string' ;; Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ). (defun ugt-convert-timestring-to-canonical (rbegin rend) "Convert arbitrary time-string into `yyyy-mm-dd Wkd'." (interactive "r") (if (use-region-p) (pcase-let* ((`,time-string (buffer-substring-no-properties rbegin rend)) ;; unpack list elements (`(,sec ,min ,hour ,day ,mon ,year ,dow ,dst ,tz) (parse-time-string time-string))) ;; reconstruct time-list with TZ=nil (let* ((time-list (list (or sec 0) (or min 0) (or hour 0) day mon year dow dst nil ;; add `nil' to end, as TZ needs to be `nil' )) (new-time-string (format-time-string "[%F %a %H:%M:%S]" (encode-time time-list)))) (kill-new new-time-string) (message "Converted %s to %s" time-string new-time-string)))))
Alan_Shutko · 2022-06-13 20:35
Check out "(elisp) Time Parsing" in the ELisp Manual. You are most likely looking for `format-time-string`.
flyin1501 · 2022-06-13 23:23
(format-time-string "%F %a" (encode-time (mapcar (lambda (x) (if x x 0)) (parse-time-string "Sunday, 12 June 2022")))) ;; => "2022-06-12 Sun" - format-time-string takes an encoded time value - parse-time-string returns a decoded time that, when fed to encode-time, *raises an error*. Replace the nil values ourselves. - format-time-string takes its own style of format strings, see its docstring for details. Edit: this is wrong. The last value in the decoded time should stay nil. What should be done is something like `org-fix-decoded-time`: (defun org-fix-decoded-time (time) "Set 0 instead of nil for the first 6 elements of time. Don't touch the rest." (let ((n 0)) (mapcar (lambda (x) (if (< (setq n (1+ n)) 7) (or x 0) x)) time))) Why this isn't builtin is something I do not understand.
pragmat1c1 · 2022-06-13 23:56
Excellent! Thank you very muchvv
iwaka · 2022-06-14 00:16
Instead of that mapcar with a lambda, you could use an `or`: (or (parse...) 0) Shorter and does the same thing.
deaddyfreddy · 2022-06-14 11:18
> (if x x 0) `(or x 0)`
pragmat1c1 · 2022-06-14 12:24
>(format-time-string "%F %a" (encode-time (mapcar (lambda (x) (if x x 0)) (parse-time-string "Sunday, 12 June 2022")))) Just found out that this'll change the last slot in here: (parse-time-string "Friday, 10. June 2022 at 22:04:13") (13 4 22 10 6 2022 5 -1 nil) The last `nil` will become `0`, and that means, that `encode-time` will interpret this differently: ``` ;; `encode-time' ;; TIME is a list (SEC MIN HOUR DAY MON YEAR IGNORED DST ZONE). ;; `parse-time-string' ;; Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ). ``` See here (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) Convert TIME to a timestamp. TIME is a list (SECOND MINUTE HOUR DAY MONTH YEAR IGNORED DST ZONE). in the style of ‘decode-time’, so that (encode-time (decode-time ...)) works. In this list, ZONE can be nil for Emacs local time, t for Universal Time, ‘wall’ for system wall clock time, or a string as in the TZ environment variable. It needs to be `nil` to be in Emacs local time.
flyin1501 · 2022-06-14 23:25
`or` does not traverse a list. (or (parse-time-string "2022-01-01") 0) ;; => (nil nil nil 1 1 2022 nil -1 nil) (mapcar (lambda (x) (or x 0)) (parse-time-string "2022-01-01")) ;; => (0 0 0 1 1 2022 0 -1 0) No, you can't use `apply` on it as `apply` only works with functions. (The approach I took is still wrong because the final nil should remain a nil.)
flyin1501 · 2022-06-14 23:31
Ah, I took a bad shortcut... If your package already depends on Org you can probably use `org-fix-decoded-time`, or you could copy it (basically count up for every replacement to keep track of the index and stop replacing for index >= 7).
iwaka · 2022-06-15 15:03
Ah, my bad, I should have checked it in Emacs first. Thanks for clarifying!