time-stamp.el 24.7 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1 2
;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1989, 1993-1995, 1997, 2000-2019 Free Software
4
;; Foundation, Inc.
Erik Naggum's avatar
Erik Naggum committed
5

6 7
;; This file is part of GNU Emacs.

Paul Eggert's avatar
Paul Eggert committed
8
;; Maintainer: Stephen Gildea <stepheng+emacs@gildea.com>
Richard M. Stallman's avatar
Richard M. Stallman committed
9 10
;; Keywords: tools

11
;; GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
12
;; it under the terms of the GNU General Public License as published by
13 14
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
15

Eli Zaretskii's avatar
Eli Zaretskii committed
16
;; GNU Emacs is distributed in the hope that it will be useful,
Richard M. Stallman's avatar
Richard M. Stallman committed
17 18 19 20 21
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
22
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
Richard M. Stallman's avatar
Richard M. Stallman committed
23 24 25

;;; Commentary:

26 27
;; A template in a file can be updated with a new time stamp when
;; you save the file.  For example:
28
;;     static char *ts = "sdmain.c Time-stamp: <2001-08-13 10:20:51 gildea>";
29

30
;; To use time-stamping, add this line to your init file:
31
;;     (add-hook 'before-save-hook 'time-stamp)
32 33 34 35
;; Now any time-stamp templates in your files will be updated automatically.

;; See the documentation for the functions `time-stamp'
;; and `time-stamp-toggle-active' for details.
Erik Naggum's avatar
Erik Naggum committed
36

Richard M. Stallman's avatar
Richard M. Stallman committed
37 38
;;; Code:

