3

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.

4
  • 1
    This 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
    Commented Mar 30, 2020 at 13:08
  • github.com/doublep/datetime supports Java format like yyyy-MM-dd, but doesn't support Emacs format like %Y-%m-%d.
    – xuchunyang
    Commented Mar 30, 2020 at 13:42
  • 1
    Note 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
    Commented Mar 31, 2020 at 3:58
  • 1
    FWIW 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?
    – phils
    Commented Mar 31, 2020 at 4:23

2 Answers 2

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"
4
  • 1
    Excellent! Could you please contribute this feature upstream to Emacs?
    – phils
    Commented 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.
    – xuchunyang
    Commented Apr 1, 2020 at 4:42
  • 1
    I 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).
    – phils
    Commented 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.
    – tmalsburg
    Commented Apr 1, 2020 at 7: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]\\)"
2
  • 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
    Commented 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.
    – tmalsburg
    Commented Mar 30, 2020 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.