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.
Asked
Active
Viewed 357 times
3
-
1This question is not about `elisp` and I've got the impression that the description of the `time-date` tag fits best. See the description of that tag, it includes handling of date formats. – Tobias Mar 30 '20 at 13:08
-
https://github.com/doublep/datetime supports Java format like yyyy-MM-dd, but doesn't support Emacs format like %Y-%m-%d. – xuchunyang Mar 30 '20 at 13:42
-
1Note that for your example format (and many others), the best you could do in general would be to check whether a string *might* be (intended to represent) a date in that format, as it could also be a different date in a different format resulting in the same string. – phils Mar 31 '20 at 03:58
-
1FWIW `format-time-string` is using [strftime(3)](https://linux.die.net/man/3/strftime) and (nowadays at least) there seems to be [strptime(3)](https://linux.die.net/man/3/strptime) 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? – phils Mar 31 '20 at 04:23
2 Answers
3
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"

xuchunyang
- 14,302
- 1
- 18
- 39
-
1
-
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. – xuchunyang Apr 01 '20 at 04:42
-
1I see that there's an old bug report https://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). – phils Apr 01 '20 at 06: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. – tmalsburg Apr 01 '20 at 07:48
2
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]\\)"

Tobias
- 32,569
- 1
- 34
- 75
-
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 use `string-match-p`. That info seems to be missing from the Q & A. Thx. – Drew Mar 30 '20 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. – tmalsburg Mar 30 '20 at 20:44