39 40 41 42 43
(defgroup time-stamp nil
  "Maintain last change time stamps in files edited by Emacs."
  :group 'data
  :group 'extensions)

44
(defcustom time-stamp-format "%:y-%02m-%02d %02H:%02M:%02S %u"
Lute Kamstra's avatar
Lute Kamstra committed
45
  "Format of the string inserted by \\[time-stamp].
46
This is a string, used verbatim except for character sequences beginning
Dave Love's avatar
Dave Love committed
47
with %, as follows.  The values of non-numeric formatted items depend
48 49
on the locale setting recorded in `system-time-locale' and
`locale-coding-system'.  The examples here are for the default
50
\(`C') locale.
51 52 53 54 55 56 57 58 59 60 61 62 63

%:a  weekday name: `Monday'.		%#A gives uppercase: `MONDAY'
%3a  abbreviated weekday: `Mon'.	%3A gives uppercase: `MON'
%:b  month name: `January'.		%#B gives uppercase: `JANUARY'
%3b  abbreviated month: `Jan'.		%3B 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
%w   day number of week, Sunday is 0
64
%02y 2-digit year: `03'			%:y 4-digit year: `2003'
65 66 67 68 69 70
%z   time zone name: `est'.		%Z  gives uppercase: `EST'

Non-date items:
%%   a literal percent character: `%'
%f   file name without directory	%F  gives absolute pathname
%s   system name
71
%u   user's login name			%U  user's full name
72 73 74 75
%h   mail host name

Decimal digits between the % and the type character specify the
field width.  Strings are truncated on the right; years on the left.
76
A leading zero in the field width zero-fills a number.
77 78

For example, to get the format used by the `date' command,
79 80
use \"%3a %3b %2d %02H:%02M:%02S %Z %:y\".

81
In the future these formats will be aligned more with `format-time-string'.
82 83 84 85
Because of this transition, the default padding for numeric formats will
change in a future version.  Therefore either a padding width should be
specified, or the : modifier should be used to explicitly request the
historical default."
86
  :type 'string
87 88
  :group 'time-stamp
  :version "20.1")
89
;;;###autoload(put 'time-stamp-format 'safe-local-variable 'stringp)
90

91
(defcustom time-stamp-active t
Lute Kamstra's avatar
Lute Kamstra committed
92
  "Non-nil to enable time-stamping of buffers by \\[time-stamp].
93 94 95 96 97 98
Can be toggled by \\[time-stamp-toggle-active].
See also the variable `time-stamp-warn-inactive'."
  :type 'boolean
  :group 'time-stamp)

(defcustom time-stamp-warn-inactive t
99
  "Have \\[time-stamp] warn if a buffer did not get time-stamped.
100 101 102
If non-nil, a warning is displayed if `time-stamp-active' has
deactivated time stamping and the buffer contains a template that
otherwise would have been updated."
103
  :type 'boolean
104 105
  :group 'time-stamp
  :version "19.29")
106 107

(defcustom time-stamp-time-zone nil
108
  "The time zone to be used by \\[time-stamp].
109
Its format is that of the ZONE argument of the `format-time-string' function."
110 111 112 113
  :type '(choice (const :tag "Emacs local time" nil)
                 (const :tag "Universal Time" t)
                 (const :tag "system wall clock time" wall)
                 (string :tag "TZ environment variable value"))
114 115
  :group 'time-stamp
  :version "20.1")
116
;;;###autoload(put 'time-stamp-time-zone 'safe-local-variable 'string-or-null-p)
117

118
;;; Do not change time-stamp-line-limit, time-stamp-start,
119 120 121 122
;;; time-stamp-end, time-stamp-pattern, time-stamp-inserts-lines,
;;; or time-stamp-count in your .emacs or you will be incompatible
;;; with other people's files!  If you must change them, do so only
;;; in the local variables section of the file itself.
Richard M. Stallman's avatar
Richard M. Stallman committed
123

124

125
(defvar time-stamp-line-limit 8	    ;Do not change!
126
  "Lines of a file searched; positive counts from start, negative from end.
127 128
The patterns `time-stamp-start' and `time-stamp-end' must be found in
the first (last) `time-stamp-line-limit' lines of the file for the
129 130
file to be time-stamped by \\[time-stamp].  A value of 0 searches the
entire buffer (use with care).
131

132 133 134 135 136 137 138
This value can also be set with the variable `time-stamp-pattern'.

Do not change `time-stamp-line-limit', `time-stamp-start',
`time-stamp-end', or `time-stamp-pattern' for yourself or you will be
incompatible with other people's files!  If you must change them for some
application, do so in the local variables section of the time-stamped file
itself.")
139
;;;###autoload(put 'time-stamp-line-limit 'safe-local-variable 'integerp)
Richard M. Stallman's avatar
Richard M. Stallman committed
140

141
(defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+"    ;Do not change!
Richard M. Stallman's avatar
Richard M. Stallman committed
142
  "Regexp after which the time stamp is written by \\[time-stamp].
143
See also the variables `time-stamp-end' and `time-stamp-line-limit'.
Richard M. Stallman's avatar
Richard M. Stallman committed
144

145 146 147 148 149 150 151
This value can also be set with the variable `time-stamp-pattern'.

Do not change `time-stamp-line-limit', `time-stamp-start',
`time-stamp-end', or `time-stamp-pattern' for yourself or you will be
incompatible with other people's files!  If you must change them for some
application, do so in the local variables section of the time-stamped file
itself.")
152
;;;###autoload(put 'time-stamp-start 'safe-local-variable 'stringp)
Richard M. Stallman's avatar
Richard M. Stallman committed
153

154
(defvar time-stamp-end "\\\\?[\">]"    ;Do not change!
Richard M. Stallman's avatar
Richard M. Stallman committed
155
  "Regexp marking the text after the time stamp.
156
\\[time-stamp] deletes the text between the first match of `time-stamp-start'
157 158
and the following match of `time-stamp-end', then writes the
time stamp specified by `time-stamp-format' between them.
159

160 161
This value can also be set with the variable `time-stamp-pattern'.

162 163 164 165 166 167
The end text normally starts on the same line as the start text ends,
but if there are any newlines in `time-stamp-format', the same number
of newlines must separate the start and end.  \\[time-stamp] tries
to not change the number of lines in the buffer.  `time-stamp-inserts-lines'
controls this behavior.

168
Do not change `time-stamp-start', `time-stamp-end', `time-stamp-pattern',
169
or `time-stamp-inserts-lines' for yourself or you will be incompatible
170 171
with other people's files!  If you must change them for some application,
do so in the local variables section of the time-stamped file itself.")
172
;;;###autoload(put 'time-stamp-end 'safe-local-variable 'stringp)
173

Richard M. Stallman's avatar
Richard M. Stallman committed
174

175
(defvar time-stamp-inserts-lines nil    ;Do not change!
176
  "Whether \\[time-stamp] can change the number of lines in a file.
177 178 179 180 181 182 183 184 185 186 187 188
If nil, \\[time-stamp] skips as many lines as there are newlines in
`time-stamp-format' before looking for the `time-stamp-end' pattern,
thus it tries not to change the number of lines in the buffer.
If non-nil, \\[time-stamp] starts looking for the end pattern
immediately after the start pattern.  This behavior can cause
unexpected changes in the buffer if used carelessly, but it is useful
for generating repeated time stamps.

Do not change `time-stamp-end' or `time-stamp-inserts-lines' for
yourself or you will be incompatible with other people's files!
If you must change them for some application, do so in the local
variables section of the time-stamped file itself.")
189
;;;###autoload(put 'time-stamp-inserts-lines 'safe-local-variable 'symbolp)
190 191 192


(defvar time-stamp-count 1		;Do not change!
193
  "How many templates \\[time-stamp] will look for in a buffer.
194
The same time stamp will be written in each case.
195 196 197 198 199

Do not change `time-stamp-count' for yourself or you will be
incompatible with other people's files!  If you must change it for
some application, do so in the local variables section of the
time-stamped file itself.")
200
;;;###autoload(put 'time-stamp-count 'safe-local-variable 'integerp)
201 202


203
(defvar time-stamp-pattern nil		;Do not change!
204
  "Convenience variable setting all `time-stamp' location and format values.
205
This string has four parts, each of which is optional.
206 207
These four parts set `time-stamp-line-limit', `time-stamp-start',
`time-stamp-format', and `time-stamp-end'.  See the documentation
208 209 210 211
for each of these variables for details.

The first part is a number followed by a slash; the number sets the number
of lines at the beginning (negative counts from end) of the file searched
212
for the time stamp.  The number and the slash may be omitted to use the
213 214 215 216 217
normal value.

The second part is a regexp identifying the pattern preceding the time stamp.
This part may be omitted to use the normal pattern.

218
The third part specifies the format of the time stamp inserted.  See
219
the documentation for `time-stamp-format' for details.  Specify this
220 221 222 223 224
part as \"%%\" to use the normal format.

The fourth part is a regexp identifying the pattern following the time stamp.
This part may be omitted to use the normal pattern.

225 226 227 228 229
Examples:
\"-10/\"
\"-9/^Last modified: %%$\"
\"@set Time-stamp: %:b %:d, %:y$\"
\"newcommand{\\\\\\\\timestamp}{%%}\"
230

231 232 233 234 235
Do not change `time-stamp-pattern' `time-stamp-line-limit',
`time-stamp-start', or `time-stamp-end' for yourself or you will be
incompatible with other people's files!  If you must change them for
some application, do so only in the local variables section of the
time-stamped file itself.")
236
;;;###autoload(put 'time-stamp-pattern 'safe-local-variable 'stringp)
237 238


239

240
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
241
(defun time-stamp ()
242
  "Update the time stamp string(s) in the buffer.
243
A template in a file can be automatically updated with a new time stamp
244
every time you save the file.  Add this line to your init file:
245
    (add-hook \\='before-save-hook \\='time-stamp)
Luc Teirlinck's avatar
Luc Teirlinck committed
246
or customize `before-save-hook' through Custom.
247 248 249 250 251
Normally the template must appear in the first 8 lines of a file and
look like one of the following:
      Time-stamp: <>
      Time-stamp: \" \"
The time stamp is written between the brackets or quotes:
252
      Time-stamp: <2001-02-18 10:20:51 gildea>
253
The time stamp is updated only if the variable `time-stamp-active' is non-nil.
254 255 256
The format of the time stamp is set by the variable `time-stamp-pattern' or
`time-stamp-format'.  The variables `time-stamp-pattern',
`time-stamp-line-limit', `time-stamp-start', `time-stamp-end',
Lute Kamstra's avatar
Lute Kamstra committed
257
`time-stamp-count', and `time-stamp-inserts-lines' control finding
258
the template."
Richard M. Stallman's avatar
Richard M. Stallman committed
259
  (interactive)
260
  (let ((line-limit time-stamp-line-limit)
261 262
	(ts-start time-stamp-start)
	(ts-format time-stamp-format)
263 264 265 266 267 268
	(ts-end time-stamp-end)
	(ts-count time-stamp-count)
	(format-lines 0)
	(end-lines 1)
	(start nil)
	search-limit)
269 270
    (if (stringp time-stamp-pattern)
	(progn
271
	  (string-match "\\`\\(\\(-?[0-9]+\\)/\\)?\\([^%]+\\)?\\(\\(%[-.,:@+_ #^()0-9]*[A-Za-z%][^%]*\\)*%[-.,:@+_ #^()0-9]*[A-Za-z%]\\)?\\([^%]+\\)?\\'" time-stamp-pattern)
272 273
	  (and (match-beginning 2)
	       (setq line-limit
274
		     (string-to-number (match-string 2 time-stamp-pattern))))
275 276 277 278 279
	  (and (match-beginning 3)
	       (setq ts-start (match-string 3 time-stamp-pattern)))
	  (and (match-beginning 4)
	       (not (string-equal (match-string 4 time-stamp-pattern) "%%"))
	       (setq ts-format (match-string 4 time-stamp-pattern)))
280 281
	  (and (match-beginning 6)
	       (setq ts-end (match-string 6 time-stamp-pattern)))))
282 283
    (cond ((not (integerp line-limit))
	   (setq line-limit 8)
284
	   (message "time-stamp-line-limit is not an integer")
285
	   (sit-for 1)))
286 287 288 289 290 291 292 293 294
    (cond ((not (integerp ts-count))
	   (setq ts-count 1)
	   (message "time-stamp-count is not an integer")
	   (sit-for 1))
	  ((< ts-count 1)
	   ;; We need to call time-stamp-once at least once
	   ;; to output any warnings about time-stamp not being active.
	   (setq ts-count 1)))
    ;; Figure out what lines the end should be on.
295 296 297 298
    (if (stringp ts-format)
	(let ((nl-start 0))
	  (while (string-match "\n" ts-format nl-start)
	    (setq format-lines (1+ format-lines) nl-start (match-end 0)))))
299 300 301 302
    (let ((nl-start 0))
      (while (string-match "\n" ts-end nl-start)
	(setq end-lines (1+ end-lines) nl-start (match-end 0))))
    ;; Find overall what lines to look at
303 304 305 306 307 308 309
    (save-excursion
      (save-restriction
	(widen)
	(cond ((> line-limit 0)
	       (goto-char (setq start (point-min)))
	       (forward-line line-limit)
	       (setq search-limit (point)))
310
	      ((< line-limit 0)
311 312
	       (goto-char (setq search-limit (point-max)))
	       (forward-line line-limit)
313 314 315
	       (setq start (point)))
	      (t			;0 => no limit (use with care!)
	       (setq start (point-min))
316 317 318 319 320 321 322 323 324 325 326
	       (setq search-limit (point-max))))))
    (while (and start
		(< start search-limit)
		(> ts-count 0))
      (setq start (time-stamp-once start search-limit ts-start ts-end
				   ts-format format-lines end-lines))
      (setq ts-count (1- ts-count))))
  nil)

(defun time-stamp-once (start search-limit ts-start ts-end
			ts-format format-lines end-lines)
327
  "Update one time stamp.  Internal routine called by \\[time-stamp].
328 329 330 331 332 333 334 335 336 337
Returns the end point, which is where `time-stamp' begins the next search."
  (let ((case-fold-search nil)
	(end nil)
	end-search-start
	(end-length nil))
    (save-excursion
      (save-restriction
	(widen)
	;; Find the location of the time stamp.
	(while (and (< (goto-char start) search-limit)
338
		    (not end)
339
		    (re-search-forward ts-start search-limit 'move))
340
	  (setq start (point))
341 342 343 344 345 346 347 348 349 350 351 352 353 354
	  (if (not time-stamp-inserts-lines)
	      (forward-line format-lines))
	  (setq end-search-start (max start (point)))
	  (if (= (forward-line end-lines) 0)
	      (progn
	       (and (bolp) (backward-char))
	       (let ((line-end (min (point) search-limit)))
		 (if (>= line-end end-search-start)
		     (progn
		      (goto-char end-search-start)
		      (if (re-search-forward ts-end line-end t)
			  (progn
			    (setq end (match-beginning 0))
			    (setq end-length (- (match-end 0) end))))))))))))
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    (if end
	(progn
	  ;; do all warnings outside save-excursion
	  (cond
	   ((not time-stamp-active)
	    (if time-stamp-warn-inactive
		;; don't signal an error in a write-file-hook
		(progn
		  (message "Warning: time-stamp-active is off; did not time-stamp buffer.")
		  (sit-for 1))))
	   ((not (and (stringp ts-start)
		      (stringp ts-end)))
	    (message "time-stamp-start or time-stamp-end is not a string")
	    (sit-for 1))
	   (t
	    (let ((new-time-stamp (time-stamp-string ts-format)))
371 372 373
	      (if (and (stringp new-time-stamp)
		       (not (string-equal (buffer-substring start end)
					  new-time-stamp)))
374 375 376 377 378 379 380 381
		  (save-excursion
		    (save-restriction
		      (widen)
		      (delete-region start end)
		      (goto-char start)
		      (insert-and-inherit new-time-stamp)
		      (setq end (point))
		      ;; remove any tabs used to format time stamp
382 383 384 385 386 387 388 389
		      (if (search-backward "\t" start t)
			  (progn
			    (untabify start end)
			    (setq end (point))))))))))))
    ;; return the location after this time stamp, if there was one
    (and end end-length
	 (+ end end-length))))

Richard M. Stallman's avatar
Richard M. Stallman committed
390

391 392
;;;###autoload
(defun time-stamp-toggle-active (&optional arg)
393
  "Toggle `time-stamp-active', setting whether \\[time-stamp] updates a buffer.
394
With ARG, turn time stamping on if and only if arg is positive."
395 396 397 398 399
  (interactive "P")
  (setq time-stamp-active
	(if (null arg)
	    (not time-stamp-active)
	  (> (prefix-numeric-value arg) 0)))
400 401
  (message "time-stamp is now %s." (if time-stamp-active "active" "off")))

402 403
(defun time-stamp--format (format time)
  (format-time-string format time time-stamp-time-zone))
404 405 406

(defun time-stamp-string (&optional ts-format)
  "Generate the new string to be inserted by \\[time-stamp].
Eli Zaretskii's avatar
Eli Zaretskii committed
407 408
Optionally use format TS-FORMAT instead of `time-stamp-format' to
format the string."
409 410 411
  (if (stringp (or ts-format (setq ts-format time-stamp-format)))
      (time-stamp--format (time-stamp-string-preprocess ts-format) nil)))

412

413 414 415
(defconst time-stamp-no-file "(no file)"
  "String to use when the buffer is not associated with a file.")

416
;;; FIXME This comment was written in 1996!
417 418 419 420 421 422 423 424 425 426 427 428 429
;;; time-stamp is transitioning to using the new, expanded capabilities
;;; of format-time-string.  During the process, this function implements
;;; intermediate, compatible formats and complains about old, soon to
;;; be unsupported, formats.  This function will get a lot (a LOT) shorter
;;; when the transition is complete and we can just pass most things
;;; straight through to format-time-string.
;;;      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.
;;;      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)
Dave Love's avatar
Dave Love committed
430 431 432
  "Use a FORMAT to format date, time, file, and user information.
Optional second argument TIME is only for testing.
Implements non-time extensions to `format-time-string'
433
and all `time-stamp-format' compatibility."
434 435 436 437 438 439 440
  (let ((fmt-len (length format))
	(ind 0)
	cur-char
	(prev-char nil)
	(result "")
	field-width
	field-result
441
	alt-form change-case
442 443 444 445 446 447 448 449 450
	(paren-level 0))
    (while (< ind fmt-len)
      (setq cur-char (aref format ind))
      (setq
       result
       (concat result
      (cond
       ((eq cur-char ?%)
	;; eat any additional args to allow for future expansion
451
	(setq alt-form nil change-case nil field-width "")
452 453 454 455 456 457 458
	(while (progn
		 (setq ind (1+ ind))
		 (setq cur-char (if (< ind fmt-len)
				    (aref format ind)
				  ?\0))
		 (or (eq ?. cur-char)
		     (eq ?, cur-char) (eq ?: cur-char) (eq ?@ cur-char)
Dave Love's avatar
Dave Love committed
459
		     (eq ?- cur-char) (eq ?+ cur-char) (eq ?_ cur-char)
460
		     (eq ?\s cur-char) (eq ?# cur-char) (eq ?^ cur-char)
461 462 463 464 465 466 467 468
		     (and (eq ?\( cur-char)
			  (not (eq prev-char ?\\))
			  (setq paren-level (1+ paren-level)))
		     (if (and (eq ?\) cur-char)
			      (not (eq prev-char ?\\))
			      (> paren-level 0))
			 (setq paren-level (1- paren-level))
		       (and (> paren-level 0)
469 470 471 472 473 474 475 476 477 478 479 480 481
			    (< ind fmt-len)))
		     (if (and (<= ?0 cur-char) (>= ?9 cur-char))
			 ;; get format width
			 (let ((field-index ind))
			   (while (progn
				    (setq ind (1+ ind))
				    (setq cur-char (if (< ind fmt-len)
						       (aref format ind)
						     ?\0))
				    (and (<= ?0 cur-char) (>= ?9 cur-char))))
			   (setq field-width (substring format field-index ind))
			   (setq ind (1- ind))
			   t))))
482 483 484 485 486 487 488 489 490
	  (setq prev-char cur-char)
	  ;; some characters we actually use
	  (cond ((eq cur-char ?:)
		 (setq alt-form t))
		((eq cur-char ?#)
		 (setq change-case t))))
	(setq field-result
	(cond
	 ((eq cur-char ?%)
491
	  "%%")
492 493
	 ((eq cur-char ?a)		;day of week
	  (if change-case
494
	      (time-stamp--format "%#a" time)
495 496 497 498
	    (or alt-form (not (string-equal field-width ""))
		(time-stamp-conv-warn "%a" "%:a"))
	    (if (and alt-form (not (string-equal field-width "")))
		""			;discourage "%:3a"
499
	      (time-stamp--format "%A" time))))
500 501
	 ((eq cur-char ?A)
	  (if alt-form
502
	      (time-stamp--format "%A" time)
503 504
	    (or change-case (not (string-equal field-width ""))
		(time-stamp-conv-warn "%A" "%#A"))
505
	    (time-stamp--format "%#A" time)))
506 507
	 ((eq cur-char ?b)		;month name
	  (if change-case
508
	      (time-stamp--format "%#b" time)
509 510 511 512
	    (or alt-form (not (string-equal field-width ""))
		(time-stamp-conv-warn "%b" "%:b"))
	    (if (and alt-form (not (string-equal field-width "")))
		""			;discourage "%:3b"
513
	    (time-stamp--format "%B" time))))
514 515
	 ((eq cur-char ?B)
	  (if alt-form
516
	      (time-stamp--format "%B" time)
517 518
	    (or change-case (not (string-equal field-width ""))
		(time-stamp-conv-warn "%B" "%#B"))
519
	    (time-stamp--format "%#B" time)))
520
	 ((eq cur-char ?d)		;day of month, 1-31
521
	  (time-stamp-do-number cur-char alt-form field-width time))
522
	 ((eq cur-char ?H)		;hour, 0-23
523
	  (time-stamp-do-number cur-char alt-form field-width time))
524
	 ((eq cur-char ?I)		;hour, 1-12
525
	  (time-stamp-do-number cur-char alt-form field-width time))
526
	 ((eq cur-char ?m)		;month number, 1-12
527
	  (time-stamp-do-number cur-char alt-form field-width time))
528
	 ((eq cur-char ?M)		;minute, 0-59
529
	  (time-stamp-do-number cur-char alt-form field-width time))
530 531 532
	 ((eq cur-char ?p)		;am or pm
	  (or change-case
	      (time-stamp-conv-warn "%p" "%#p"))
533
	  (time-stamp--format "%#p" time))
534
	 ((eq cur-char ?P)		;AM or PM
535
	  (time-stamp--format "%p" time))
536
	 ((eq cur-char ?S)		;seconds, 00-60
537
	  (time-stamp-do-number cur-char alt-form field-width time))
538
	 ((eq cur-char ?w)		;weekday number, Sunday is 0
539
	  (time-stamp--format "%w" time))
540 541 542
	 ((eq cur-char ?y)		;year
	  (or alt-form (not (string-equal field-width ""))
	      (time-stamp-conv-warn "%y" "%:y"))
543
	  (string-to-number (time-stamp--format "%Y" time)))
544
	 ((eq cur-char ?Y)		;4-digit year, new style
545
	  (string-to-number (time-stamp--format "%Y" time)))
546 547 548
	 ((eq cur-char ?z)		;time zone lower case
	  (if change-case
	      ""			;discourage %z variations
549
	    (time-stamp--format "%#Z" time)))
550 551
	 ((eq cur-char ?Z)
	  (if change-case
552 553
	      (time-stamp--format "%#Z" time)
	    (time-stamp--format "%Z" time)))
554 555 556 557 558 559 560 561 562 563 564
	 ((eq cur-char ?f)		;buffer-file-name, base name only
	  (if buffer-file-name
	      (file-name-nondirectory buffer-file-name)
	    time-stamp-no-file))
	 ((eq cur-char ?F)		;buffer-file-name, full path
	  (or buffer-file-name
	      time-stamp-no-file))
	 ((eq cur-char ?s)		;system name
	  (system-name))
	 ((eq cur-char ?u)		;user name
	  (user-login-name))
565 566
	 ((eq cur-char ?U)		;user full name
	  (user-full-name))
567 568 569 570
	 ((eq cur-char ?l)		;logname (undocumented user name alt)
	  (user-login-name))
	 ((eq cur-char ?L)		;(undocumented alt user full name)
	  (user-full-name))
571
	 ((eq cur-char ?h)		;mail host name
572
	  (or mail-host-address (system-name)))
573 574 575 576 577 578 579
	 ((eq cur-char ?q)		;(undocumented unqual hostname)
	  (let ((qualname (system-name)))
	    (if (string-match "\\." qualname)
		(substring qualname 0 (match-beginning 0))
	      qualname)))
	 ((eq cur-char ?Q)		;(undocumented fully-qualified host)
	  (system-name))
580
	 ))
581 582 583 584 585 586 587 588
	(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
589
				   (string-to-number field-width))))
590 591 592 593 594 595 596 597
	    (if (> initial-length desired-length)
		;; truncate strings on right, years on left
		(if (stringp field-result)
		    (substring padded-result 0 desired-length)
		  (if (eq cur-char ?y)
		      (substring padded-result (- desired-length))
		    padded-result))	;non-year numbers don't truncate
	      padded-result))))
598 599 600 601 602
       (t
	(char-to-string cur-char)))))
      (setq ind (1+ ind)))
    result))

603
(defun time-stamp-do-number (format-char alt-form field-width time)
Dave Love's avatar
Dave Love committed
604 605 606
  "Handle compatible FORMAT-CHAR where only default width/padding will change.
ALT-FORM is whether `#' specified.  FIELD-WIDTH is the string
width specification or \"\".  TIME is the time to convert."
607 608 609 610 611 612
  (let ((format-string (concat "%" (char-to-string format-char))))
    (and (not alt-form) (string-equal field-width "")
	 (time-stamp-conv-warn format-string
			       (format "%%:%c" format-char)))
    (if (and alt-form (not (string-equal field-width "")))
	""				;discourage "%:2d" and the like
613
      (string-to-number (time-stamp--format format-string time)))))
614 615

(defvar time-stamp-conversion-warn t
616 617
  "Warn about soon-to-be-unsupported forms in `time-stamp-format'.
If nil, these warnings are disabled, which would be a bad idea!
618 619 620
You really need to update your files instead.

The new formats will work with old versions of Emacs.
621 622
New formats are being recommended now to allow `time-stamp-format'
to change in the future to be compatible with `format-time-string'.
623 624 625 626
The new forms being recommended now will continue to work then.")


(defun time-stamp-conv-warn (old-form new-form)
627 628
  "Display a warning about a soon-to-be-obsolete format.
Suggests replacing OLD-FORM with NEW-FORM."
629 630
  (cond
   (time-stamp-conversion-warn
631
    (with-current-buffer (get-buffer-create "*Time-stamp-compatibility*")
632 633 634 635 636 637 638 639 640
      (goto-char (point-max))
      (if (bobp)
	  (progn
	    (insert
	     "The formats recognized in time-stamp-format will change in a future release\n"
	     "to be compatible with the new, expanded format-time-string function.\n\n"
	     "The following obsolescent time-stamp-format construct(s) were found:\n\n")))
      (insert "\"" old-form "\" -- use " new-form "\n"))
    (display-buffer "*Time-stamp-compatibility*"))))
641

Richard M. Stallman's avatar
Richard M. Stallman committed
642 643 644
(provide 'time-stamp)

;;; time-stamp.el ends here