compare symbol names with `equal'
[bpt/emacs.git] / lisp / calendar / time-date.el
CommitLineData
707f2b38 1;;; time-date.el --- Date and time handling functions
dbfca9c4 2
ba318903 3;; Copyright (C) 1998-2014 Free Software Foundation, Inc.
c113de23
GM
4
5;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6;; Masanobu Umeda <umerin@mse.kyutech.ac.jp>
7;; Keywords: mail news util
8
9;; This file is part of GNU Emacs.
10
2ed66575 11;; GNU Emacs is free software: you can redistribute it and/or modify
c113de23 12;; it under the terms of the GNU General Public License as published by
2ed66575
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
c113de23
GM
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
2ed66575 18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
c113de23
GM
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
2ed66575 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
c113de23
GM
23
24;;; Commentary:
25
d35af63c 26;; Time values come in several formats. The oldest format is a cons
ca2d9ad8 27;; cell of the form (HIGH . LOW). This format is obsolete, but still
d35af63c
PE
28;; supported. The other formats are the lists (HIGH LOW), (HIGH LOW
29;; USEC), and (HIGH LOW USEC PSEC). These formats specify the time
30;; value equal to HIGH * 2^16 + LOW + USEC * 10^-6 + PSEC * 10^-12
31;; seconds, where missing components are treated as zero. HIGH can be
32;; negative, either because the value is a time difference, or because
535efd4a
PE
33;; the machine supports negative time stamps that fall before the epoch.
34;; The macro `with-decoded-time-value' and the function
35;; `encode-time-value' make it easier to deal with these formats.
36;; See `time-subtract' for an example of how to use them.
ca2d9ad8 37
c113de23
GM
38;;; Code:
39
ca2d9ad8
LK
40(defmacro with-decoded-time-value (varlist &rest body)
41 "Decode a time value and bind it according to VARLIST, then eval BODY.
42
43The value of the last form in BODY is returned.
44
45Each element of the list VARLIST is a list of the form
d35af63c 46\(HIGH-SYMBOL LOW-SYMBOL MICRO-SYMBOL [PICO-SYMBOL [TYPE-SYMBOL]] TIME-VALUE).
ca2d9ad8
LK
47The time value TIME-VALUE is decoded and the result it bound to
48the symbols HIGH-SYMBOL, LOW-SYMBOL and MICRO-SYMBOL.
d35af63c 49The optional PICO-SYMBOL is bound to the picoseconds part.
ca2d9ad8
LK
50
51The optional TYPE-SYMBOL is bound to the type of the time value.
52Type 0 is the cons cell (HIGH . LOW), type 1 is the list (HIGH
d35af63c
PE
53LOW), type 2 is the list (HIGH LOW MICRO), and type 3 is the
54list (HIGH LOW MICRO PICO)."
ca2d9ad8
LK
55 (declare (indent 1)
56 (debug ((&rest (symbolp symbolp symbolp &or [symbolp form] form))
57 body)))
58 (if varlist
59 (let* ((elt (pop varlist))
60 (high (pop elt))
61 (low (pop elt))
62 (micro (pop elt))
d35af63c
PE
63 (pico (unless (<= (length elt) 2)
64 (pop elt)))
ca2d9ad8
LK
65 (type (unless (eq (length elt) 1)
66 (pop elt)))
67 (time-value (car elt))
68 (gensym (make-symbol "time")))
69 `(let* ,(append `((,gensym ,time-value)
70 (,high (pop ,gensym))
71 ,low ,micro)
d35af63c 72 (when pico `(,pico))
ca2d9ad8
LK
73 (when type `(,type)))
74 (if (consp ,gensym)
75 (progn
76 (setq ,low (pop ,gensym))
77 (if ,gensym
d35af63c
PE
78 (progn
79 (setq ,micro (car ,gensym))
80 ,(cond (pico
81 `(if (cdr ,gensym)
82 ,(append `(setq ,pico (cadr ,gensym))
83 (when type `(,type 3)))
84 ,(append `(setq ,pico 0)
85 (when type `(,type 2)))))
86 (type
87 `(setq type 2))))
ca2d9ad8 88 ,(append `(setq ,micro 0)
d35af63c 89 (when pico `(,pico 0))
ca2d9ad8
LK
90 (when type `(,type 1)))))
91 ,(append `(setq ,low ,gensym ,micro 0)
d35af63c 92 (when pico `(,pico 0))
ca2d9ad8
LK
93 (when type `(,type 0))))
94 (with-decoded-time-value ,varlist ,@body)))
95 `(progn ,@body)))
96
d35af63c
PE
97(defun encode-time-value (high low micro pico &optional type)
98 "Encode HIGH, LOW, MICRO, and PICO into a time value of type TYPE.
ca2d9ad8 99Type 0 is the cons cell (HIGH . LOW), type 1 is the list (HIGH LOW),
d35af63c
PE
100type 2 is (HIGH LOW MICRO), and type 3 is (HIGH LOW MICRO PICO).
101
102For backward compatibility, if only four arguments are given,
103it is assumed that PICO was omitted and should be treated as zero."
ca2d9ad8
LK
104 (cond
105 ((eq type 0) (cons high low))
106 ((eq type 1) (list high low))
d35af63c
PE
107 ((eq type 2) (list high low micro))
108 ((eq type 3) (list high low micro pico))
109 ((null type) (encode-time-value high low micro 0 pico))))
c113de23 110
bbcb5072 111(autoload 'parse-time-string "parse-time")
ce2a8a6a
DL
112(autoload 'timezone-make-date-arpa-standard "timezone")
113
c113de23 114;;;###autoload
b069e5a6
G
115;; `parse-time-string' isn't sufficiently general or robust. It fails
116;; to grok some of the formats that timezone does (e.g. dodgy
117;; post-2000 stuff from some Elms) and either fails or returns bogus
118;; values. timezone-make-date-arpa-standard should help.
c113de23 119(defun date-to-time (date)
244b023e
CY
120 "Parse a string DATE that represents a date-time and return a time value.
121If DATE lacks timezone information, GMT is assumed."
c113de23 122 (condition-case ()
b069e5a6
G
123 (apply 'encode-time (parse-time-string date))
124 (error (condition-case ()
125 (apply 'encode-time
126 (parse-time-string
127 (timezone-make-date-arpa-standard date)))
128 (error (error "Invalid date: %s" date))))))
c113de23 129
697c7714
GM
130;; Bit of a mess. Emacs has float-time since at least 21.1.
131;; This file is synced to Gnus, and XEmacs packages may have been written
132;; using time-to-seconds from the Gnus library.
6f0d4bb6
GM
133;;;###autoload(if (or (featurep 'emacs)
134;;;###autoload (and (fboundp 'float-time)
135;;;###autoload (subrp (symbol-function 'float-time))))
855b17af 136;;;###autoload (defalias 'time-to-seconds 'float-time)
cdce0b33
KY
137;;;###autoload (autoload 'time-to-seconds "time-date"))
138
cdce0b33 139(eval-when-compile
6f0d4bb6
GM
140 (or (featurep 'emacs)
141 (and (fboundp 'float-time)
142 (subrp (symbol-function 'float-time)))
143 (defun time-to-seconds (time)
144 "Convert time value TIME to a floating point number."
d35af63c 145 (with-decoded-time-value ((high low micro pico type time))
6f0d4bb6
GM
146 (+ (* 1.0 high 65536)
147 low
d35af63c 148 (/ (+ (* micro 1e6) pico) 1e12))))))
c113de23 149
74fcda73 150;;;###autoload
c113de23 151(defun seconds-to-time (seconds)
74fcda73 152 "Convert SECONDS (a floating point number) to a time value."
d35af63c
PE
153 (let* ((usec (* 1000000 (mod seconds 1)))
154 (ps (round (* 1000000 (mod usec 1))))
155 (us (floor usec))
156 (lo (floor (mod seconds 65536)))
157 (hi (floor seconds 65536)))
158 (if (eq ps 1000000)
159 (progn
160 (setq ps 0)
161 (setq us (1+ us))
162 (if (eq us 1000000)
163 (progn
164 (setq us 0)
165 (setq lo (1+ lo))
166 (if (eq lo 65536)
167 (progn
168 (setq lo 0)
169 (setq hi (1+ hi))))))))
170 (list hi lo us ps)))
c113de23 171
74fcda73 172;;;###autoload
c113de23 173(defun time-less-p (t1 t2)
6f0d4bb6 174 "Return non-nil if time value T1 is earlier than time value T2."
d35af63c
PE
175 (with-decoded-time-value ((high1 low1 micro1 pico1 type1 t1)
176 (high2 low2 micro2 pico2 type2 t2))
ca2d9ad8
LK
177 (or (< high1 high2)
178 (and (= high1 high2)
179 (or (< low1 low2)
180 (and (= low1 low2)
d35af63c
PE
181 (or (< micro1 micro2)
182 (and (= micro1 micro2)
183 (< pico1 pico2)))))))))
c113de23 184
74fcda73 185;;;###autoload
c113de23 186(defun days-to-time (days)
74fcda73 187 "Convert DAYS into a time value."
c113de23 188 (let* ((seconds (* 1.0 days 60 60 24))
91472578 189 (high (condition-case nil (floor (/ seconds 65536))
ca2d9ad8 190 (range-error most-positive-fixnum))))
91472578
MB
191 (list high (condition-case nil (floor (- seconds (* 1.0 high 65536)))
192 (range-error 65535)))))
c113de23 193
74fcda73 194;;;###autoload
c113de23 195(defun time-since (time)
74fcda73
RS
196 "Return the time elapsed since TIME.
197TIME should be either a time value or a date-time string."
c113de23
GM
198 (when (stringp time)
199 ;; Convert date strings to internal time.
200 (setq time (date-to-time time)))
ca2d9ad8 201 (time-subtract (current-time) time))
c113de23 202
74fcda73
RS
203;;;###autoload
204(defalias 'subtract-time 'time-subtract)
205
206;;;###autoload
207(defun time-subtract (t1 t2)
7d4005cc 208 "Subtract two time values, T1 minus T2.
74fcda73 209Return the difference in the format of a time value."
d35af63c
PE
210 (with-decoded-time-value ((high low micro pico type t1)
211 (high2 low2 micro2 pico2 type2 t2))
ca2d9ad8
LK
212 (setq high (- high high2)
213 low (- low low2)
214 micro (- micro micro2)
d35af63c 215 pico (- pico pico2)
ca2d9ad8 216 type (max type type2))
d35af63c
PE
217 (when (< pico 0)
218 (setq micro (1- micro)
219 pico (+ pico 1000000)))
ca2d9ad8
LK
220 (when (< micro 0)
221 (setq low (1- low)
222 micro (+ micro 1000000)))
223 (when (< low 0)
224 (setq high (1- high)
91472578 225 low (+ low 65536)))
d35af63c 226 (encode-time-value high low micro pico type)))
c113de23 227
74fcda73
RS
228;;;###autoload
229(defun time-add (t1 t2)
7d4005cc 230 "Add two time values T1 and T2. One should represent a time difference."
d35af63c
PE
231 (with-decoded-time-value ((high low micro pico type t1)
232 (high2 low2 micro2 pico2 type2 t2))
ca2d9ad8
LK
233 (setq high (+ high high2)
234 low (+ low low2)
235 micro (+ micro micro2)
d35af63c 236 pico (+ pico pico2)
ca2d9ad8 237 type (max type type2))
d35af63c
PE
238 (when (>= pico 1000000)
239 (setq micro (1+ micro)
240 pico (- pico 1000000)))
ca2d9ad8
LK
241 (when (>= micro 1000000)
242 (setq low (1+ low)
243 micro (- micro 1000000)))
91472578 244 (when (>= low 65536)
ca2d9ad8 245 (setq high (1+ high)
91472578 246 low (- low 65536)))
d35af63c 247 (encode-time-value high low micro pico type)))
74fcda73
RS
248
249;;;###autoload
c113de23 250(defun date-to-day (date)
74fcda73
RS
251 "Return the number of days between year 1 and DATE.
252DATE should be a date-time string."
c113de23
GM
253 (time-to-days (date-to-time date)))
254
74fcda73 255;;;###autoload
c113de23 256(defun days-between (date1 date2)
74fcda73
RS
257 "Return the number of days between DATE1 and DATE2.
258DATE1 and DATE2 should be date-time strings."
c113de23
GM
259 (- (date-to-day date1) (date-to-day date2)))
260
74fcda73 261;;;###autoload
c113de23
GM
262(defun date-leap-year-p (year)
263 "Return t if YEAR is a leap year."
264 (or (and (zerop (% year 4))
265 (not (zerop (% year 100))))
266 (zerop (% year 400))))
267
74fcda73 268;;;###autoload
c113de23 269(defun time-to-day-in-year (time)
1525ea1e 270 "Return the day number within the year corresponding to TIME."
c113de23
GM
271 (let* ((tim (decode-time time))
272 (month (nth 4 tim))
273 (day (nth 3 tim))
274 (year (nth 5 tim))
275 (day-of-year (+ day (* 31 (1- month)))))
276 (when (> month 2)
277 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
278 (when (date-leap-year-p year)
279 (setq day-of-year (1+ day-of-year))))
280 day-of-year))
281
74fcda73 282;;;###autoload
c113de23
GM
283(defun time-to-days (time)
284 "The number of days between the Gregorian date 0001-12-31bce and TIME.
74fcda73 285TIME should be a time value.
c113de23
GM
286The Gregorian date Sunday, December 31, 1bce is imaginary."
287 (let* ((tim (decode-time time))
c113de23
GM
288 (year (nth 5 tim)))
289 (+ (time-to-day-in-year time) ; Days this year
290 (* 365 (1- year)) ; + Days in prior years
291 (/ (1- year) 4) ; + Julian leap years
292 (- (/ (1- year) 100)) ; - century years
293 (/ (1- year) 400)))) ; + Gregorian leap years
294
6f0d4bb6
GM
295(defun time-to-number-of-days (time)
296 "Return the number of days represented by TIME.
297Returns a floating point number."
298 (/ (funcall (eval-when-compile
299 (if (or (featurep 'emacs)
300 (and (fboundp 'float-time)
301 (subrp (symbol-function 'float-time))))
302 'float-time
303 'time-to-seconds)) time) (* 60 60 24)))
23f87bed 304
c113de23
GM
305;;;###autoload
306(defun safe-date-to-time (date)
7d4005cc 307 "Parse a string DATE that represents a date-time and return a time value.
74fcda73 308If DATE is malformed, return a time value of zeros."
c113de23
GM
309 (condition-case ()
310 (date-to-time date)
311 (error '(0 0))))
312
9ce1b62f 313\f
6afa3d67 314;;;###autoload
99d8d540 315(defun format-seconds (string seconds)
6afa3d67
GM
316 "Use format control STRING to format the number SECONDS.
317The valid format specifiers are:
318%y is the number of (365-day) years.
319%d is the number of days.
320%h is the number of hours.
321%m is the number of minutes.
322%s is the number of seconds.
99d8d540 323%z is a non-printing control flag (see below).
6afa3d67
GM
324%% is a literal \"%\".
325
326Upper-case specifiers are followed by the unit-name (e.g. \"years\").
327Lower-case specifiers return only the unit.
328
329\"%\" may be followed by a number specifying a width, with an
330optional leading \".\" for zero-padding. For example, \"%.3Y\" will
331return something of the form \"001 year\".
332
99d8d540
GM
333The \"%z\" specifier does not print anything. When it is used, specifiers
334must be given in order of decreasing size. To the left of \"%z\", nothing
335is output until the first non-zero unit is encountered.
6afa3d67 336
99d8d540 337This function does not work for SECONDS greater than `most-positive-fixnum'."
6afa3d67
GM
338 (let ((start 0)
339 (units '(("y" "year" 31536000)
340 ("d" "day" 86400)
341 ("h" "hour" 3600)
342 ("m" "minute" 60)
99d8d540
GM
343 ("s" "second" 1)
344 ("z")))
6afa3d67 345 (case-fold-search t)
99d8d540 346 spec match usedunits zeroflag larger prev name unit num zeropos)
6afa3d67
GM
347 (while (string-match "%\\.?[0-9]*\\(.\\)" string start)
348 (setq start (match-end 0)
349 spec (match-string 1 string))
350 (unless (string-equal spec "%")
1518e4f0 351 (or (setq match (assoc (downcase spec) units))
6afa3d67 352 (error "Bad format specifier: `%s'" spec))
1518e4f0 353 (if (assoc (downcase spec) usedunits)
6afa3d67 354 (error "Multiple instances of specifier: `%s'" spec))
99d8d540
GM
355 (if (string-equal (car match) "z")
356 (setq zeroflag t)
357 (unless larger
358 (setq unit (nth 2 match)
359 larger (and prev (> unit prev))
360 prev unit)))
361 (push match usedunits)))
362 (and zeroflag larger
363 (error "Units are not in decreasing order of size"))
364 (dolist (u units)
365 (setq spec (car u)
366 name (cadr u)
367 unit (nth 2 u))
6afa3d67 368 (when (string-match (format "%%\\(\\.?[0-9]+\\)?\\(%s\\)" spec) string)
99d8d540
GM
369 (if (string-equal spec "z") ; must be last in units
370 (setq string
371 (replace-regexp-in-string
372 "%z" ""
373 (substring string (min (or zeropos (match-end 0))
374 (match-beginning 0)))))
375 ;; Cf article-make-date-line in gnus-art.
376 (setq num (floor seconds unit)
377 seconds (- seconds (* num unit)))
378 ;; Start position of the first non-zero unit.
379 (or zeropos
380 (setq zeropos (unless (zerop num) (match-beginning 0))))
381 (setq string
382 (replace-match
383 (format (concat "%" (match-string 1 string) "d%s") num
384 (if (string-equal (match-string 2 string) spec)
385 "" ; lower-case, no unit-name
386 (format " %s%s" name
387 (if (= num 1) "" "s"))))
388 t t string))))))
6afa3d67
GM
389 (replace-regexp-in-string "%%" "%" string))
390
ecaf7f4d
SS
391(defvar seconds-to-string
392 (list (list 1 "ms" 0.001)
393 (list 100 "s" 1)
394 (list (* 60 100) "m" 60.0)
395 (list (* 3600 30) "h" 3600.0)
396 (list (* 3600 24 400) "d" (* 3600.0 24.0))
397 (list nil "y" (* 365.25 24 3600)))
398 "Formatting used by the function `seconds-to-string'.")
399;;;###autoload
400(defun seconds-to-string (delay)
401 "Convert the time interval in seconds to a short string."
402 (cond ((> 0 delay) (concat "-" (seconds-to-string (- delay))))
403 ((= 0 delay) "0s")
404 (t (let ((sts seconds-to-string) here)
405 (while (and (car (setq here (pop sts)))
406 (<= (car here) delay)))
001da405 407 (concat (format "%.2f" (/ delay (car (cddr here)))) (cadr here))))))
9ce1b62f 408
c113de23
GM
409(provide 'time-date)
410
411;;; time-date.el ends here