:group 'extensions)
-(defcustom time-stamp-format "%Y-%02m-%02d %02H:%02M:%02S %l"
+(defcustom time-stamp-format "%Y-%m-%d %H:%M:%S %l"
"Format of the string inserted by \\[time-stamp].
-This is a string, used verbatim except for character sequences beginning
-with %, as follows.
-
-%:A weekday name: `Monday' %#A gives uppercase: `MONDAY'
-%3a abbreviated weekday: `Mon' %#a gives uppercase: `MON'
-%:B month name: `January' %#B gives uppercase: `JANUARY'
-%3b abbreviated month: `Jan' %#b gives uppercase: `JAN'
-%02d day of month
-%02H 24-hour clock hour
-%02I 12-hour clock hour
-%02m month number
-%02M minute
-%#p `am' or `pm' %P gives uppercase: `AM' or `PM'
-%02S seconds
+The string is inserted verbatim except for character sequences beginning
+with %, which are converted as follows:
+
+%A weekday name: `Monday' %a abbreviated weekday name: `Mon'
+%B month name: `January' %b abbreviated month name: `Jan'
+%d day of month
+%H 24-hour clock hour %I 12-hour clock hour
+%m month number
+%M minute
+%p `AM' or `PM'
+%S seconds
%w day number of week, Sunday is 0
-%02y 2-digit year %Y 4-digit year
-%Z time zone name: `EST' %#Z gives lowercase: `est'
-%5z time zone offset: `-0500' (since Emacs 27; see note below)
+%Y 4-digit year %y 2-digit year
+%Z time zone name: `EST'
+%-z zone offset with hour: `-08' %:::z adds colons as needed: `+05:30'
+%5z zone offset with mins: `-0800' %:z adds colon: `-08:00'
Non-date items:
-%% a literal percent character: `%'
-%f file name without directory %F absolute file name
-%l login name %L full name of logged-in user
-%q unqualified host name %Q fully-qualified host name
+%% literal percent character: \"%\"
+%f file name without directory %F absolute file name
+%l login name %L full name of logged-in user
+%q unqualified host name %Q fully-qualified host name
%h mail host name
-Decimal digits between the % and the type character specify the
-field width. Strings are truncated on the right.
-A leading zero in the field width zero-fills a number.
+A \"#\" after the % changes the case of letters. For example, on Mondays,
+in the default locale, \"%#A\" converts to \"MONDAY\".
-For example, to get a common format used by the `date' command,
-use \"%3a %3b %2d %02H:%02M:%02S %Z %Y\".
+Decimal digits before the type character specify the minimum field
+width. A \"0\" before the field width adds insignificant zeroes
+as appropriate, otherwise the padding is done with spaces.
+
+If no padding is specified, a field that can be one or two digits is padded
+with \"0\" to two digits if necessary. Follow the % with \"_\" to pad with a
+space instead, or follow it with \"-\" to suppress this padding entirely.
+Thus, on the 5th of the month, the day is converted as follows:
+
+\"%d\" -> \"05\"
+\"%_d\" -> \" 5\"
+\"%-d\" -> \"5\"
+
+For example, to get a common format used by the \"date\" command,
+use \"%a %b %_d %H:%M:%S %Z %Y\".
The values of non-numeric formatted items depend on the locale
setting recorded in `system-time-locale' and `locale-coding-system'.
-The examples here are for the default (`C') locale.
+The examples here are for the default (\"C\") locale.
`time-stamp-time-zone' controls the time zone used.
-The default padding of some formats has changed to be more compatible
-with format-time-string. To be compatible with older versions of Emacs,
-specify a padding width (as shown) or use the : modifier to request the
-transitional behavior (again, as shown).
-
-The behavior of `%5z' is new in Emacs 27. If your files might be
-edited by older versions of Emacs also, do not use this format yet."
+Some of the conversions recommended here work only in Emacs 27 or later.
+If your files might be edited by older versions of Emacs also, you should
+limit yourself to the formats recommended by that older version."
:type 'string
:version "27.1")
;;;###autoload(put 'time-stamp-format 'safe-local-variable 'stringp)
// time-stamp-pattern: \"-9/^Last modified: %%$\"
(sets `time-stamp-line-limit', `time-stamp-start' and `time-stamp-end')
-@c time-stamp-pattern: \"@set Time-stamp: %:B %1d, %Y$\"
+@c time-stamp-pattern: \"@set Time-stamp: %B %-d, %Y$\"
(sets `time-stamp-start', `time-stamp-format' and `time-stamp-end')
%% time-stamp-pattern: \"newcommand{\\\\\\\\timestamp}{%%}\"
- (sets `time-stamp-start'and `time-stamp-end')
+ (sets `time-stamp-start' and `time-stamp-end')
See also `time-stamp-count' and `time-stamp-inserts-lines'.")
;;; At all times, all the formats recommended in the doc string
;;; of time-stamp-format will work not only in the current version of
;;; Emacs, but in all versions that have been released within the past
-;;; two years.
+;;; five years.
;;; The : modifier is a temporary conversion feature used to resolve
;;; ambiguous formats--formats that are changing (over time) incompatibly.
(defun time-stamp-string-preprocess (format &optional time)
(time-stamp--format "%#a" time)
(time-stamp--format "%a" time))))
((eq cur-char ?A)
- (if (or change-case upcase (not (string-equal field-width
- "")))
- (time-stamp--format "%#A" time)
- (time-stamp--format "%A" time)))
+ (if (and (>= (string-to-number field-width) 1)
+ (<= (string-to-number field-width) 3)
+ (not flag-minimize)
+ (not flag-pad-with-spaces))
+ (progn
+ (time-stamp-conv-warn "%3A" "%#a")
+ (time-stamp--format "%#a" time))
+ (if (or change-case upcase)
+ (time-stamp--format "%#A" time)
+ (if (or (> alt-form 0)
+ flag-minimize flag-pad-with-spaces
+ (string-equal field-width ""))
+ (time-stamp--format "%A" time)
+ (time-stamp-conv-warn (format "%%%sA" field-width)
+ (format "%%#%sA" field-width))
+ (time-stamp--format "%#A" time)))))
((eq cur-char ?b) ;month name
(if (> alt-form 0)
(if (string-equal field-width "")
(time-stamp--format "%#b" time)
(time-stamp--format "%b" time))))
((eq cur-char ?B)
- (if (or change-case upcase (not (string-equal field-width
- "")))
- (time-stamp--format "%#B" time)
- (time-stamp--format "%B" time)))
+ (if (and (>= (string-to-number field-width) 1)
+ (<= (string-to-number field-width) 3)
+ (not flag-minimize)
+ (not flag-pad-with-spaces))
+ (progn
+ (time-stamp-conv-warn "%3B" "%#b")
+ (time-stamp--format "%#b" time))
+ (if (or change-case upcase)
+ (time-stamp--format "%#B" time)
+ (if (or (> alt-form 0)
+ flag-minimize flag-pad-with-spaces
+ (string-equal field-width ""))
+ (time-stamp--format "%B" time)
+ (time-stamp-conv-warn (format "%%%sB" field-width)
+ (format "%%#%sB" field-width))
+ (time-stamp--format "%#B" time)))))
((eq cur-char ?d) ;day of month, 1-31
(time-stamp-do-number cur-char alt-form field-width time))
((eq cur-char ?H) ;hour, 0-23
((eq cur-char ?w) ;weekday number, Sunday is 0
(time-stamp--format "%w" time))
((eq cur-char ?y) ;year
- (if (> alt-form 0)
- (string-to-number (time-stamp--format "%Y" time))
- (if (or (string-equal field-width "")
- (<= (string-to-number field-width) 2))
- (string-to-number (time-stamp--format "%y" time))
- (time-stamp-conv-warn (format "%%%sy" field-width) "%Y")
- (string-to-number (time-stamp--format "%Y" time)))))
+ (if (= alt-form 0)
+ (if (or (string-equal field-width "")
+ (<= (string-to-number field-width) 2))
+ (string-to-number (time-stamp--format "%y" time))
+ (time-stamp-conv-warn
+ (format "%%%sy" field-width) "%Y")
+ (string-to-number (time-stamp--format "%Y" time)))
+ (time-stamp-conv-warn "%:y" "%Y")
+ (string-to-number (time-stamp--format "%Y" time))))
((eq cur-char ?Y) ;4-digit year
(string-to-number (time-stamp--format "%Y" time)))
((eq cur-char ?z) ;time zone offset
(or buffer-file-name
time-stamp-no-file))
((eq cur-char ?s) ;system name, legacy
+ (time-stamp-conv-warn "%s" "%Q")
(system-name))
((eq cur-char ?u) ;user name, legacy
+ (time-stamp-conv-warn "%u" "%l")
(user-login-name))
((eq cur-char ?U) ;user full name, legacy
+ (time-stamp-conv-warn "%U" "%L")
(user-full-name))
((eq cur-char ?l) ;login name
(user-login-name))
))
(and (numberp field-result)
(= alt-form 0)
- (string-equal field-width "")
+ (or (string-equal field-width "")
+ (string-equal field-width "0"))
;; no width provided; set width for default
(setq field-width "02"))
- (let ((padded-result
- (format (format "%%%s%c"
- field-width
- (if (numberp field-result) ?d ?s))
- (or field-result ""))))
- (let* ((initial-length (length padded-result))
- (desired-length (if (string-equal field-width "")
- initial-length
- (string-to-number field-width))))
- (if (> initial-length desired-length)
- ;; truncate strings on right
- (if (and (stringp field-result)
- (not (eq cur-char ?z))) ;offset does not truncate
- (substring padded-result 0 desired-length)
- padded-result) ;numbers don't truncate
- padded-result)))))
+ (format (format "%%%s%c"
+ field-width
+ (if (numberp field-result) ?d ?s))
+ (or field-result ""))))
(t
(char-to-string cur-char)))))
(setq ind (1+ ind)))
formatted according to the preceding parameters.
This is an internal function used by `time-stamp'."
- ;; The caller of this function must have already parsed the %z
+ ;; Callers of this function need to have already parsed the %z
;; format string; this function accepts just the parts of the format.
;; `time-stamp-string-preprocess' is the full-fledged parser normally
;; used. The unit test (in time-stamp-tests.el) defines the simpler
,@body)))
(defmacro with-time-stamp-test-time (reference-time &rest body)
- "Force any contained time-stamp call to use time REFERENCE-TIME."
+ "Force `time-stamp' to use time REFERENCE-TIME while evaluating BODY."
(declare (indent 1) (debug t))
`(cl-letf*
((orig-time-stamp-string-fn (symbol-function 'time-stamp-string))
,@body))
(defmacro with-time-stamp-system-name (name &rest body)
- "Force (system-name) to return NAME while evaluating BODY."
+ "Force `system-name' to return NAME while evaluating BODY."
(declare (indent 1) (debug t))
`(cl-letf (((symbol-function 'system-name)
(lambda () ,name)))
,@body))
(defmacro time-stamp-should-warn (form)
- "Similar to `should' but verifies that a format warning is generated."
+ "Similar to `should' and also verify that FORM generates a format warning."
(declare (debug t))
`(let ((warning-count 0))
(cl-letf (((symbol-function 'time-stamp-conv-warn)
(ert-deftest time-stamp-format-day-of-week ()
"Test time-stamp formats for named day of week."
(with-time-stamp-test-env
- (let ((Mon (format-time-string "%a" ref-time1 t))
- (MON (format-time-string "%^a" ref-time1 t))
- (Monday (format-time-string "%A" ref-time1 t))
- (MONDAY (format-time-string "%^A" ref-time1 t)))
- ;; implemented and documented since 1997
- (should (equal (time-stamp-string "%3a" ref-time1) Mon))
+ (let* ((Mon (format-time-string "%a" ref-time1 t))
+ (MON (format-time-string "%^a" ref-time1 t))
+ (Monday (format-time-string "%A" ref-time1 t))
+ (MONDAY (format-time-string "%^A" ref-time1 t))
+ (p4-Mon (string-pad Mon 4 ?\s t))
+ (p4-MON (string-pad MON 4 ?\s t))
+ (p10-Monday (string-pad Monday 10 ?\s t))
+ (p10-MONDAY (string-pad MONDAY 10 ?\s t)))
+ ;; implemented and recommended since 1997
(should (equal (time-stamp-string "%#A" ref-time1) MONDAY))
- ;; documented 1997-2019
- (should (equal (time-stamp-string "%3A" ref-time1)
- (substring MONDAY 0 3)))
+ (should (equal (time-stamp-string "%#10A" ref-time1) p10-MONDAY))
+ ;; implemented since 1997, recommended 1997-2024
+ (should (equal (time-stamp-string "%3a" ref-time1) Mon))
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:a" ref-time1) Monday))
- ;; implemented since 2001, documented since 2019
+ ;; recommended 1997-2019, warned since 2024, will change
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%3A" ref-time1) MON)))
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%10A" ref-time1) p10-MONDAY)))
+ ;; implemented since 2001, recommended since 2019
(should (equal (time-stamp-string "%#a" ref-time1) MON))
+ (should (equal (time-stamp-string "%#3a" ref-time1) MON))
+ (should (equal (time-stamp-string "%#4a" ref-time1) p4-MON))
+ ;; implemented since 2001, recommended 2019-2024
(should (equal (time-stamp-string "%:A" ref-time1) Monday))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; broken 2019-2024
+ (should (equal (time-stamp-string "%:10A" ref-time1) p10-Monday))
+ ;; broken in 2019, changed in 2024
+ (should (equal (time-stamp-string "%-A" ref-time1) Monday))
+ (should (equal (time-stamp-string "%_A" ref-time1) Monday))
+ ;; allowed but not recommended since 2019 (warned 1997-2019)
(should (equal (time-stamp-string "%^A" ref-time1) MONDAY))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%a" ref-time1) Mon))
+ (should (equal (time-stamp-string "%4a" ref-time1) p4-Mon))
+ (should (equal (time-stamp-string "%04a" ref-time1) p4-Mon))
+ (should (equal (time-stamp-string "%A" ref-time1) Monday))
+ ;; warned 1997-2019, changed in 2019
(should (equal (time-stamp-string "%^a" ref-time1) MON))
- (should (equal (time-stamp-string "%A" ref-time1) Monday)))))
+ (should (equal (time-stamp-string "%^4a" ref-time1) p4-MON)))))
(ert-deftest time-stamp-format-month-name ()
"Test time-stamp formats for month name."
(with-time-stamp-test-env
- (let ((Jan (format-time-string "%b" ref-time1 t))
- (JAN (format-time-string "%^b" ref-time1 t))
- (January (format-time-string "%B" ref-time1 t))
- (JANUARY (format-time-string "%^B" ref-time1 t)))
- ;; implemented and documented since 1997
- (should (equal (time-stamp-string "%3b" ref-time1)
- (substring January 0 3)))
+ (let* ((Jan (format-time-string "%b" ref-time1 t))
+ (JAN (format-time-string "%^b" ref-time1 t))
+ (January (format-time-string "%B" ref-time1 t))
+ (JANUARY (format-time-string "%^B" ref-time1 t))
+ (p4-Jan (string-pad Jan 4 ?\s t))
+ (p4-JAN (string-pad JAN 4 ?\s t))
+ (p10-January (string-pad January 10 ?\s t))
+ (p10-JANUARY (string-pad JANUARY 10 ?\s t)))
+ ;; implemented and recommended since 1997
(should (equal (time-stamp-string "%#B" ref-time1) JANUARY))
- ;; documented 1997-2019
- (should (equal (time-stamp-string "%3B" ref-time1)
- (substring JANUARY 0 3)))
+ (should (equal (time-stamp-string "%#10B" ref-time1) p10-JANUARY))
+ ;; implemented since 1997, recommended 1997-2024
+ (should (equal (time-stamp-string "%3b" ref-time1) Jan))
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:b" ref-time1) January))
- ;; implemented since 2001, documented since 2019
+ ;; recommended 1997-2019, warned since 2024, will change
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%3B" ref-time1) JAN)))
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%10B" ref-time1) p10-JANUARY)))
+ ;; implemented since 2001, recommended since 2019
(should (equal (time-stamp-string "%#b" ref-time1) JAN))
+ (should (equal (time-stamp-string "%#3b" ref-time1) JAN))
+ (should (equal (time-stamp-string "%#4b" ref-time1) p4-JAN))
+ ;; implemented since 2001, recommended 2019-2024
(should (equal (time-stamp-string "%:B" ref-time1) January))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; broken 2019-2024
+ (should (equal (time-stamp-string "%:10B" ref-time1) p10-January))
+ ;; broken in 2019, changed in 2024
+ (should (equal (time-stamp-string "%-B" ref-time1) January))
+ (should (equal (time-stamp-string "%_B" ref-time1) January))
+ ;; allowed but not recommended since 2019 (warned 1997-2019)
(should (equal (time-stamp-string "%^B" ref-time1) JANUARY))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%b" ref-time1) Jan))
+ (should (equal (time-stamp-string "%4b" ref-time1) p4-Jan))
+ (should (equal (time-stamp-string "%04b" ref-time1) p4-Jan))
+ (should (equal (time-stamp-string "%B" ref-time1) January))
+ ;; warned 1997-2019, changed in 2019
(should (equal (time-stamp-string "%^b" ref-time1) JAN))
- (should (equal (time-stamp-string "%B" ref-time1) January)))))
+ (should (equal (time-stamp-string "%^4b" ref-time1) p4-JAN)))))
(ert-deftest time-stamp-format-day-of-month ()
"Test time-stamp formats for day of month."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2d" ref-time1) " 2"))
(should (equal (time-stamp-string "%2d" ref-time2) "18"))
(should (equal (time-stamp-string "%02d" ref-time1) "02"))
(should (equal (time-stamp-string "%02d" ref-time2) "18"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:d" ref-time1) "2"))
(should (equal (time-stamp-string "%:d" ref-time2) "18"))
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended 2019-2024
(should (equal (time-stamp-string "%1d" ref-time1) "2"))
(should (equal (time-stamp-string "%1d" ref-time2) "18"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-d" ref-time1) "2"))
(should (equal (time-stamp-string "%-d" ref-time2) "18"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_d" ref-time1) " 2"))
(should (equal (time-stamp-string "%_d" ref-time2) "18"))
(should (equal (time-stamp-string "%d" ref-time1) "02"))
(ert-deftest time-stamp-format-hours-24 ()
"Test time-stamp formats for hour on a 24-hour clock."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2H" ref-time1) "15"))
(should (equal (time-stamp-string "%2H" ref-time2) "12"))
(should (equal (time-stamp-string "%2H" ref-time3) " 6"))
(should (equal (time-stamp-string "%02H" ref-time1) "15"))
(should (equal (time-stamp-string "%02H" ref-time2) "12"))
(should (equal (time-stamp-string "%02H" ref-time3) "06"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:H" ref-time1) "15"))
(should (equal (time-stamp-string "%:H" ref-time2) "12"))
(should (equal (time-stamp-string "%:H" ref-time3) "6"))
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended 2019-2024
(should (equal (time-stamp-string "%1H" ref-time1) "15"))
(should (equal (time-stamp-string "%1H" ref-time2) "12"))
(should (equal (time-stamp-string "%1H" ref-time3) "6"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-H" ref-time1) "15"))
(should (equal (time-stamp-string "%-H" ref-time2) "12"))
(should (equal (time-stamp-string "%-H" ref-time3) "6"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_H" ref-time1) "15"))
(should (equal (time-stamp-string "%_H" ref-time2) "12"))
(should (equal (time-stamp-string "%_H" ref-time3) " 6"))
(ert-deftest time-stamp-format-hours-12 ()
"Test time-stamp formats for hour on a 12-hour clock."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2I" ref-time1) " 3"))
(should (equal (time-stamp-string "%2I" ref-time2) "12"))
(should (equal (time-stamp-string "%2I" ref-time3) " 6"))
(should (equal (time-stamp-string "%02I" ref-time1) "03"))
(should (equal (time-stamp-string "%02I" ref-time2) "12"))
(should (equal (time-stamp-string "%02I" ref-time3) "06"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:I" ref-time1) "3")) ;PM
(should (equal (time-stamp-string "%:I" ref-time2) "12")) ;PM
(should (equal (time-stamp-string "%:I" ref-time3) "6")) ;AM
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended since 2019
(should (equal (time-stamp-string "%1I" ref-time1) "3"))
(should (equal (time-stamp-string "%1I" ref-time2) "12"))
(should (equal (time-stamp-string "%1I" ref-time3) "6"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-I" ref-time1) "3"))
(should (equal (time-stamp-string "%-I" ref-time2) "12"))
(should (equal (time-stamp-string "%-I" ref-time3) "6"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_I" ref-time1) " 3"))
(should (equal (time-stamp-string "%_I" ref-time2) "12"))
(should (equal (time-stamp-string "%_I" ref-time3) " 6"))
(ert-deftest time-stamp-format-month-number ()
"Test time-stamp formats for month number."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2m" ref-time1) " 1"))
(should (equal (time-stamp-string "%2m" ref-time2) "11"))
(should (equal (time-stamp-string "%02m" ref-time1) "01"))
(should (equal (time-stamp-string "%02m" ref-time2) "11"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:m" ref-time1) "1"))
(should (equal (time-stamp-string "%:m" ref-time2) "11"))
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended since 2019
(should (equal (time-stamp-string "%1m" ref-time1) "1"))
(should (equal (time-stamp-string "%1m" ref-time2) "11"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-m" ref-time1) "1"))
(should (equal (time-stamp-string "%-m" ref-time2) "11"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_m" ref-time1) " 1"))
(should (equal (time-stamp-string "%_m" ref-time2) "11"))
(should (equal (time-stamp-string "%m" ref-time1) "01"))
(ert-deftest time-stamp-format-minute ()
"Test time-stamp formats for minute."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2M" ref-time1) " 4"))
(should (equal (time-stamp-string "%2M" ref-time2) "14"))
(should (equal (time-stamp-string "%02M" ref-time1) "04"))
(should (equal (time-stamp-string "%02M" ref-time2) "14"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:M" ref-time1) "4"))
(should (equal (time-stamp-string "%:M" ref-time2) "14"))
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended since 2019
(should (equal (time-stamp-string "%1M" ref-time1) "4"))
(should (equal (time-stamp-string "%1M" ref-time2) "14"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-M" ref-time1) "4"))
(should (equal (time-stamp-string "%-M" ref-time2) "14"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_M" ref-time1) " 4"))
(should (equal (time-stamp-string "%_M" ref-time2) "14"))
(should (equal (time-stamp-string "%M" ref-time1) "04"))
(ert-deftest time-stamp-format-second ()
"Test time-stamp formats for second."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended until 2024
(should (equal (time-stamp-string "%2S" ref-time1) " 5"))
(should (equal (time-stamp-string "%2S" ref-time2) "15"))
(should (equal (time-stamp-string "%02S" ref-time1) "05"))
(should (equal (time-stamp-string "%02S" ref-time2) "15"))
- ;; documented 1997-2019
+ ;; recommended 1997-2019
(should (equal (time-stamp-string "%:S" ref-time1) "5"))
(should (equal (time-stamp-string "%:S" ref-time2) "15"))
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended since 2019
(should (equal (time-stamp-string "%1S" ref-time1) "5"))
(should (equal (time-stamp-string "%1S" ref-time2) "15"))
- ;; allowed but undocumented since 2019 (warned 1997-2019)
+ ;; warned 1997-2019, allowed 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%-S" ref-time1) "5"))
(should (equal (time-stamp-string "%-S" ref-time2) "15"))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%_S" ref-time1) " 5"))
(should (equal (time-stamp-string "%_S" ref-time2) "15"))
(should (equal (time-stamp-string "%S" ref-time1) "05"))
(ert-deftest time-stamp-format-year-2digit ()
"Test time-stamp formats for %y."
(with-time-stamp-test-env
- ;; implemented and documented since 1995
+ ;; implemented since 1995, recommended 1995-2024
(should (equal (time-stamp-string "%02y" ref-time1) "06"))
(should (equal (time-stamp-string "%02y" ref-time2) "16"))
- ;; documented 1997-2019
- (should (equal (time-stamp-string "%:y" ref-time1) "2006"))
- (should (equal (time-stamp-string "%:y" ref-time2) "2016"))
+ ;; recommended 1997-2019, warned since 2024
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%:y" ref-time1) "2006")))
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%:y" ref-time2) "2016")))
;; warned 1997-2019, changed in 2019
;; (We don't expect the %-y or %_y form to be useful,
;; but we test both so that we can confidently state that
(should (equal (time-stamp-string "%-y" ref-time2) "16"))
(should (equal (time-stamp-string "%_y" ref-time1) " 6"))
(should (equal (time-stamp-string "%_y" ref-time2) "16"))
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%y" ref-time1) "06"))
(should (equal (time-stamp-string "%y" ref-time2) "16"))
;; implemented since 1995, warned since 2019, will change
(ert-deftest time-stamp-format-year-4digit ()
"Test time-stamp format %Y."
(with-time-stamp-test-env
- ;; implemented since 1997, documented since 2019
+ ;; implemented since 1997, recommended since 2019
(should (equal (time-stamp-string "%Y" ref-time1) "2006"))
;; numbers do not truncate
(should (equal (time-stamp-string "%2Y" ref-time1) "2006"))
(Am (format-time-string "%p" ref-time3 t))
(PM (format-time-string "%^p" ref-time1 t))
(AM (format-time-string "%^p" ref-time3 t)))
- ;; implemented and documented since 1997
+ ;; implemented and recommended since 1997
(should (equal (time-stamp-string "%#p" ref-time1) pm))
(should (equal (time-stamp-string "%#p" ref-time3) am))
+ ;; implemented since 1997, recommended 1997-2024
(should (equal (time-stamp-string "%P" ref-time1) Pm))
(should (equal (time-stamp-string "%P" ref-time3) Am))
;; implemented since 1997
(should (equal (time-stamp-string "%^#p" ref-time1) pm))
(should (equal (time-stamp-string "%^#p" ref-time3) am))
- ;; warned 1997-2019, changed in 2019
+ ;; warned 1997-2019, changed in 2019, recommended (with caveat) since 2024
(should (equal (time-stamp-string "%p" ref-time1) Pm))
(should (equal (time-stamp-string "%p" ref-time3) Am))
;; changed in 2024
(with-time-stamp-test-env
(let ((UTC-abbr (format-time-string "%Z" ref-time1 t))
(utc-abbr (format-time-string "%#Z" ref-time1 t)))
- ;; implemented and documented since 1995
+ ;; implemented and recommended since 1995
(should (equal (time-stamp-string "%Z" ref-time1) UTC-abbr))
- ;; implemented since 1997, documented since 2019
- (should (equal (time-stamp-string "%#Z" ref-time1) utc-abbr)))))
+ ;; implemented since 1997, recommended since 2019
+ (should (equal (time-stamp-string "%#Z" ref-time1) utc-abbr))
+ ;; ^ accepted and ignored since 1995/1997, test for consistency with %p
+ (should (equal (time-stamp-string "%^Z" ref-time1) UTC-abbr))
+ (should (equal (time-stamp-string "%^#Z" ref-time1) utc-abbr)))))
(ert-deftest time-stamp-format-time-zone-offset ()
"Test time-stamp legacy format %z and spot-test new offset format %5z."
(with-time-stamp-test-env
(let ((utc-abbr (format-time-string "%#Z" ref-time1 t)))
- ;; documented 1995-2019, warned since 2019, will change
+ ;; recommended 1995-2019, warned since 2019, will change
(time-stamp-should-warn
(equal (time-stamp-string "%z" ref-time1) utc-abbr)))
- ;; implemented and documented (with compat caveat) since 2019
+ ;; implemented and recommended (with compat caveat) since 2019
(should (equal (time-stamp-string "%5z" ref-time1) "+0000"))
(let ((time-stamp-time-zone "PST8"))
(should (equal (time-stamp-string "%5z" ref-time1) "-0800")))
(should (equal (time-stamp-string "%5z" ref-time1) "-1000")))
(let ((time-stamp-time-zone "CET-1"))
(should (equal (time-stamp-string "%5z" ref-time1) "+0100")))
- ;; implemented since 2019, verify that these don't warn
+ ;; implemented since 2019, recommended (with compat caveat) since 2024
;; See also the "formatz" tests below, which since 2021 test more
;; variants with more offsets.
(should (equal (time-stamp-string "%-z" ref-time1) "+00"))
+ (should (equal (time-stamp-string "%:::z" ref-time1) "+00"))
(should (equal (time-stamp-string "%:z" ref-time1) "+00:00"))
+ ;; implemented since 2019
(should (equal (time-stamp-string "%::z" ref-time1) "+00:00:00"))
- (should (equal (time-stamp-string "%9::z" ref-time1) "+00:00:00"))
- (should (equal (time-stamp-string "%:::z" ref-time1) "+00"))))
+ (should (equal (time-stamp-string "%9::z" ref-time1) "+00:00:00"))))
(ert-deftest time-stamp-format-non-date-conversions ()
"Test time-stamp formats for non-date items."
(with-time-stamp-test-env
(with-time-stamp-system-name "test-system-name.example.org"
- ;; implemented and documented since 1995
+ ;; implemented and recommended since 1995
(should (equal (time-stamp-string "%%" ref-time1) "%")) ;% last char
(should (equal (time-stamp-string "%%P" ref-time1) "%P")) ;% not last char
(should (equal (time-stamp-string "%f" ref-time1) "time-stamped-file"))
(let ((mail-host-address nil))
(should (equal (time-stamp-string "%h" ref-time1)
"test-system-name.example.org")))
- ;; documented 1995-2019
- (should (equal (time-stamp-string "%s" ref-time1)
- "test-system-name.example.org"))
- (should (equal (time-stamp-string "%U" ref-time1) "100%d Tester"))
- (should (equal (time-stamp-string "%u" ref-time1) "test-logname"))
- ;; implemented since 2001, documented since 2019
+ ;; recommended 1997-2019, warned since 2024
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%s" ref-time1)
+ "test-system-name.example.org")))
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%U" ref-time1) "100%d Tester")))
+ (time-stamp-should-warn
+ (should (equal (time-stamp-string "%u" ref-time1) "test-logname")))
+ ;; implemented since 2001, recommended since 2019
(should (equal (time-stamp-string "%L" ref-time1) "100%d Tester"))
(should (equal (time-stamp-string "%l" ref-time1) "test-logname"))
- ;; implemented since 2007, documented since 2019
+ ;; implemented since 2007, recommended since 2019
(should (equal (time-stamp-string "%Q" ref-time1)
"test-system-name.example.org"))
(should (equal (time-stamp-string "%q" ref-time1) "test-system-name")))
(ert-deftest time-stamp-format-string-width ()
"Test time-stamp string width modifiers."
(with-time-stamp-test-env
- (let ((May (format-time-string "%b" ref-time3 t))
- (SUN (format-time-string "%^a" ref-time3 t))
- (NOV (format-time-string "%^b" ref-time2 t)))
- ;; strings truncate on the right or are blank-padded on the left
- (should (equal (time-stamp-string "%0b" ref-time3) ""))
- (should (equal (time-stamp-string "%1b" ref-time3) (substring May 0 1)))
- (should (equal (time-stamp-string "%2b" ref-time3) (substring May 0 2)))
- (should (equal (time-stamp-string "%3b" ref-time3) (substring May 0 3)))
- (should (equal (time-stamp-string "%4b" ref-time3) (concat " " May)))
- (should (equal (time-stamp-string "%0%" ref-time3) ""))
- (should (equal (time-stamp-string "%1%" ref-time3) "%"))
- (should (equal (time-stamp-string "%2%" ref-time3) " %"))
- (should (equal (time-stamp-string "%9%" ref-time3) " %"))
- (should (equal (time-stamp-string "%10%" ref-time3) " %"))
- (should (equal (time-stamp-string "%#3a" ref-time3)
- (substring SUN 0 3)))
- (should (equal (time-stamp-string "%#3b" ref-time2)
- (substring NOV 0 3))))))
+ (let ((UTC-abbr (format-time-string "%Z" ref-time1 t)))
+ (should (equal (time-stamp-string "%1%" ref-time3) "%"))
+ (should (equal (time-stamp-string "%2%" ref-time3) " %"))
+ (should (equal (time-stamp-string "%9%" ref-time3) " %"))
+ (should (equal (time-stamp-string "%10%" ref-time3) " %"))
+ (should (equal (time-stamp-string "%03d" ref-time3) "025"))
+ (should (equal (time-stamp-string "%3d" ref-time3) " 25"))
+ (should (equal (time-stamp-string "%_3d" ref-time3) " 25"))
+ ;; since 2024
+ (should (equal (time-stamp-string "%0d" ref-time1) "02"))
+ (should (equal (time-stamp-string "%0d" ref-time2) "18"))
+ ;; broken 2019-2024
+ (should (equal (time-stamp-string "%-Z" ref-time1) UTC-abbr))
+ (should (equal (time-stamp-string "%_Z" ref-time1) UTC-abbr)))))
;;; Tests of helper functions
(defun formatz-mod-pad-r10 (string)
"Return STRING padded on the right to 10 characters."
- (concat string (make-string (- 10 (length string)) ?\s)))
+ (string-pad string 10))
(defun formatz-mod-pad-r12 (string)
"Return STRING padded on the right to 12 characters."
- (concat string (make-string (- 12 (length string)) ?\s)))
+ (string-pad string 12))
;; Convenience macro for generating groups of test cases.
;;;; The actual test cases for %z
-;;; %z formats without colons.
+;;; Test %z formats without colons.
;; Option character "-" (minus) minimizes; it removes "00" minutes.
(formatz-generate-tests ("%-z" "%-3z")
("+100:00")
("+100:00:30"))
-;; Tests that minus with padding pads with spaces.
+;; Minus with padding pads with spaces.
(formatz-generate-tests ("%-12z")
("+00 " formatz-mod-pad-r12)
("+0030 " formatz-mod-del-colons formatz-mod-pad-r12)
("+100:00 " formatz-mod-pad-r12)
("+100:00:30 " formatz-mod-pad-r12))
-;; Tests that 0 after other digits becomes padding of ten, not zero flag.
+;; 0 after other digits becomes padding of ten, not zero flag.
(formatz-generate-tests ("%-10z")
("+00 " formatz-mod-pad-r10)
("+0030 " formatz-mod-del-colons formatz-mod-pad-r10)
("+100:00")
("+100:00:30"))
-;; Tests that padding adds spaces.
+;; Padding adds spaces.
(formatz-generate-tests ("%12z")
("+0000 " formatz-mod-add-00 formatz-mod-pad-r12)
("+0030 " formatz-mod-del-colons formatz-mod-pad-r12)
("+100:00:00 " formatz-mod-add-colon00 formatz-mod-pad-r12)
("+100:00:30 " formatz-mod-pad-r12))
-;;; %z formats with colons
+;;; Test %z formats with colons.
;; Three colons can output hours only,
;; like %-z, but uses colons with non-zero minutes and seconds.
("+100:00")
("+100:00:30"))
-;; Padding with three colons adds spaces
+;; Padding with three colons adds spaces.
(formatz-generate-tests ("%12:::z")
("+00 " formatz-mod-pad-r12)
("+00:30 " formatz-mod-pad-r12)
("+00:00:30 " formatz-mod-pad-r12)
("+100:00 " formatz-mod-pad-r12)
("+100:00:30 " formatz-mod-pad-r12))
-;; Tests that 0 after other digits becomes padding of ten, not zero flag.
+
+;; 0 after other digits becomes padding of ten, not zero flag.
(formatz-generate-tests ("%10:::z")
("+00 " formatz-mod-pad-r10)
("+00:30 " formatz-mod-pad-r10)
("+100:00")
("+100:00:30"))
-;; Padding with one colon adds spaces
+;; Padding with one colon adds spaces.
(formatz-generate-tests ("%12:z")
("+00:00 " formatz-mod-add-colon00 formatz-mod-pad-r12)
("+00:30 " formatz-mod-pad-r12)
("+100:00:00 " formatz-mod-add-colon00 formatz-mod-pad-r12)
("+100:00:30 " formatz-mod-pad-r12))
-;;; Illegal %z formats
+;;; Test illegal %z formats.
(ert-deftest formatz-illegal-options ()
"Test that illegal/nonsensical/ambiguous %z formats don't produce output."