I have a string and would like to check whether it contains a date in a given date format, e.g. "%Y-%m-%d", with format as for format-time-string
. This is for a package and users can configure the date format freely. So I cannot just hard-code a regular expression.
2 Answers
It seems format-time-string
uses the same format as strftime(3), and strptime(3) is the reverse of strftime(3), so I wrote an Emacs dynamic module just for strptime(3), for example,
(strptime "2020-04-01" "%Y-%m-%d")
;; => (0 0 0 1 4 2020 3 nil 0)
The result is (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF)
, you can feed it to encode-time
(apply #'encode-time (strptime "2020-04-01" "%Y-%m-%d"))
;; => (24195 55680)
(format-time-string
"%Y-%m-%d"
(apply #'encode-time (strptime "2020-04-01" "%Y-%m-%d")))
;; => "2020-04-01"
-
1Excellent! Could you please contribute this feature upstream to Emacs?– philsCommented Apr 1, 2020 at 4:18
-
1@phils I am not confident to do that as I don't have much experience in C. Though I would be very happy if I can contribute. Commented Apr 1, 2020 at 4:42
-
1I see that there's an old bug report debbugs.gnu.org/cgi/bugreport.cgi?bug=25112 except it was closed on the basis of
date-to-time
existing (which isn't the same thing, but obviously sufficed for those purposes).– philsCommented Apr 1, 2020 at 6:00 -
This is great! I agree with @phils that this should be part of Emacs. I had one patch accepted to Emacs and it was a surprisingly good experience, mainly thanks to Eli Zaretskii who gave me valuable input. Commented Apr 1, 2020 at 7:48
A combination of regexp-quote
and format-spec
should help in your case. Especially, format-spec
works with format specifiers of the format sequences consisting of one %
character and one letter.
(defun my-format-spec-to-regexp (format)
"Convert FORMAT with placeholders %Y, %m, and %d into a regexp."
(format-spec (regexp-quote format)
'((?Y . "\\(?:[0-9][0-9]\\)\\{1,2\\}")
(?m . "\\(?:1[012]\\|0?[1-9]\\)")
(?d . "\\(?:0?[1-9]\\|[12][0-9]\\|3[01]\\)"))))
;; Some test:
(insert (prin1-to-string (my-format-spec-to-regexp "%Y\\%m\\%d")))
"\\(?:[0-9][0-9]\\)\\{1,2\\}\\\\\\(?:1[012]\\|0?[1-9]\\)\\\\\\(?:0?[1-9]\\|[12][0-9]\\|3[01]\\)"
-
For the benefit of readers who might not be aware of it, please consider adding to this answer that to test whether a given time-date string is in the given input (argument
FORMAT
), you would usestring-match-p
. That info seems to be missing from the Q & A. Thx.– DrewCommented Mar 30, 2020 at 16:32 -
Thanks for the idea, but with this approach one would have to completely reverse-engineer
format-time-string
which is fantastically complex (e.g., locale-dependent). Seems like a major effort and with a lot of room for bugs. Unfortunately, I don't have the resources to do that. Commented Mar 30, 2020 at 20:44
elisp
and I've got the impression that the description of thetime-date
tag fits best. See the description of that tag, it includes handling of date formats.format-time-string
is using strftime(3) and (nowadays at least) there seems to be strptime(3) which does the inverse (which is more or less what you're asking for), so if that library call is available everywhere that modern Emacs cares about, this is probably a feature which could be added?