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.

  • 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 Mar 30 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 Mar 30 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 Mar 31 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 Mar 31 at 4:23
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"
| improve this answer | |
  • 1
    Excellent! Could you please contribute this feature upstream to Emacs? – phils Apr 1 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 Apr 1 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 Apr 1 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 Apr 1 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]\\)"
| improve this answer | |
  • 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 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 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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