Attached are the updated versions of my patches. Thank you for your suggestions.

On 7/13/25 14:00, Ihor Radchenko wrote:
for <time ...> tag, right?

Exactly. I've tried to improve the wording.

What is the motivation behind changing -- to EN DASH?

This is not really a change. Previously, `org-html-timestamp' would perform this replacement, but not `org-html-planning' nor `org-html-clock'. When refactoring the code to use a common function between these three functions, I decided to move the `replace-regexp-in-string` into the common `org-html--format-timestamp' rather than keep this replacement exclusive to `org-html-timestamp'. I could revert this, but think it's better this way for consistency.

I've tried to clarify what changed in the updated commit message, too.
From 94fc03eb61fcfa61c0654e28a8921f84e416f8c1 Mon Sep 17 00:00:00 2001
From: Lukas Epple <em...@lukasepple.de>
Date: Wed, 25 Jun 2025 15:45:43 +0200
Subject: [PATCH 5/5] lisp/ox-html.el: Use time element for timestamps in fancy
 html5

* ox-html.el (org-html--format-timestamp): Use time html element and its
datetime attribute when `org-html--html5-fancy-p'.
(org-html-datetime-formats): Add custom value that allows the user to
modify how `org-html--format-timestamp' renders the timestamp in the
HTML5 datetime attribute without affecting how it is displayed.
* testing/lisp/test-ox-html.el (ox-html/html5-fancy-timestamps): Add
simple test for the changed behavior (which only covers the behavior of
`org-html-timestamp').
* ORG-NEWS: Describe changed behavior of timestamp formatting with fancy
HTML5 rendering.
---
 etc/ORG-NEWS                 | 10 ++++++++++
 lisp/ox-html.el              | 37 ++++++++++++++++++++++++++++++------
 testing/lisp/test-ox-html.el | 18 ++++++++++++++++++
 3 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 4cb7561a8..a89e40fe7 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -658,6 +658,16 @@ will be defined as empty and not produce any metadata if their
 corresponding ~org-latex-with-author~, ~org-latex-with-title~, or
 ~org-latex-with-creator~ option is set to ~nil~.
 
+*** Fancy HTML5 export uses ~<time>~ element for timestamps
+Previously, timestamps would always be rendered inside a ~<span
+class="timestamp">~. Now, if both ~org-html-doctype~ is ~html5~ and
+~org-html-html5-fancy~ is enabled, org will now use the semantically
+appropriate ~<time>~ element. This will also use the ~timestamp~ class,
+but additionally set the ~datetime~ attribute with a machine readable
+variant of the timestamp. The format used for the attribute can be
+customized using ~org-html-datetime-attribute-date-timestamp-format~
+and ~org-html-datetime-attribute-full-timestamp-format~.
+
 * Version 9.7
 
 ** Important announcements and breaking changes
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index 9eeb6a1a7..be66f6bb3 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -1168,6 +1168,19 @@ See `format-time-string' for more information on its components."
   :package-version '(Org . "8.0")
   :type 'string)
 
+(defcustom org-html-datetime-formats '("%F" . "%FT%T")
+  "Formats used for the timestamp added as metadata to the time HTML element.
+This only has an effect when `org-html-html5-fancy' is enabled, but
+does not affect how the timestamp is displayed.  The format in CAR
+represents the timestamp used for timestamps without a time component,
+CDR the one for the full date and time.  Note that the HTML standard
+restricts what timestamp formats are considered valid for the datetime
+attribute.  See `format-time-string' for more information on its
+components."
+  :type '(cons string string)
+  :group 'org-export-html
+  :package-version '(Org . "9.8"))
+
 ;;;; Template :: Mathjax
 
 (defcustom org-html-mathjax-options
@@ -1811,12 +1824,24 @@ a value to `org-html-standalone-image-predicate'."
   "Format given TIMESTAMP for inclusion in an HTML document.
 INFO is a plist used as a communication channel.  Formatted timestamp
 will be wrapped in an element with class timestamp."
-  (replace-regexp-in-string
-   "--"
-   "&ndash;"
-   (format "<span class=\"timestamp\">%s</span>"
-           (org-html-plain-text (org-timestamp-translate timestamp)
-                                info))))
+  (let ((html-tag (if (org-html--html5-fancy-p info) "time" "span"))
+        (html-attrs (concat "class=\"timestamp\""
+                            (when (org-html--html5-fancy-p info)
+                              (format " datetime=\"%s\""
+                                      (org-format-timestamp
+                                       timestamp
+                                       (if (org-timestamp-has-time-p timestamp)
+                                           (cdr org-html-datetime-formats)
+                                           (car org-html-datetime-formats))))))))
+    (replace-regexp-in-string
+     "--"
+     "&ndash;"
+     (format "<%s %s>%s</%s>"
+             html-tag
+             html-attrs
+             (org-html-plain-text (org-timestamp-translate timestamp)
+                                  info)
+             html-tag))))
 
 ;;;; Table
 
diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el
index 3d786629a..15b09a31f 100644
--- a/testing/lisp/test-ox-html.el
+++ b/testing/lisp/test-ox-html.el
@@ -954,6 +954,24 @@ SCHEDULED: <2025-03-26 Wed> DEADLINE: <2025-03-27 Thu 13:00> CLOSED: [2025-03-25
                 "<span class=\"timestamp-kwd\">DEADLINE:</span> <span class=\"timestamp\">&lt;2025-03-27 Thu 13:00&gt; </span>"
                 "<span class=\"timestamp-kwd\">SCHEDULED:</span> <span class=\"timestamp\">&lt;2025-03-26 Wed&gt; </span>"))))))
 
+(ert-deftest ox-html/html5-fancy-timestamps ()
+  "Test rendering of timestamps with fancy HTML5 enabled"
+  (org-test-with-temp-text "
+[2025-06-25 Wed]
+<2025-06-25 Wed 19:10>
+"
+   (let ((export-buffer "*Test HTML Export")
+         (org-export-show-temporary-buffer nil)
+         (org-html-doctype "html5")
+         (org-html-html5-fancy t))
+     (org-export-to-buffer 'html export-buffer
+       nil nil nil t)
+     (with-current-buffer export-buffer
+       (mapc (lambda (s)
+               (should (= 1 (how-many (rx-to-string s)))))
+             '("<span class=\"timestamp-wrapper\"><time class=\"timestamp\" datetime=\"2025-06-25\">[2025-06-25 Wed]</time></span>"
+               "<span class=\"timestamp-wrapper\"><time class=\"timestamp\" datetime=\"2025-06-25T19:10:00\">&lt;2025-06-25 Wed 19:10&gt;</time></span>"))))))
+
 
 ;;; Postamble Format
 
-- 
2.50.0

From 55843e0c99793da3bb52a3145f785abeb5b5da8d Mon Sep 17 00:00:00 2001
From: Lukas Epple <em...@lukasepple.de>
Date: Fri, 21 Feb 2025 17:12:39 +0100
Subject: [PATCH 4/5] lisp/ox-html.el: Unify timestamp formatting

* lisp/ox-html.el (org-html--format-timestamp): Add new function for
handling the creation of <span class="timestamp"> elements.
(org-html-clock): Use `org-html--format-timestamp', except for the
duration string which can't use the same code.
(org-html-planning, org-html-timestamp): Use `org-html--format-timestamp'.
* lisp/testing/test-ox-html.el (ox-html/clock): Update expected output
since `org-html--format-timestamp' replaces -- with &ndash; in all cases
now, matching the previous behavior of `org-html-timestamp', but not
`org-html-clock'.
---
 lisp/ox-html.el              | 43 ++++++++++++++++++++++--------------
 testing/lisp/test-ox-html.el |  2 +-
 2 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index f60bda02b..9eeb6a1a7 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -1807,6 +1807,17 @@ is meant to be used as a predicate for `org-export-get-ordinal' or
 a value to `org-html-standalone-image-predicate'."
   (org-element-property :caption element))
 
+(defun org-html--format-timestamp (timestamp info)
+  "Format given TIMESTAMP for inclusion in an HTML document.
+INFO is a plist used as a communication channel.  Formatted timestamp
+will be wrapped in an element with class timestamp."
+  (replace-regexp-in-string
+   "--"
+   "&ndash;"
+   (format "<span class=\"timestamp\">%s</span>"
+           (org-html-plain-text (org-timestamp-translate timestamp)
+                                info))))
+
 ;;;; Table
 
 (defun org-html-htmlize-region-for-paste (beg end)
@@ -2651,17 +2662,17 @@ holding contextual information."
 
 ;;;; Clock
 
-(defun org-html-clock (clock _contents _info)
+(defun org-html-clock (clock _contents info)
   "Transcode a CLOCK element from Org to HTML.
 CONTENTS is nil.  INFO is a plist used as a communication
 channel."
   (format "<p>
 <span class=\"timestamp-wrapper\">
-<span class=\"timestamp-kwd\">%s</span> <span class=\"timestamp\">%s</span>%s
+<span class=\"timestamp-kwd\">%s</span> %s%s
 </span>
 </p>"
 	  org-clock-string
-	  (org-timestamp-translate (org-element-property :value clock))
+	  (org-html--format-timestamp (org-element-property :value clock) info)
 	  (let ((time (org-element-property :duration clock)))
 	    (and time (format " <span class=\"timestamp\">(%s)</span>" time)))))
 
@@ -3577,10 +3588,9 @@ channel."
 	 (when timestamp
 	   (let ((string (car pair)))
 	     (format "<span class=\"timestamp-kwd\">%s</span> \
-<span class=\"timestamp\">%s</span> "
+%s "
 		     string
-		     (org-html-plain-text (org-timestamp-translate timestamp)
-					  info))))))
+		     (org-html--format-timestamp timestamp info))))))
      `((,org-closed-string . ,(org-element-property :closed planning))
        (,org-deadline-string . ,(org-element-property :deadline planning))
        (,org-scheduled-string . ,(org-element-property :scheduled planning)))
@@ -3932,17 +3942,16 @@ information."
   "Transcode a TIMESTAMP object from Org to HTML.
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
-  (let* (
-         ;; Strip :post-blank
-         ;; It will be handled as a part of generic transcoder code
-         ;; so we should avoid double-counting post-blank.
-         (timestamp-no-blank
-          (org-element-put-property
-           (org-element-copy timestamp t)
-           :post-blank 0))
-         (value (org-html-plain-text (org-timestamp-translate timestamp-no-blank) info)))
-    (format "<span class=\"timestamp-wrapper\"><span class=\"timestamp\">%s</span></span>"
-	    (replace-regexp-in-string "--" "&ndash;" value))))
+  (let (
+        ;; Strip :post-blank
+        ;; It will be handled as a part of generic transcoder code
+        ;; so we should avoid double-counting post-blank.
+        (timestamp-no-blank
+         (org-element-put-property
+          (org-element-copy timestamp t)
+          :post-blank 0)))
+    (format "<span class=\"timestamp-wrapper\">%s</span>"
+	    (org-html--format-timestamp timestamp-no-blank info))))
 
 ;;;; Underline
 
diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el
index 55f457054..3d786629a 100644
--- a/testing/lisp/test-ox-html.el
+++ b/testing/lisp/test-ox-html.el
@@ -934,7 +934,7 @@ CLOCK: [2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] =>  0:05
         nil nil nil t)
       (with-current-buffer export-buffer
         (should (search-forward
-                 "<span class=\"timestamp-kwd\">CLOCK:</span> <span class=\"timestamp\">[2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] </span> <span class=\"timestamp\">(0:05)</span>"
+                 "<span class=\"timestamp-kwd\">CLOCK:</span> <span class=\"timestamp\">[2025-02-21 Fri 17:43]&ndash;[2025-02-21 Fri 17:48] </span> <span class=\"timestamp\">(0:05)</span>"
                  nil t))))))
 
 (ert-deftest ox-html/planning ()
-- 
2.50.0

From 4c390d45809a1ef62160752a85b9efae7ce45799 Mon Sep 17 00:00:00 2001
From: Lukas Epple <em...@lukasepple.de>
Date: Tue, 25 Mar 2025 19:30:34 +0100
Subject: [PATCH 3/5] testing/lisp/test-ox-html.el: Add test for rendering of
 planning

---
 testing/lisp/test-ox-html.el | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el
index c64dacae1..55f457054 100644
--- a/testing/lisp/test-ox-html.el
+++ b/testing/lisp/test-ox-html.el
@@ -937,6 +937,23 @@ CLOCK: [2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] =>  0:05
                  "<span class=\"timestamp-kwd\">CLOCK:</span> <span class=\"timestamp\">[2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] </span> <span class=\"timestamp\">(0:05)</span>"
                  nil t))))))
 
+(ert-deftest ox-html/planning ()
+  "Test rendering of timestamps in planning elements"
+  (org-test-with-temp-text "
+* Some Item
+SCHEDULED: <2025-03-26 Wed> DEADLINE: <2025-03-27 Thu 13:00> CLOSED: [2025-03-25 Tue 19:09]
+"
+    (let ((export-buffer "*Test HTML Export")
+          (org-export-show-temporary-buffer nil)
+          (org-export-with-planning t))
+      (org-export-to-buffer 'html export-buffer
+        nil nil nil t)
+      (with-current-buffer export-buffer
+        (mapc (lambda (s) (should (search-forward s nil t)))
+              '("<span class=\"timestamp-kwd\">CLOSED:</span> <span class=\"timestamp\">[2025-03-25 Tue 19:09]</span>"
+                "<span class=\"timestamp-kwd\">DEADLINE:</span> <span class=\"timestamp\">&lt;2025-03-27 Thu 13:00&gt; </span>"
+                "<span class=\"timestamp-kwd\">SCHEDULED:</span> <span class=\"timestamp\">&lt;2025-03-26 Wed&gt; </span>"))))))
+
 
 ;;; Postamble Format
 
-- 
2.50.0

From 7c963398c341ee82a0a1c9302c34f84235c9c992 Mon Sep 17 00:00:00 2001
From: Lukas Epple <em...@lukasepple.de>
Date: Tue, 25 Mar 2025 19:06:51 +0100
Subject: [PATCH 2/5] testing/lisp/test-ox-html.el: Add test for clock
 rendering

---
 testing/lisp/test-ox-html.el | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el
index d969a5669..c64dacae1 100644
--- a/testing/lisp/test-ox-html.el
+++ b/testing/lisp/test-ox-html.el
@@ -918,6 +918,25 @@ $x$"
                 "<span class=\"timestamp\">&lt;2025-02-18 Tue 23:59&gt;</span>"
                 "<span class=\"timestamp\">[2025-02-17 Mon 17:00]&ndash;[2025-02-17 Mon 19:00]</span>"))))))
 
+(ert-deftest ox-html/clock ()
+  "Test rendering of clock elements"
+  (org-test-with-temp-text "
+* Test
+:LOGBOOK:
+CLOCK: [2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] =>  0:05
+:END:
+"
+    (let ((export-buffer "*Test HTML Export")
+          (org-export-show-temporary-buffer nil)
+          (org-export-with-drawers t)
+          (org-export-with-clocks t))
+      (org-export-to-buffer 'html export-buffer
+        nil nil nil t)
+      (with-current-buffer export-buffer
+        (should (search-forward
+                 "<span class=\"timestamp-kwd\">CLOCK:</span> <span class=\"timestamp\">[2025-02-21 Fri 17:43]--[2025-02-21 Fri 17:48] </span> <span class=\"timestamp\">(0:05)</span>"
+                 nil t))))))
+
 
 ;;; Postamble Format
 
-- 
2.50.0

From 72aad0c90723727979d0953e6926972c45742cf5 Mon Sep 17 00:00:00 2001
From: Lukas Epple <em...@lukasepple.de>
Date: Fri, 21 Feb 2025 19:20:09 +0100
Subject: [PATCH 1/5] testing/lisp/test-ox-html.el: Add test for timestamp
 rendering

---
 testing/lisp/test-ox-html.el | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el
index ec8a7b18f..d969a5669 100644
--- a/testing/lisp/test-ox-html.el
+++ b/testing/lisp/test-ox-html.el
@@ -894,6 +894,29 @@ $x$"
         (with-current-buffer export-buffer
           (libxml-parse-xml-region (point-min) (point-max))))))))
 
+
+;;; Rendering Timestamps
+
+(ert-deftest ox-html/plain-timestamps ()
+  "Test rendering of timestamps (outside of clock/planning)"
+  (org-test-with-temp-text "
+- [2025-01-31 Fri]
+- [2025-01-31 Fri 14:00]
+- <2025-02-18 Tue>
+- <2025-02-18 Tue 23:59>
+- [2025-02-17 Tue 17:00]--[2025-02-17 Fri 19:00]
+"
+    (let ((export-buffer "*Test HTML Export")
+          (org-export-show-temporary-buffer nil))
+      (org-export-to-buffer 'html export-buffer
+        nil nil nil t)
+      (with-current-buffer export-buffer
+        (mapc (lambda (s) (should (search-forward s nil t)))
+              '("<span class=\"timestamp\">[2025-01-31 Fri]</span>"
+                "<span class=\"timestamp\">[2025-01-31 Fri 14:00]</span>"
+                "<span class=\"timestamp\">&lt;2025-02-18 Tue&gt;</span>"
+                "<span class=\"timestamp\">&lt;2025-02-18 Tue 23:59&gt;</span>"
+                "<span class=\"timestamp\">[2025-02-17 Mon 17:00]&ndash;[2025-02-17 Mon 19:00]</span>"))))))
 
 
 ;;; Postamble Format
-- 
2.50.0

Reply via email to