Merge from emacs-24; up to 2012-12-22T02:59:08Z!cyd@gnu.org
[bpt/emacs.git] / lisp / calc / calc-forms.el
CommitLineData
3132f345
CW
1;;; calc-forms.el --- data format conversion functions for Calc
2
ab422c4d 3;; Copyright (C) 1990-1993, 2001-2013 Free Software Foundation, Inc.
3132f345
CW
4
5;; Author: David Gillespie <daveg@synaptics.com>
e8fff8ed 6;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
136211a9
EZ
7
8;; This file is part of GNU Emacs.
9
662c9c64 10;; GNU Emacs is free software: you can redistribute it and/or modify
7c671b23 11;; it under the terms of the GNU General Public License as published by
662c9c64
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
7c671b23 14
136211a9 15;; GNU Emacs is distributed in the hope that it will be useful,
7c671b23
GM
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
662c9c64 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
136211a9 22
3132f345 23;;; Commentary:
136211a9 24
3132f345 25;;; Code:
136211a9
EZ
26
27;; This file is autoloaded from calc-ext.el.
136211a9 28
76216e5a 29(require 'calc-ext)
136211a9
EZ
30(require 'calc-macs)
31
1976223f
JB
32;; Declare functions which are defined elsewhere.
33(declare-function calendar-current-time-zone "cal-dst" ())
34(declare-function calendar-absolute-from-gregorian "calendar" (date))
35(declare-function dst-in-effect "cal-dst" (date))
36
37
136211a9
EZ
38(defun calc-time ()
39 (interactive)
40 (calc-wrapper
41 (let ((time (current-time-string)))
42 (calc-enter-result 0 "time"
43 (list 'mod
44 (list 'hms
5574e209
JB
45 (string-to-number (substring time 11 13))
46 (string-to-number (substring time 14 16))
47 (string-to-number (substring time 17 19)))
bf77c646 48 (list 'hms 24 0 0))))))
136211a9
EZ
49
50(defun calc-to-hms (arg)
51 (interactive "P")
52 (calc-wrapper
53 (if (calc-is-inverse)
54 (if (eq calc-angle-mode 'rad)
55 (calc-unary-op ">rad" 'calcFunc-rad arg)
56 (calc-unary-op ">deg" 'calcFunc-deg arg))
bf77c646 57 (calc-unary-op ">hms" 'calcFunc-hms arg))))
136211a9
EZ
58
59(defun calc-from-hms (arg)
60 (interactive "P")
61 (calc-invert-func)
bf77c646 62 (calc-to-hms arg))
136211a9
EZ
63
64
65(defun calc-hms-notation (fmt)
66 (interactive "sHours-minutes-seconds format (hms, @ ' \", etc.): ")
67 (calc-wrapper
68 (if (string-match "\\`\\([^,; ]+\\)\\([,; ]*\\)\\([^,; ]\\)\\([,; ]*\\)\\([^,; ]\\)\\'" fmt)
69 (progn
70 (calc-change-mode 'calc-hms-format
71 (concat "%s" (math-match-substring fmt 1)
72 (math-match-substring fmt 2)
73 "%s" (math-match-substring fmt 3)
74 (math-match-substring fmt 4)
75 "%s" (math-match-substring fmt 5))
76 t)
77 (setq-default calc-hms-format calc-hms-format)) ; for minibuffer
3132f345 78 (error "Bad hours-minutes-seconds format"))))
136211a9
EZ
79
80(defun calc-date-notation (fmt arg)
81 (interactive "sDate format (e.g., M/D/YY h:mm:ss): \nP")
82 (calc-wrapper
6292c599 83 (if (string-match-p "\\`\\s-*\\'" fmt)
136211a9 84 (setq fmt "1"))
8c21bef6 85 (if (string-match "\\` *\\([0-9]\\|10\\|11\\) *\\'" fmt)
5574e209 86 (setq fmt (nth (string-to-number fmt) calc-standard-date-formats)))
136211a9
EZ
87 (or (string-match "[a-zA-Z]" fmt)
88 (error "Bad date format specifier"))
89 (and arg
90 (>= (setq arg (prefix-numeric-value arg)) 0)
ff3f6c26 91 (<= arg 11)
136211a9
EZ
92 (setq calc-standard-date-formats
93 (copy-sequence calc-standard-date-formats))
94 (setcar (nthcdr arg calc-standard-date-formats) fmt))
95 (let ((case-fold-search nil))
96 (and (not (string-match "<.*>" fmt))
e7c8fcc4 97 ;; Find time part to put in <...>
682ceaf8 98 (string-match "\\`[^hHspPT]*\\([^ac-gi-lnoqrt-zAC-GI-OQRU-Z]*\\(bs\\|bm\\|bh\\|BS\\|BH\\|[hHmpPsST]\\)+[^ac-gi-lnoqrt-zAC-GI-OQRU-Z]*\\)[^hHspPT]*\\'" fmt)
136211a9
EZ
99 (string-match (concat "[^ac-gi-lnoqrt-zAC-GI-OQRT-Z]*"
100 (regexp-quote (math-match-substring fmt 1))
101 "[^ac-gi-lnoqrt-zAC-GI-OQRT-Z]*") fmt)
102 (setq fmt (concat (substring fmt 0 (match-beginning 0))
103 "<"
104 (substring fmt (match-beginning 0) (match-end 0))
105 ">"
106 (substring fmt (match-end 0))))))
107 (let ((lfmt nil)
108 (fullfmt nil)
109 (time nil)
110 pos pos2 sym temp)
111 (let ((case-fold-search nil))
112 (and (setq temp (string-match ":[BS]S" fmt))
113 (aset fmt temp ?C)))
114 (while (setq pos (string-match "[<>a-zA-Z]" fmt))
115 (if (> pos 0)
116 (setq lfmt (cons (substring fmt 0 pos) lfmt)))
117 (setq pos2 (1+ pos))
118 (cond ((= (aref fmt pos) ?\<)
119 (and time (error "Nested <'s not allowed"))
120 (and lfmt (setq fullfmt (nconc lfmt fullfmt)
121 lfmt nil))
122 (setq time t))
123 ((= (aref fmt pos) ?\>)
124 (or time (error "Misplaced > in format"))
125 (and lfmt (setq fullfmt (cons (nreverse lfmt) fullfmt)
126 lfmt nil))
127 (setq time nil))
128 (t
682ceaf8 129 (if (string-match "\\`[^a-zA-Z]*[bBZI][a-zA-Z]" fmt)
136211a9
EZ
130 (setq pos2 (1+ pos2)))
131 (while (and (< pos2 (length fmt))
132 (= (upcase (aref fmt pos2))
133 (upcase (aref fmt (1- pos2)))))
134 (setq pos2 (1+ pos2)))
135 (setq sym (intern (substring fmt pos pos2)))
136 (or (memq sym '(Y YY BY YYY YYYY
682ceaf8 137 ZYYY IYYY Iww w
136211a9
EZ
138 aa AA aaa AAA aaaa AAAA
139 bb BB bbb BBB bbbb BBBB
140 M MM BM mmm Mmm Mmmm MMM MMMM
141 D DD BD d ddd bdd
142 W www Www Wwww WWW WWWW
143 h hh bh H HH BH
144 p P pp PP pppp PPPP
e7c8fcc4 145 m mm bm s ss bs SS BS C
682ceaf8 146 N n J j U b T))
136211a9
EZ
147 (and (eq sym 'X) (not lfmt) (not fullfmt))
148 (error "Bad format code: %s" sym))
149 (and (memq sym '(bb BB bbb BBB bbbb BBBB))
150 (setq lfmt (cons 'b lfmt)))
151 (setq lfmt (cons sym lfmt))))
152 (setq fmt (substring fmt pos2)))
153 (or (equal fmt "")
154 (setq lfmt (cons fmt lfmt)))
155 (and lfmt (if time
156 (setq fullfmt (cons (nreverse lfmt) fullfmt))
157 (setq fullfmt (nconc lfmt fullfmt))))
bf77c646 158 (calc-change-mode 'calc-date-format (nreverse fullfmt) t))))
136211a9
EZ
159
160
161(defun calc-hms-mode ()
162 (interactive)
163 (calc-wrapper
164 (calc-change-mode 'calc-angle-mode 'hms)
3132f345 165 (message "Angles measured in degrees-minutes-seconds")))
136211a9
EZ
166
167
168(defun calc-now (arg)
169 (interactive "P")
bf77c646 170 (calc-date-zero-args "now" 'calcFunc-now arg))
136211a9
EZ
171
172(defun calc-date-part (arg)
173 (interactive "NPart code (1-9 = Y,M,D,H,M,S,Wd,Yd,Hms): ")
174 (if (or (< arg 1) (> arg 9))
175 (error "Part code out of range"))
176 (calc-wrapper
177 (calc-enter-result 1
178 (nth arg '(nil "year" "mnth" "day" "hour" "minu"
179 "sec" "wday" "yday" "hmst"))
180 (list (nth arg '(nil calcFunc-year calcFunc-month
181 calcFunc-day calcFunc-hour
182 calcFunc-minute calcFunc-second
183 calcFunc-weekday calcFunc-yearday
184 calcFunc-time))
bf77c646 185 (calc-top-n 1)))))
136211a9
EZ
186
187(defun calc-date (arg)
188 (interactive "p")
189 (if (or (< arg 1) (> arg 6))
190 (error "Between one and six arguments are allowed"))
191 (calc-wrapper
bf77c646 192 (calc-enter-result arg "date" (cons 'calcFunc-date (calc-top-list-n arg)))))
136211a9
EZ
193
194(defun calc-julian (arg)
195 (interactive "P")
bf77c646 196 (calc-date-one-arg "juln" 'calcFunc-julian arg))
136211a9
EZ
197
198(defun calc-unix-time (arg)
199 (interactive "P")
bf77c646 200 (calc-date-one-arg "unix" 'calcFunc-unixtime arg))
136211a9
EZ
201
202(defun calc-time-zone (arg)
203 (interactive "P")
bf77c646 204 (calc-date-zero-args "zone" 'calcFunc-tzone arg))
136211a9
EZ
205
206(defun calc-convert-time-zones (old &optional new)
207 (interactive "sFrom time zone: ")
208 (calc-wrapper
209 (if (equal old "$")
210 (calc-enter-result 3 "tzcv" (cons 'calcFunc-tzconv (calc-top-list-n 3)))
211 (if (equal old "") (setq old "local"))
212 (or new
213 (setq new (read-string (concat "From time zone: " old
214 ", to zone: "))))
215 (if (stringp old) (setq old (math-read-expr old)))
216 (if (eq (car-safe old) 'error)
1e9a52a5 217 (error "Error in expression: %S" (nth 1 old)))
136211a9
EZ
218 (if (equal new "") (setq new "local"))
219 (if (stringp new) (setq new (math-read-expr new)))
220 (if (eq (car-safe new) 'error)
1e9a52a5 221 (error "Error in expression: %S" (nth 1 new)))
136211a9 222 (calc-enter-result 1 "tzcv" (list 'calcFunc-tzconv
bf77c646 223 (calc-top-n 1) old new)))))
136211a9
EZ
224
225(defun calc-new-week (arg)
226 (interactive "P")
bf77c646 227 (calc-date-one-arg "nwwk" 'calcFunc-newweek arg))
136211a9
EZ
228
229(defun calc-new-month (arg)
230 (interactive "P")
bf77c646 231 (calc-date-one-arg "nwmn" 'calcFunc-newmonth arg))
136211a9
EZ
232
233(defun calc-new-year (arg)
234 (interactive "P")
bf77c646 235 (calc-date-one-arg "nwyr" 'calcFunc-newyear arg))
136211a9
EZ
236
237(defun calc-inc-month (arg)
238 (interactive "p")
bf77c646 239 (calc-date-one-arg "incm" 'calcFunc-incmonth arg))
136211a9
EZ
240
241(defun calc-business-days-plus (arg)
242 (interactive "P")
243 (calc-wrapper
bf77c646 244 (calc-binary-op "bus+" 'calcFunc-badd arg)))
136211a9
EZ
245
246(defun calc-business-days-minus (arg)
247 (interactive "P")
248 (calc-wrapper
bf77c646 249 (calc-binary-op "bus-" 'calcFunc-bsub arg)))
136211a9
EZ
250
251(defun calc-date-zero-args (prefix func arg)
252 (calc-wrapper
253 (if (consp arg)
254 (calc-enter-result 1 prefix (list func (calc-top-n 1)))
255 (calc-enter-result 0 prefix (if arg
256 (list func (prefix-numeric-value arg))
bf77c646 257 (list func))))))
136211a9
EZ
258
259(defun calc-date-one-arg (prefix func arg)
260 (calc-wrapper
261 (if (consp arg)
262 (calc-enter-result 2 prefix (cons func (calc-top-list-n 2)))
263 (calc-enter-result 1 prefix (if arg
264 (list func (calc-top-n 1)
265 (prefix-numeric-value arg))
bf77c646 266 (list func (calc-top-n 1)))))))
136211a9
EZ
267
268
269;;;; Hours-minutes-seconds forms.
270
271(defun math-normalize-hms (a)
272 (let ((h (math-normalize (nth 1 a)))
273 (m (math-normalize (nth 2 a)))
274 (s (let ((calc-internal-prec (max (- calc-internal-prec 4) 3)))
275 (math-normalize (nth 3 a)))))
276 (if (math-negp h)
277 (progn
278 (if (math-posp s)
279 (setq s (math-add s -60)
280 m (math-add m 1)))
281 (if (math-posp m)
282 (setq m (math-add m -60)
283 h (math-add h 1)))
284 (if (not (Math-lessp -60 s))
285 (setq s (math-add s 60)
286 m (math-add m -1)))
287 (if (not (Math-lessp -60 m))
288 (setq m (math-add m 60)
289 h (math-add h -1))))
290 (if (math-negp s)
291 (setq s (math-add s 60)
292 m (math-add m -1)))
293 (if (math-negp m)
294 (setq m (math-add m 60)
295 h (math-add h -1)))
296 (if (not (Math-lessp s 60))
297 (setq s (math-add s -60)
298 m (math-add m 1)))
299 (if (not (Math-lessp m 60))
300 (setq m (math-add m -60)
301 h (math-add h 1))))
302 (if (and (eq (car-safe s) 'float)
303 (<= (+ (math-numdigs (nth 1 s)) (nth 2 s))
304 (- 2 calc-internal-prec)))
305 (setq s 0))
bf77c646 306 (list 'hms h m s)))
136211a9
EZ
307
308;;; Convert A from ANG or current angular mode to HMS format.
309(defun math-to-hms (a &optional ang) ; [X R] [Public]
310 (cond ((eq (car-safe a) 'hms) a)
311 ((eq (car-safe a) 'sdev)
312 (math-make-sdev (math-to-hms (nth 1 a))
313 (math-to-hms (nth 2 a))))
314 ((not (Math-numberp a))
315 (list 'calcFunc-hms a))
316 ((math-negp a)
317 (math-neg (math-to-hms (math-neg a) ang)))
318 ((eq (or ang calc-angle-mode) 'rad)
319 (math-to-hms (math-div a (math-pi-over-180)) 'deg))
320 ((memq (car-safe a) '(cplx polar)) a)
321 (t
322 ;(setq a (let ((calc-internal-prec (max (1- calc-internal-prec) 3)))
323 ; (math-normalize a)))
324 (math-normalize
325 (let* ((b (math-mul a 3600))
326 (hm (math-trunc (math-div b 60)))
327 (hmd (math-idivmod hm 60)))
328 (list 'hms
329 (car hmd)
330 (cdr hmd)
bf77c646 331 (math-sub b (math-mul hm 60))))))))
136211a9
EZ
332(defun calcFunc-hms (h &optional m s)
333 (or (Math-realp h) (math-reject-arg h 'realp))
334 (or m (setq m 0))
335 (or (Math-realp m) (math-reject-arg m 'realp))
336 (or s (setq s 0))
337 (or (Math-realp s) (math-reject-arg s 'realp))
338 (if (and (not (Math-lessp m 0)) (Math-lessp m 60)
339 (not (Math-lessp s 0)) (Math-lessp s 60))
340 (math-add (math-to-hms h)
341 (list 'hms 0 m s))
342 (math-to-hms (math-add h
343 (math-add (math-div (or m 0) 60)
344 (math-div (or s 0) 3600)))
bf77c646 345 'deg)))
136211a9
EZ
346
347;;; Convert A from HMS format to ANG or current angular mode.
348(defun math-from-hms (a &optional ang) ; [R X] [Public]
349 (cond ((not (eq (car-safe a) 'hms))
350 (if (Math-numberp a)
351 a
352 (if (eq (car-safe a) 'sdev)
353 (math-make-sdev (math-from-hms (nth 1 a) ang)
354 (math-from-hms (nth 2 a) ang))
355 (if (eq (or ang calc-angle-mode) 'rad)
356 (list 'calcFunc-rad a)
357 (list 'calcFunc-deg a)))))
358 ((math-negp a)
359 (math-neg (math-from-hms (math-neg a) ang)))
360 ((eq (or ang calc-angle-mode) 'rad)
361 (math-mul (math-from-hms a 'deg) (math-pi-over-180)))
362 (t
363 (math-add (math-div (math-add (math-div (nth 3 a)
364 '(float 6 1))
365 (nth 2 a))
366 60)
bf77c646 367 (nth 1 a)))))
136211a9
EZ
368
369;;;; Date forms.
370
371
372;;; Some of these functions are adapted from Edward Reingold's "calendar.el".
373;;; These versions are rewritten to use arbitrary-size integers.
136211a9
EZ
374
375;;; A numerical date is the number of days since midnight on
e8909bdc
JB
376;;; the morning of December 31, 1 B.C. (Gregorian) or January 2, 1 A.D. (Julian).
377;;; Emacs's calendar refers to such a date as an absolute date, some Calc function
378;;; names also use that terminology. If the date is a non-integer, it represents
379;;; a specific date and time.
136211a9
EZ
380;;; A "dt" is a list of the form, (year month day), corresponding to
381;;; an integer code, or (year month day hour minute second), corresponding
382;;; to a non-integer code.
383
c9f618db
JB
384(defun math-date-to-gregorian-dt (date)
385 "Return the day (YEAR MONTH DAY) in the Gregorian calendar.
386DATE is the number of days since December 31, -1 in the Gregorian calendar."
387 (let* ((month 1)
388 day
389 (year (math-quotient (math-add date (if (Math-lessp date 711859)
390 365 ; for speed, we take
391 -108)) ; >1950 as a special case
392 (if (math-negp date) 366 365)))
393 ; this result may be an overestimate
394 temp)
395 (while (Math-lessp date (setq temp (math-absolute-from-gregorian-dt year 1 1)))
396 (setq year (math-add year -1)))
397 (if (eq year 0) (setq year -1))
398 (setq date (1+ (math-sub date temp)))
399 (setq temp
400 (if (math-leap-year-p year)
401 [1 32 61 92 122 153 183 214 245 275 306 336 999]
402 [1 32 60 91 121 152 182 213 244 274 305 335 999]))
403 (while (>= date (aref temp month))
404 (setq month (1+ month)))
405 (setq day (1+ (- date (aref temp (1- month)))))
406 (list year month day)))
407
408(defun math-date-to-julian-dt (date)
409 "Return the day (YEAR MONTH DAY) in the Julian calendar.
410DATE is the number of days since December 31, -1 in the Gregorian calendar."
411 (let* ((month 1)
412 day
413 (year (math-quotient (math-add date (if (Math-lessp date 711859)
e8909bdc
JB
414 367 ; for speed, we take
415 -106)) ; >1950 as a special case
c9f618db
JB
416 (if (math-negp date) 366 365)))
417 ; this result may be an overestimate
418 temp)
419 (while (Math-lessp date (setq temp (math-absolute-from-julian-dt year 1 1)))
420 (setq year (math-add year -1)))
421 (if (eq year 0) (setq year -1))
422 (setq date (1+ (math-sub date temp)))
423 (setq temp
424 (if (math-leap-year-p year t)
425 [1 32 61 92 122 153 183 214 245 275 306 336 999]
426 [1 32 60 91 121 152 182 213 244 274 305 335 999]))
427 (while (>= date (aref temp month))
428 (setq month (1+ month)))
429 (setq day (1+ (- date (aref temp (1- month)))))
430 (list year month day)))
431
136211a9 432(defun math-date-to-dt (value)
c9f618db
JB
433 "Return the day and time of VALUE.
434The integer part of VALUE is the number of days since Dec 31, -1
435in the Gregorian calendar and the remaining part determines the time."
136211a9
EZ
436 (if (eq (car-safe value) 'date)
437 (setq value (nth 1 value)))
438 (or (math-realp value)
439 (math-reject-arg value 'datep))
440 (let* ((parts (math-date-parts value))
441 (date (car parts))
442 (time (nth 1 parts))
c9f618db
JB
443 (dt (if (and calc-gregorian-switch
444 (Math-lessp value
445 (or
446 (nth 3 calc-gregorian-switch)
447 (apply 'math-absolute-from-gregorian-dt calc-gregorian-switch))
448))
eef97f35
JB
449 (math-date-to-julian-dt date)
450 (math-date-to-gregorian-dt date))))
136211a9 451 (if (math-integerp value)
c9f618db
JB
452 dt
453 (append dt
454 (list
455 (/ time 3600)
456 (% (/ time 60) 60)
457 (math-add (% time 60) (nth 2 parts)))))))
136211a9 458
682ceaf8
JB
459(defun math-date-to-iso-dt (date)
460 "Return the ISO8601 date (year week day) of DATE."
461 (unless (Math-integerp date)
462 (setq date (math-floor date)))
463 (let* ((approx (nth 0 (math-date-to-gregorian-dt (math-sub date 3))))
464 (year (math-add approx
465 (let ((y approx)
466 (sum 0))
467 (while (>= (math-compare date
1bf3989c 468 (math-absolute-from-iso-dt (setq y (math-add y 1)) 1 1)) 0)
682ceaf8
JB
469 (setq sum (+ sum 1)))
470 sum))))
471 (list
472 year
473 (math-add (car (math-idivmod
1bf3989c 474 (math-sub date (math-absolute-from-iso-dt year 1 1))
682ceaf8
JB
475 7))
476 1)
77a74935
JB
477 (let ((day (calcFunc-mod date 7)))
478 (if (= day 0) 7 day)))))
682ceaf8 479
136211a9
EZ
480(defun math-dt-to-date (dt)
481 (or (integerp (nth 1 dt))
482 (math-reject-arg (nth 1 dt) 'fixnump))
483 (if (or (< (nth 1 dt) 1) (> (nth 1 dt) 12))
484 (math-reject-arg (nth 1 dt) "Month value is out of range"))
485 (or (integerp (nth 2 dt))
486 (math-reject-arg (nth 2 dt) 'fixnump))
487 (if (or (< (nth 2 dt) 1) (> (nth 2 dt) 31))
488 (math-reject-arg (nth 2 dt) "Day value is out of range"))
c9f618db 489 (let ((date (math-absolute-from-dt (car dt) (nth 1 dt) (nth 2 dt))))
136211a9
EZ
490 (if (nth 3 dt)
491 (math-add (math-float date)
492 (math-div (math-add (+ (* (nth 3 dt) 3600)
493 (* (nth 4 dt) 60))
494 (nth 5 dt))
495 '(float 864 2)))
bf77c646 496 date)))
136211a9 497
682ceaf8
JB
498(defun math-iso-dt-to-date (dt)
499 (let ((date (math-absolute-from-iso-dt (car dt) (nth 1 dt) (nth 2 dt))))
500 (if (nth 3 dt)
501 (math-add (math-float date)
502 (math-div (math-add (+ (* (nth 3 dt) 3600)
503 (* (nth 4 dt) 60))
504 (nth 5 dt))
505 '(float 864 2)))
506 date)))
507
136211a9
EZ
508(defun math-date-parts (value &optional offset)
509 (let* ((date (math-floor value))
510 (time (math-round (math-mul (math-sub value (or offset date)) 86400)
511 (and (> calc-internal-prec 12)
512 (- calc-internal-prec 12))))
513 (ftime (math-floor time)))
514 (list date
515 ftime
bf77c646 516 (math-sub time ftime))))
136211a9
EZ
517
518
519(defun math-this-year ()
0bfcf5c5 520 (nth 5 (decode-time)))
136211a9 521
c9f618db
JB
522(defun math-leap-year-p (year &optional julian)
523 "Non-nil if YEAR is a leap year.
524If JULIAN is non-nil, then use the criterion for leap years
525in the Julian calendar, otherwise use the criterion in the
526Gregorian calendar."
527 (if julian
136211a9
EZ
528 (if (math-negp year)
529 (= (math-imod (math-neg year) 4) 1)
530 (= (math-imod year 4) 0))
e8909bdc
JB
531 (if (math-negp year)
532 (setq year (math-sub -1 year)))
136211a9
EZ
533 (setq year (math-imod year 400))
534 (or (and (= (% year 4) 0) (/= (% year 100) 0))
bf77c646 535 (= year 0))))
136211a9
EZ
536
537(defun math-days-in-month (year month)
538 (if (and (= month 2) (math-leap-year-p year))
539 29
bf77c646 540 (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
136211a9 541
c9f618db
JB
542(defun math-day-in-year (year month day &optional julian)
543 "Return the number of days of the year up to YEAR MONTH DAY.
544The count includes the given date.
545If JULIAN is non-nil, use the Julian calendar, otherwise
546use the Gregorian calendar."
136211a9
EZ
547 (let ((day-of-year (+ day (* 31 (1- month)))))
548 (if (> month 2)
549 (progn
550 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
c9f618db 551 (if (math-leap-year-p year julian)
136211a9 552 (setq day-of-year (1+ day-of-year)))))
bf77c646 553 day-of-year))
136211a9 554
c9f618db
JB
555(defun math-day-number (year month day)
556 "Return the number of days of the year up to YEAR MONTH DAY.
557The count includes the given date."
558 (if calc-gregorian-switch
3dc7dad2 559 (cond ((eq year (nth 0 calc-gregorian-switch))
c9f618db
JB
560 (1+
561 (- (math-absolute-from-dt year month day)
562 (math-absolute-from-dt year 1 1))))
563 ((Math-lessp year (nth 0 calc-gregorian-switch))
564 (math-day-in-year year month day t))
565 (t
566 (math-day-in-year year month day)))
567 (math-day-in-year year month day)))
568
569(defun math-dt-before-p (dt1 dt2)
570 "Non-nil if DT1 occurs before DT2.
571A DT is a list of the form (YEAR MONTH DAY)."
572 (or (Math-lessp (nth 0 dt1) (nth 0 dt2))
573 (and (equal (nth 0 dt1) (nth 0 dt2))
574 (or (< (nth 1 dt1) (nth 1 dt2))
575 (and (= (nth 1 dt1) (nth 1 dt2))
576 (< (nth 2 dt1) (nth 2 dt2)))))))
577
578(defun math-absolute-from-gregorian-dt (year month day)
579 "Return the DATE of the day given by the Gregorian day YEAR MONTH DAY.
580Recall that DATE is the number of days since December 31, -1
581in the Gregorian calendar."
136211a9
EZ
582 (if (eq year 0) (setq year -1))
583 (let ((yearm1 (math-sub year 1)))
c9f618db
JB
584 (math-sub
585 ;; Add the number of days of the year and the numbers of days
586 ;; in the previous years (leap year days to be added separately)
587 (math-add (math-day-in-year year month day)
588 (math-add (math-mul 365 yearm1)
589 ;; Add the number of Julian leap years
590 (if (math-posp year)
591 (math-quotient yearm1 4)
592 (math-sub 365
593 (math-quotient (math-sub 3 year)
594 4)))))
595 ;; Subtract the number of Julian leap years which are not
596 ;; Gregorian leap years. In C=4N+r centuries, there will
597 ;; be 3N+r of these days. The following will compute
598 ;; 3N+r.
599 (let* ((correction (math-mul (math-quotient yearm1 100) 3))
600 (res (math-idivmod correction 4)))
601 (math-add (if (= (cdr res) 0)
602 0
603 1)
604 (car res))))))
605
606(defun math-absolute-from-julian-dt (year month day)
607 "Return the DATE of the day given by the Julian day YEAR MONTH DAY.
608Recall that DATE is the number of days since December 31, -1
609in the Gregorian calendar."
610 (if (eq year 0) (setq year -1))
611 (let ((yearm1 (math-sub year 1)))
612 (math-sub
613 ;; Add the number of days of the year and the numbers of days
614 ;; in the previous years (leap year days to be added separately)
615 (math-add (math-day-in-year year month day)
616 (math-add (math-mul 365 yearm1)
617 ;; Add the number of Julian leap years
618 (if (math-posp year)
619 (math-quotient yearm1 4)
620 (math-sub 365
621 (math-quotient (math-sub 3 year)
622 4)))))
623 ;; Adjustment, since January 1, 1 (Julian) is absolute day -1
624 2)))
625
3dc7dad2
JB
626;; calc-gregorian-switch is a customizable variable defined in calc.el
627(defvar calc-gregorian-switch)
628
682ceaf8
JB
629(defun math-absolute-from-iso-dt (year week day)
630 "Return the DATE of the day given by the iso8601 day YEAR WEEK DAY."
631 (let* ((janfour (math-absolute-from-gregorian-dt year 1 4))
632 (prevmon (math-sub janfour
633 (cdr (math-idivmod (math-sub janfour 1) 7)))))
634 (math-add
635 (math-add prevmon (* (1- week) 7))
636 (if (zerop day) 6 (1- day)))))
3dc7dad2 637
c9f618db
JB
638(defun math-absolute-from-dt (year month day)
639 "Return the DATE of the day given by the day YEAR MONTH DAY.
640Recall that DATE is the number of days since December 31, -1
641in the Gregorian calendar."
642 (if (and calc-gregorian-switch
643 ;; The next few lines determine if the given date
644 ;; occurs before the switch to the Gregorian calendar.
645 (math-dt-before-p (list year month day) calc-gregorian-switch))
646 (math-absolute-from-julian-dt year month day)
647 (math-absolute-from-gregorian-dt year month day)))
136211a9 648
865fe16f 649;;; It is safe to redefine these in your init file to use a different
136211a9
EZ
650;;; language.
651
652(defvar math-long-weekday-names '( "Sunday" "Monday" "Tuesday" "Wednesday"
653 "Thursday" "Friday" "Saturday" ))
654(defvar math-short-weekday-names '( "Sun" "Mon" "Tue" "Wed"
655 "Thu" "Fri" "Sat" ))
656
657(defvar math-long-month-names '( "January" "February" "March" "April"
658 "May" "June" "July" "August"
659 "September" "October" "November" "December" ))
660(defvar math-short-month-names '( "Jan" "Feb" "Mar" "Apr" "May" "Jun"
661 "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" ))
662
663
3132f345 664(defvar math-format-date-cache nil)
98223359 665
d55ebb63 666;; The variables math-fd-date, math-fd-dt, math-fd-year,
98223359
JB
667;; math-fd-month, math-fd-day, math-fd-weekday, math-fd-hour,
668;; math-fd-minute, math-fd-second, math-fd-bc-flag are local
669;; to math-format-date, but are used by math-format-date-part,
670;; which is called by math-format-date.
671(defvar math-fd-date)
672(defvar math-fd-dt)
673(defvar math-fd-year)
674(defvar math-fd-month)
675(defvar math-fd-day)
676(defvar math-fd-weekday)
677(defvar math-fd-hour)
678(defvar math-fd-minute)
679(defvar math-fd-second)
680(defvar math-fd-bc-flag)
682ceaf8
JB
681(defvar math-fd-iso-dt)
682(defvar math-fd-isoyear)
683(defvar math-fd-isoweek)
684(defvar math-fd-isoweekday)
98223359
JB
685
686(defun math-format-date (math-fd-date)
687 (if (eq (car-safe math-fd-date) 'date)
688 (setq math-fd-date (nth 1 math-fd-date)))
689 (let ((entry (list math-fd-date calc-internal-prec calc-date-format)))
136211a9 690 (or (cdr (assoc entry math-format-date-cache))
98223359 691 (let* ((math-fd-dt nil)
682ceaf8 692 (math-fd-iso-dt nil)
136211a9
EZ
693 (calc-group-digits nil)
694 (calc-leading-zeros nil)
695 (calc-number-radix 10)
2d1c8c66 696 (calc-twos-complement-mode nil)
d55ebb63 697 math-fd-year math-fd-month math-fd-day math-fd-weekday
98223359 698 math-fd-hour math-fd-minute math-fd-second
682ceaf8 699 math-fd-isoyear math-fd-isoweek math-fd-isoweekday
98223359 700 (math-fd-bc-flag nil)
136211a9
EZ
701 (fmt (apply 'concat (mapcar 'math-format-date-part
702 calc-date-format))))
703 (setq math-format-date-cache (cons (cons entry fmt)
704 math-format-date-cache))
98223359
JB
705 (and (setq math-fd-dt (nthcdr 10 math-format-date-cache))
706 (setcdr math-fd-dt nil))
bf77c646 707 fmt))))
136211a9 708
310e60d9
PE
709(defconst math-julian-date-beginning '(float 17214225 -1)
710 "The beginning of the Julian date calendar,
711as measured in the number of days before December 31, 1 BC (Gregorian).")
98e9ffe3 712
310e60d9
PE
713(defconst math-julian-date-beginning-int 1721423
714 "The beginning of the Julian date calendar,
715as measured in the integer number of days before December 31, 1 BC (Gregorian).")
98e9ffe3 716
136211a9
EZ
717(defun math-format-date-part (x)
718 (cond ((stringp x)
719 x)
720 ((listp x)
98223359 721 (if (math-integerp math-fd-date)
136211a9
EZ
722 ""
723 (apply 'concat (mapcar 'math-format-date-part x))))
724 ((eq x 'X)
725 "")
726 ((eq x 'N)
98223359 727 (math-format-number math-fd-date))
136211a9 728 ((eq x 'n)
98223359 729 (math-format-number (math-floor math-fd-date)))
136211a9 730 ((eq x 'J)
3b6d80bc 731 (math-format-number
98e9ffe3 732 (math-add math-fd-date math-julian-date-beginning)))
136211a9 733 ((eq x 'j)
3b6d80bc
JB
734 (math-format-number (math-add
735 (math-floor math-fd-date)
98e9ffe3 736 math-julian-date-beginning-int)))
136211a9 737 ((eq x 'U)
98223359 738 (math-format-number (nth 1 (math-date-parts math-fd-date 719164))))
682ceaf8
JB
739 ((memq x '(IYYY Iww w))
740 (progn
741 (or math-fd-iso-dt
742 (setq math-fd-iso-dt (math-date-to-iso-dt math-fd-date)
682ceaf8
JB
743 math-fd-isoyear (car math-fd-iso-dt)
744 math-fd-isoweek (nth 1 math-fd-iso-dt)
745 math-fd-isoweekday (nth 2 math-fd-iso-dt)))
746 (cond ((eq x 'IYYY)
747 (let* ((neg (Math-negp math-fd-isoyear))
748 (pyear (calcFunc-abs math-fd-isoyear)))
749 (if (and (natnump pyear) (< pyear 10000))
750 (concat (if neg "-" "") (format "%04d" pyear))
751 (concat (if neg "-" "+") (math-format-number pyear)))))
752 ((eq x 'Iww)
753 (concat "W" (format "%02d" math-fd-isoweek)))
754 ((eq x 'w)
755 (format "%d" math-fd-isoweekday)))))
136211a9 756 ((progn
98223359 757 (or math-fd-dt
136211a9 758 (progn
98223359
JB
759 (setq math-fd-dt (math-date-to-dt math-fd-date)
760 math-fd-year (car math-fd-dt)
761 math-fd-month (nth 1 math-fd-dt)
762 math-fd-day (nth 2 math-fd-dt)
c9f618db 763 math-fd-weekday (math-mod (math-floor math-fd-date) 7)
98223359
JB
764 math-fd-hour (nth 3 math-fd-dt)
765 math-fd-minute (nth 4 math-fd-dt)
766 math-fd-second (nth 5 math-fd-dt))
136211a9 767 (and (memq 'b calc-date-format)
98223359
JB
768 (math-negp math-fd-year)
769 (setq math-fd-year (math-neg math-fd-year)
770 math-fd-bc-flag t))))
136211a9 771 (memq x '(Y YY BY)))
98223359 772 (if (and (integerp math-fd-year) (> math-fd-year 1940) (< math-fd-year 2040))
136211a9
EZ
773 (format (cond ((eq x 'YY) "%02d")
774 ((eq x 'BYY) "%2d")
775 (t "%d"))
98223359
JB
776 (% math-fd-year 100))
777 (if (and (natnump math-fd-year) (< math-fd-year 100))
778 (format "+%d" math-fd-year)
779 (math-format-number math-fd-year))))
136211a9 780 ((eq x 'YYY)
98223359 781 (math-format-number math-fd-year))
136211a9 782 ((eq x 'YYYY)
98223359
JB
783 (if (and (natnump math-fd-year) (< math-fd-year 100))
784 (format "+%d" math-fd-year)
785 (math-format-number math-fd-year)))
682ceaf8
JB
786 ((eq x 'ZYYY)
787 (let* ((year (if (Math-negp math-fd-year)
788 (math-add math-fd-year 1)
789 math-fd-year))
790 (neg (Math-negp year))
791 (pyear (calcFunc-abs year)))
792 (if (and (natnump pyear) (< pyear 10000))
793 (concat (if neg "-" "") (format "%04d" pyear))
794 (concat (if neg "-" "+") (math-format-number pyear)))))
136211a9
EZ
795 ((eq x 'b) "")
796 ((eq x 'aa)
98223359 797 (and (not math-fd-bc-flag) "ad"))
136211a9 798 ((eq x 'AA)
98223359 799 (and (not math-fd-bc-flag) "AD"))
136211a9 800 ((eq x 'aaa)
98223359 801 (and (not math-fd-bc-flag) "ad "))
136211a9 802 ((eq x 'AAA)
98223359 803 (and (not math-fd-bc-flag) "AD "))
136211a9 804 ((eq x 'aaaa)
98223359 805 (and (not math-fd-bc-flag) "a.d."))
136211a9 806 ((eq x 'AAAA)
98223359 807 (and (not math-fd-bc-flag) "A.D."))
136211a9 808 ((eq x 'bb)
98223359 809 (and math-fd-bc-flag "bc"))
136211a9 810 ((eq x 'BB)
98223359 811 (and math-fd-bc-flag "BC"))
136211a9 812 ((eq x 'bbb)
98223359 813 (and math-fd-bc-flag " bc"))
136211a9 814 ((eq x 'BBB)
98223359 815 (and math-fd-bc-flag " BC"))
136211a9 816 ((eq x 'bbbb)
98223359 817 (and math-fd-bc-flag "b.c."))
136211a9 818 ((eq x 'BBBB)
98223359 819 (and math-fd-bc-flag "B.C."))
682ceaf8 820 ((eq x 'T) "T")
136211a9 821 ((eq x 'M)
98223359 822 (format "%d" math-fd-month))
136211a9 823 ((eq x 'MM)
98223359 824 (format "%02d" math-fd-month))
136211a9 825 ((eq x 'BM)
98223359 826 (format "%2d" math-fd-month))
136211a9 827 ((eq x 'mmm)
98223359 828 (downcase (nth (1- math-fd-month) math-short-month-names)))
136211a9 829 ((eq x 'Mmm)
98223359 830 (nth (1- math-fd-month) math-short-month-names))
136211a9 831 ((eq x 'MMM)
98223359 832 (upcase (nth (1- math-fd-month) math-short-month-names)))
136211a9 833 ((eq x 'Mmmm)
98223359 834 (nth (1- math-fd-month) math-long-month-names))
136211a9 835 ((eq x 'MMMM)
98223359 836 (upcase (nth (1- math-fd-month) math-long-month-names)))
136211a9 837 ((eq x 'D)
98223359 838 (format "%d" math-fd-day))
136211a9 839 ((eq x 'DD)
98223359 840 (format "%02d" math-fd-day))
136211a9 841 ((eq x 'BD)
98223359 842 (format "%2d" math-fd-day))
136211a9 843 ((eq x 'W)
98223359 844 (format "%d" math-fd-weekday))
136211a9 845 ((eq x 'www)
98223359 846 (downcase (nth math-fd-weekday math-short-weekday-names)))
136211a9 847 ((eq x 'Www)
98223359 848 (nth math-fd-weekday math-short-weekday-names))
136211a9 849 ((eq x 'WWW)
98223359 850 (upcase (nth math-fd-weekday math-short-weekday-names)))
136211a9 851 ((eq x 'Wwww)
98223359 852 (nth math-fd-weekday math-long-weekday-names))
136211a9 853 ((eq x 'WWWW)
98223359 854 (upcase (nth math-fd-weekday math-long-weekday-names)))
136211a9 855 ((eq x 'd)
98223359 856 (format "%d" (math-day-number math-fd-year math-fd-month math-fd-day)))
136211a9 857 ((eq x 'ddd)
98223359 858 (format "%03d" (math-day-number math-fd-year math-fd-month math-fd-day)))
136211a9 859 ((eq x 'bdd)
98223359 860 (format "%3d" (math-day-number math-fd-year math-fd-month math-fd-day)))
136211a9 861 ((eq x 'h)
98223359 862 (and math-fd-hour (format "%d" math-fd-hour)))
136211a9 863 ((eq x 'hh)
98223359 864 (and math-fd-hour (format "%02d" math-fd-hour)))
136211a9 865 ((eq x 'bh)
98223359 866 (and math-fd-hour (format "%2d" math-fd-hour)))
136211a9 867 ((eq x 'H)
98223359 868 (and math-fd-hour (format "%d" (1+ (% (+ math-fd-hour 11) 12)))))
136211a9 869 ((eq x 'HH)
98223359 870 (and math-fd-hour (format "%02d" (1+ (% (+ math-fd-hour 11) 12)))))
136211a9 871 ((eq x 'BH)
98223359 872 (and math-fd-hour (format "%2d" (1+ (% (+ math-fd-hour 11) 12)))))
136211a9 873 ((eq x 'p)
98223359 874 (and math-fd-hour (if (< math-fd-hour 12) "a" "p")))
136211a9 875 ((eq x 'P)
98223359 876 (and math-fd-hour (if (< math-fd-hour 12) "A" "P")))
136211a9 877 ((eq x 'pp)
98223359 878 (and math-fd-hour (if (< math-fd-hour 12) "am" "pm")))
136211a9 879 ((eq x 'PP)
98223359 880 (and math-fd-hour (if (< math-fd-hour 12) "AM" "PM")))
136211a9 881 ((eq x 'pppp)
98223359 882 (and math-fd-hour (if (< math-fd-hour 12) "a.m." "p.m.")))
136211a9 883 ((eq x 'PPPP)
98223359 884 (and math-fd-hour (if (< math-fd-hour 12) "A.M." "P.M.")))
136211a9 885 ((eq x 'm)
98223359 886 (and math-fd-minute (format "%d" math-fd-minute)))
136211a9 887 ((eq x 'mm)
98223359 888 (and math-fd-minute (format "%02d" math-fd-minute)))
136211a9 889 ((eq x 'bm)
98223359 890 (and math-fd-minute (format "%2d" math-fd-minute)))
136211a9 891 ((eq x 'C)
98223359 892 (and math-fd-second (not (math-zerop math-fd-second))
136211a9
EZ
893 ":"))
894 ((memq x '(s ss bs SS BS))
98223359
JB
895 (and math-fd-second
896 (not (and (memq x '(SS BS)) (math-zerop math-fd-second)))
897 (if (integerp math-fd-second)
136211a9
EZ
898 (format (cond ((memq x '(ss SS)) "%02d")
899 ((memq x '(bs BS)) "%2d")
900 (t "%d"))
98223359
JB
901 math-fd-second)
902 (concat (if (Math-lessp math-fd-second 10)
136211a9
EZ
903 (cond ((memq x '(ss SS)) "0")
904 ((memq x '(bs BS)) " ")
905 (t ""))
906 "")
907 (let ((calc-float-format
908 (list 'fix (min (- 12 calc-internal-prec)
909 0))))
98223359 910 (math-format-number math-fd-second))))))))
136211a9 911
98223359
JB
912;; The variable math-pd-str is local to math-parse-date and
913;; math-parse-standard-date, but is used by math-parse-date-word,
914;; which is called by math-parse-date and math-parse-standard-date.
915(defvar math-pd-str)
136211a9 916
98223359 917(defun math-parse-date (math-pd-str)
136211a9 918 (catch 'syntax
98223359
JB
919 (or (math-parse-standard-date math-pd-str t)
920 (math-parse-standard-date math-pd-str nil)
fd1f1603 921 (and (string-match "W[0-9][0-9]" math-pd-str)
ff3f6c26 922 (math-parse-iso-date math-pd-str))
98223359
JB
923 (and (string-match "\\`[^-+/0-9a-zA-Z]*\\([-+]?[0-9]+\\.?[0-9]*\\([eE][-+]?[0-9]+\\)?\\)[^-+/0-9a-zA-Z]*\\'" math-pd-str)
924 (list 'date (math-read-number (math-match-substring math-pd-str 1))))
136211a9
EZ
925 (let ((case-fold-search t)
926 (year nil) (month nil) (day nil) (weekday nil)
927 (hour nil) (minute nil) (second nil) (bc-flag nil)
928 (a nil) (b nil) (c nil) (bigyear nil) temp)
929
930 ;; Extract the time, if any.
96cd475f
JB
931 (if (or (string-match "\\([0-9][0-9]?\\):\\([0-9][0-9]?\\)\\(:\\([0-9][0-9]?\\(\\.[0-9]+\\)?\\)\\)? *\\([ap]\\>\\|[ap]m\\|[ap]\\. *m\\.\\|noon\\|n\\>\\|midnight\\|mid\\>\\|m\\>\\)?" math-pd-str)
932 (string-match "\\([0-9][0-9]?\\)\\(\\)\\(\\(\\(\\)\\)\\) *\\([ap]\\>\\|[ap]m\\|[ap]\\. *m\\.\\|noon\\|n\\>\\|midnight\\|mid\\>\\|m\\>\\)" math-pd-str))
98223359 933 (let ((ampm (math-match-substring math-pd-str 6)))
5574e209 934 (setq hour (string-to-number (math-match-substring math-pd-str 1))
98223359
JB
935 minute (math-match-substring math-pd-str 2)
936 second (math-match-substring math-pd-str 4)
937 math-pd-str (concat (substring math-pd-str 0 (match-beginning 0))
938 (substring math-pd-str (match-end 0))))
136211a9
EZ
939 (if (equal minute "")
940 (setq minute 0)
5574e209 941 (setq minute (string-to-number minute)))
136211a9
EZ
942 (if (equal second "")
943 (setq second 0)
944 (setq second (math-read-number second)))
945 (if (equal ampm "")
fd1f1603
JB
946 (if (or
947 (> hour 24)
948 (and (= hour 24)
949 (not (= minute 0))
950 (not (eq second 0))))
951 (throw 'syntax "Hour value is out of range"))
136211a9
EZ
952 (setq ampm (upcase (aref ampm 0)))
953 (if (memq ampm '(?N ?M))
954 (if (and (= hour 12) (= minute 0) (eq second 0))
955 (if (eq ampm ?M) (setq hour 0))
956 (throw 'syntax
957 "Time must be 12:00:00 in this context"))
958 (if (or (= hour 0) (> hour 12))
fd1f1603 959 (throw 'syntax "Hour value is out of range"))
136211a9
EZ
960 (if (eq (= ampm ?A) (= hour 12))
961 (setq hour (% (+ hour 12) 24)))))))
962
963 ;; Rewrite xx-yy-zz to xx/yy/zz to avoid seeing "-" as a minus sign.
98223359 964 (while (string-match "[0-9a-zA-Z]\\(-\\)[0-9a-zA-Z]" math-pd-str)
136211a9 965 (progn
98223359
JB
966 (setq math-pd-str (copy-sequence math-pd-str))
967 (aset math-pd-str (match-beginning 1) ?\/)))
136211a9
EZ
968
969 ;; Extract obvious month or weekday names.
98223359 970 (if (string-match "[a-zA-Z]" math-pd-str)
136211a9
EZ
971 (progn
972 (setq month (math-parse-date-word math-long-month-names))
973 (setq weekday (math-parse-date-word math-long-weekday-names))
974 (or month (setq month
975 (math-parse-date-word math-short-month-names)))
976 (or weekday (math-parse-date-word math-short-weekday-names))
977 (or hour
978 (if (setq temp (math-parse-date-word
979 '( "noon" "midnight" "mid" )))
980 (setq hour (if (= temp 1) 12 0) minute 0 second 0)))
981 (or (math-parse-date-word '( "ad" "a.d." ))
982 (if (math-parse-date-word '( "bc" "b.c." ))
983 (setq bc-flag t)))
98223359 984 (if (string-match "[a-zA-Z]+" math-pd-str)
136211a9 985 (throw 'syntax (format "Bad word in date: \"%s\""
98223359 986 (math-match-substring math-pd-str 0))))))
136211a9
EZ
987
988 ;; If there is a huge number other than the year, ignore it.
98223359
JB
989 (while (and (string-match "[-+]?0*[1-9][0-9][0-9][0-9][0-9]+" math-pd-str)
990 (setq temp (concat (substring math-pd-str 0 (match-beginning 0))
991 (substring math-pd-str (match-end 0))))
d55ebb63 992 (string-match
98223359
JB
993 "[4-9][0-9]\\|[0-9][0-9][0-9]\\|[-+][0-9]+[^-]*\\'" temp))
994 (setq math-pd-str temp))
136211a9
EZ
995
996 ;; If there is a number with a sign or a large number, it is a year.
98223359
JB
997 (if (or (string-match "\\([-+][0-9]+\\)[^-]*\\'" math-pd-str)
998 (string-match "\\(0*[1-9][0-9][0-9]+\\)" math-pd-str))
999 (setq year (math-match-substring math-pd-str 1)
1000 math-pd-str (concat (substring math-pd-str 0 (match-beginning 1))
1001 (substring math-pd-str (match-end 1)))
136211a9
EZ
1002 year (math-read-number year)
1003 bigyear t))
1004
1005 ;; Collect remaining numbers.
1006 (setq temp 0)
98223359 1007 (while (string-match "[0-9]+" math-pd-str temp)
136211a9 1008 (and c (throw 'syntax "Too many numbers in date"))
5574e209 1009 (setq c (string-to-number (math-match-substring math-pd-str 0)))
136211a9
EZ
1010 (or b (setq b c c nil))
1011 (or a (setq a b b nil))
1012 (setq temp (match-end 0)))
1013
1014 ;; Check that we have the right amount of information.
1015 (setq temp (+ (if year 1 0) (if month 1 0) (if day 1 0)
1016 (if a 1 0) (if b 1 0) (if c 1 0)))
1017 (if (> temp 3)
1018 (throw 'syntax "Too many numbers in date")
1019 (if (or (< temp 2) (and year (= temp 2)))
1020 (throw 'syntax "Not enough numbers in date")
1021 (if (= temp 2) ; if year omitted, assume current year
1022 (setq year (math-this-year)))))
1023
1024 ;; A large number must be a year.
1025 (or year
1026 (if (and a (or (> a 31) (< a 1)))
1027 (setq year a a b b c c nil)
1028 (if (and b (or (> b 31) (< b 1)))
1029 (setq year b b c c nil)
1030 (if (and c (or (> c 31) (< c 1)))
1031 (setq year c c nil)))))
1032
1033 ;; A medium-large number must be a day.
1034 (if year
1035 (if (and a (> a 12))
1036 (setq day a a b b c c nil)
1037 (if (and b (> b 12))
1038 (setq day b b c c nil)
1039 (if (and c (> c 12))
1040 (setq day c c nil)))))
1041
1042 ;; We may know enough to sort it out now.
1043 (if (and year day)
1044 (or month (setq month a))
1045 (if (and year month)
1046 (setq day a)
1047
1048 ;; Interpret order of numbers as same as for display format.
1049 (setq temp calc-date-format)
1050 (while temp
1051 (cond ((not (symbolp (car temp))))
1052 ((memq (car temp) '(Y YY BY YYY YYYY))
1053 (or year (setq year a a b b c)))
1054 ((memq (car temp) '(M MM BM mmm Mmm Mmmm MMM MMMM))
1055 (or month (setq month a a b b c)))
1056 ((memq (car temp) '(D DD BD))
1057 (or day (setq day a a b b c))))
1058 (setq temp (cdr temp)))
1059
1060 ;; If display format was not complete, assume American style.
1061 (or month (setq month a a b b c))
1062 (or day (setq day a a b b c))
1063 (or year (setq year a a b b c))))
1064
1065 (if bc-flag
1066 (setq year (math-neg (math-abs year))))
1067
1068 (math-parse-date-validate year bigyear month day
bf77c646 1069 hour minute second)))))
136211a9
EZ
1070
1071(defun math-parse-date-validate (year bigyear month day hour minute second)
1072 (and (not bigyear) (natnump year) (< year 100)
1073 (setq year (+ year (if (< year 40) 2000 1900))))
1074 (if (eq year 0)
1075 (throw 'syntax "Year value is out of range"))
1076 (if (or (< month 1) (> month 12))
1077 (throw 'syntax "Month value is out of range"))
1078 (if (or (< day 1) (> day (math-days-in-month year month)))
1079 (throw 'syntax "Day value is out of range"))
1080 (and hour
1081 (progn
fd1f1603
JB
1082 (if (or (< hour 0)
1083 (> hour 24)
1084 (and (= hour 24)
1085 (not (= minute 0))
1086 (not (eq second 0))))
136211a9
EZ
1087 (throw 'syntax "Hour value is out of range"))
1088 (if (or (< minute 0) (> minute 59))
1089 (throw 'syntax "Minute value is out of range"))
1090 (if (or (math-negp second) (not (Math-lessp second 60)))
1091 (throw 'syntax "Seconds value is out of range"))))
1092 (list 'date (math-dt-to-date (append (list year month day)
bf77c646 1093 (and hour (list hour minute second))))))
136211a9 1094
682ceaf8
JB
1095(defun math-parse-iso-date-validate (isoyear isoweek isoweekday hour minute second)
1096 (if (or (< isoweek 1) (> isoweek 53))
1097 (throw 'syntax "Week value is out of range"))
ff3f6c26
JB
1098 (if (or (< isoweekday 1) (> isoweekday 7))
1099 (throw 'syntax "Weekday value is out of range"))
682ceaf8
JB
1100 (and hour
1101 (progn
fd1f1603
JB
1102 (if (or (< hour 0)
1103 (> hour 24)
1104 (and (= hour 24)
1105 (not (= minute 0))
1106 (not (eq second 0))))
682ceaf8
JB
1107 (throw 'syntax "Hour value is out of range"))
1108 (if (or (< minute 0) (> minute 59))
1109 (throw 'syntax "Minute value is out of range"))
1110 (if (or (math-negp second) (not (Math-lessp second 60)))
1111 (throw 'syntax "Seconds value is out of range"))))
1112 (list 'date (math-iso-dt-to-date (append (list isoyear isoweek isoweekday)
1113 (and hour (list hour minute second))))))
1114
136211a9
EZ
1115(defun math-parse-date-word (names &optional front)
1116 (let ((n 1))
1117 (while (and names (not (string-match (if (equal (car names) "Sep")
1118 "Sept?"
1119 (regexp-quote (car names)))
98223359 1120 math-pd-str)))
136211a9
EZ
1121 (setq names (cdr names)
1122 n (1+ n)))
1123 (and names
1124 (or (not front) (= (match-beginning 0) 0))
1125 (progn
98223359 1126 (setq math-pd-str (concat (substring math-pd-str 0 (match-beginning 0))
136211a9 1127 (if front "" " ")
98223359 1128 (substring math-pd-str (match-end 0))))
bf77c646 1129 n))))
136211a9 1130
98223359 1131(defun math-parse-standard-date (math-pd-str with-time)
136211a9
EZ
1132 (let ((case-fold-search t)
1133 (okay t) num
1134 (fmt calc-date-format) this next (gnext nil)
682ceaf8 1135 (isoyear nil) (isoweek nil) (isoweekday nil)
136211a9
EZ
1136 (year nil) (month nil) (day nil) (bigyear nil) (yearday nil)
1137 (hour nil) (minute nil) (second nil) (bc-flag nil))
1138 (while (and fmt okay)
1139 (setq this (car fmt)
1140 fmt (setq fmt (or (cdr fmt)
1141 (prog1
1142 gnext
1143 (setq gnext nil))))
1144 next (car fmt))
1145 (if (consp next) (setq next (car next)))
1146 (or (cond ((listp this)
1147 (or (not with-time)
1148 (not this)
1149 (setq gnext fmt
1150 fmt this)))
1151 ((stringp this)
98223359 1152 (if (and (<= (length this) (length math-pd-str))
136211a9 1153 (equal this
98223359
JB
1154 (substring math-pd-str 0 (length this))))
1155 (setq math-pd-str (substring math-pd-str (length this)))))
136211a9
EZ
1156 ((eq this 'X)
1157 t)
1158 ((memq this '(n N j J))
98223359
JB
1159 (and (string-match "\\`[-+]?[0-9.]+\\([eE][-+]?[0-9]+\\)?" math-pd-str)
1160 (setq num (math-match-substring math-pd-str 0)
1161 math-pd-str (substring math-pd-str (match-end 0))
136211a9
EZ
1162 num (math-date-to-dt (math-read-number num))
1163 num (math-sub num
1164 (if (memq this '(n N))
1165 0
1166 (if (or (eq this 'j)
1167 (math-integerp num))
98e9ffe3
JB
1168 math-julian-date-beginning-int
1169 math-julian-date-beginning)))
136211a9
EZ
1170 hour (or (nth 3 num) hour)
1171 minute (or (nth 4 num) minute)
1172 second (or (nth 5 num) second)
1173 year (car num)
1174 month (nth 1 num)
1175 day (nth 2 num))))
1176 ((eq this 'U)
98223359
JB
1177 (and (string-match "\\`[-+]?[0-9]+" math-pd-str)
1178 (setq num (math-match-substring math-pd-str 0)
1179 math-pd-str (substring math-pd-str (match-end 0))
136211a9
EZ
1180 num (math-date-to-dt
1181 (math-add 719164
1182 (math-div (math-read-number num)
1183 '(float 864 2))))
1184 hour (nth 3 num)
1185 minute (nth 4 num)
1186 second (nth 5 num)
1187 year (car num)
1188 month (nth 1 num)
1189 day (nth 2 num))))
1190 ((memq this '(mmm Mmm MMM))
1191 (setq month (math-parse-date-word math-short-month-names t)))
1192 ((memq this '(Mmmm MMMM))
1193 (setq month (math-parse-date-word math-long-month-names t)))
1194 ((memq this '(www Www WWW))
1195 (math-parse-date-word math-short-weekday-names t))
1196 ((memq this '(Wwww WWWW))
1197 (math-parse-date-word math-long-weekday-names t))
1198 ((memq this '(p P))
98223359 1199 (if (string-match "\\`a" math-pd-str)
136211a9 1200 (setq hour (if (= hour 12) 0 hour)
98223359
JB
1201 math-pd-str (substring math-pd-str 1))
1202 (if (string-match "\\`p" math-pd-str)
136211a9 1203 (setq hour (if (= hour 12) 12 (% (+ hour 12) 24))
98223359 1204 math-pd-str (substring math-pd-str 1)))))
136211a9 1205 ((memq this '(pp PP pppp PPPP))
98223359 1206 (if (string-match "\\`am\\|a\\.m\\." math-pd-str)
136211a9 1207 (setq hour (if (= hour 12) 0 hour)
98223359
JB
1208 math-pd-str (substring math-pd-str (match-end 0)))
1209 (if (string-match "\\`pm\\|p\\.m\\." math-pd-str)
136211a9 1210 (setq hour (if (= hour 12) 12 (% (+ hour 12) 24))
98223359 1211 math-pd-str (substring math-pd-str (match-end 0))))))
682ceaf8 1212 ((memq this '(Y YY BY YYY YYYY ZYYY))
136211a9
EZ
1213 (and (if (memq next '(MM DD ddd hh HH mm ss SS))
1214 (if (memq this '(Y YY BYY))
98223359
JB
1215 (string-match "\\` *[0-9][0-9]" math-pd-str)
1216 (string-match "\\`[0-9][0-9][0-9][0-9]" math-pd-str))
1217 (string-match "\\`[-+]?[0-9]+" math-pd-str))
1218 (setq year (math-match-substring math-pd-str 0)
682ceaf8 1219 bigyear (or (eq this 'YYY)
98223359
JB
1220 (memq (aref math-pd-str 0) '(?\+ ?\-)))
1221 math-pd-str (substring math-pd-str (match-end 0))
682ceaf8
JB
1222 year (math-read-number year))
1223 (if (and (eq this 'ZYYY) (eq year 0))
1224 (setq year (math-sub year 1)
1225 bigyear t)
1226 t)))
1227 ((eq this 'IYYY)
1228 (if (string-match "\\`[-+]?[0-9]+" math-pd-str)
1229 (setq isoyear (string-to-number (math-match-substring math-pd-str 0))
1230 math-pd-str (substring math-pd-str (match-end 0)))))
1231 ((eq this 'Iww)
1232 (if (string-match "W\\([0-9][0-9]\\)" math-pd-str)
1233 (setq isoweek (string-to-number (math-match-substring math-pd-str 1))
1234 math-pd-str (substring math-pd-str 3))))
136211a9
EZ
1235 ((eq this 'b)
1236 t)
682ceaf8
JB
1237 ((eq this 'T)
1238 (if (eq (aref math-pd-str 0) ?T)
1239 (setq math-pd-str (substring math-pd-str 1))
1240 t))
136211a9 1241 ((memq this '(aa AA aaaa AAAA))
98223359
JB
1242 (if (string-match "\\` *\\(ad\\|a\\.d\\.\\)" math-pd-str)
1243 (setq math-pd-str (substring math-pd-str (match-end 0)))))
136211a9 1244 ((memq this '(aaa AAA))
98223359
JB
1245 (if (string-match "\\` *ad *" math-pd-str)
1246 (setq math-pd-str (substring math-pd-str (match-end 0)))))
136211a9 1247 ((memq this '(bb BB bbb BBB bbbb BBBB))
98223359
JB
1248 (if (string-match "\\` *\\(bc\\|b\\.c\\.\\)" math-pd-str)
1249 (setq math-pd-str (substring math-pd-str (match-end 0))
136211a9
EZ
1250 bc-flag t)))
1251 ((memq this '(s ss bs SS BS))
1252 (and (if (memq next '(YY YYYY MM DD hh HH mm))
98223359
JB
1253 (string-match "\\` *[0-9][0-9]\\(\\.[0-9]+\\)?" math-pd-str)
1254 (string-match "\\` *[0-9][0-9]?\\(\\.[0-9]+\\)?" math-pd-str))
1255 (setq second (math-match-substring math-pd-str 0)
1256 math-pd-str (substring math-pd-str (match-end 0))
136211a9
EZ
1257 second (math-read-number second))))
1258 ((eq this 'C)
98223359
JB
1259 (if (string-match "\\`:[0-9][0-9]" math-pd-str)
1260 (setq math-pd-str (substring math-pd-str 1))
136211a9
EZ
1261 t))
1262 ((or (not (if (and (memq this '(ddd MM DD hh HH mm))
1263 (memq next '(YY YYYY MM DD ddd
1264 hh HH mm ss SS)))
1265 (if (eq this 'ddd)
98223359
JB
1266 (string-match "\\` *[0-9][0-9][0-9]" math-pd-str)
1267 (string-match "\\` *[0-9][0-9]" math-pd-str))
1268 (string-match "\\` *[0-9]+" math-pd-str)))
5574e209 1269 (and (setq num (string-to-number
98223359
JB
1270 (math-match-substring math-pd-str 0))
1271 math-pd-str (substring math-pd-str (match-end 0)))
136211a9
EZ
1272 nil))
1273 nil)
1274 ((eq this 'W)
682ceaf8
JB
1275 (and (>= num 0) (< num 7)))
1276 ((eq this 'w)
1277 (setq isoweekday num))
136211a9
EZ
1278 ((memq this '(d ddd bdd))
1279 (setq yearday num))
1280 ((memq this '(M MM BM))
1281 (setq month num))
1282 ((memq this '(D DD BD))
1283 (setq day num))
1284 ((memq this '(h hh bh H HH BH))
1285 (setq hour num))
1286 ((memq this '(m mm bm))
1287 (setq minute num)))
1288 (setq okay nil)))
1289 (if yearday
1290 (if (and month day)
1291 (setq yearday nil)
1292 (setq month 1 day 1)))
98223359 1293 (if (and okay (equal math-pd-str ""))
682ceaf8
JB
1294 (if isoyear
1295 (math-parse-iso-date-validate isoyear isoweek isoweekday hour minute second)
1296 (and month day (or (not (or hour minute second))
1297 (and hour minute))
1298 (progn
1299 (or year (setq year (math-this-year)))
1300 (or second (setq second 0))
1301 (if bc-flag
1302 (setq year (math-neg (math-abs year))))
1303 (setq day (math-parse-date-validate year bigyear month day
1304 hour minute second))
1305 (if yearday
1306 (setq day (math-add day (1- yearday))))
1307 day))))))
136211a9 1308
ff3f6c26
JB
1309(defun math-parse-iso-date (math-pd-str)
1310 "Parse MATH-PD-STR as an ISO week date, or return nil."
1311 (let ((case-fold-search t)
1312 (isoyear nil) (isoweek nil) (isoweekday nil)
1313 (hour nil) (minute nil) (second nil))
1314 ;; Extract the time, if any.
1315 (if (string-match "T[^0-9]*\\([0-9][0-9]\\)[^0-9]*\\([0-9][0-9]\\)?[^0-9]*\\([0-9][0-9]\\(\\.[0-9]+\\)?\\)?" math-pd-str)
1316 (progn
1317 (setq hour (string-to-number (math-match-substring math-pd-str 1))
1318 minute (math-match-substring math-pd-str 2)
1319 second (math-match-substring math-pd-str 3)
1320 math-pd-str (substring math-pd-str 0 (match-beginning 0)))
1321 (if (equal minute "")
1322 (setq minute 0)
1323 (setq minute (string-to-number minute)))
1324 (if (equal second "")
1325 (setq second 0)
1326 (setq second (math-read-number second)))))
1327 ;; Next, the year, week and weekday
1328 (if (string-match "\\(-?[0-9]*\\)[^0-9]*W\\([0-9][0-9]\\)[^0-9]*\\([0-9]\\)[^0-9]*\\'" math-pd-str)
1329 (progn
1330 (setq isoyear (string-to-number (math-match-substring math-pd-str 1))
1331 isoweek (string-to-number (math-match-substring math-pd-str 2))
1332 isoweekday (string-to-number (math-match-substring math-pd-str 3)))
1333 (math-parse-iso-date-validate isoyear isoweek isoweekday hour minute second)))))
136211a9
EZ
1334
1335(defun calcFunc-now (&optional zone)
1336 (let ((date (let ((calc-date-format nil))
1337 (math-parse-date (current-time-string)))))
1338 (if (consp date)
1339 (if zone
1340 (math-add date (math-div (math-sub (calcFunc-tzone nil date)
1341 (calcFunc-tzone zone date))
1342 '(float 864 2)))
1343 date)
1344 (calc-record-why "*Unable to interpret current date from system")
bf77c646 1345 (append (list 'calcFunc-now) (and zone (list zone))))))
136211a9
EZ
1346
1347(defun calcFunc-year (date)
bf77c646 1348 (car (math-date-to-dt date)))
136211a9
EZ
1349
1350(defun calcFunc-month (date)
bf77c646 1351 (nth 1 (math-date-to-dt date)))
136211a9
EZ
1352
1353(defun calcFunc-day (date)
bf77c646 1354 (nth 2 (math-date-to-dt date)))
136211a9
EZ
1355
1356(defun calcFunc-weekday (date)
1357 (if (eq (car-safe date) 'date)
1358 (setq date (nth 1 date)))
1359 (or (math-realp date)
1360 (math-reject-arg date 'datep))
c9f618db 1361 (math-mod (math-floor date) 7))
136211a9
EZ
1362
1363(defun calcFunc-yearday (date)
1364 (let ((dt (math-date-to-dt date)))
bf77c646 1365 (math-day-number (car dt) (nth 1 dt) (nth 2 dt))))
136211a9
EZ
1366
1367(defun calcFunc-hour (date)
1368 (if (eq (car-safe date) 'hms)
1369 (nth 1 date)
bf77c646 1370 (or (nth 3 (math-date-to-dt date)) 0)))
136211a9
EZ
1371
1372(defun calcFunc-minute (date)
1373 (if (eq (car-safe date) 'hms)
1374 (nth 2 date)
bf77c646 1375 (or (nth 4 (math-date-to-dt date)) 0)))
136211a9
EZ
1376
1377(defun calcFunc-second (date)
1378 (if (eq (car-safe date) 'hms)
1379 (nth 3 date)
bf77c646 1380 (or (nth 5 (math-date-to-dt date)) 0)))
136211a9
EZ
1381
1382(defun calcFunc-time (date)
1383 (let ((dt (math-date-to-dt date)))
1384 (if (nth 3 dt)
1385 (cons 'hms (nthcdr 3 dt))
bf77c646 1386 (list 'hms 0 0 0))))
136211a9
EZ
1387
1388(defun calcFunc-date (date &optional month day hour minute second)
1389 (and (math-messy-integerp month) (setq month (math-trunc month)))
1390 (and month (not (integerp month)) (math-reject-arg month 'fixnump))
1391 (and (math-messy-integerp day) (setq day (math-trunc day)))
1392 (and day (not (integerp day)) (math-reject-arg day 'fixnump))
1393 (if (and (eq (car-safe hour) 'hms) (not minute))
1394 (setq second (nth 3 hour)
1395 minute (nth 2 hour)
1396 hour (nth 1 hour)))
1397 (and (math-messy-integerp hour) (setq hour (math-trunc hour)))
1398 (and hour (not (integerp hour)) (math-reject-arg hour 'fixnump))
1399 (and (math-messy-integerp minute) (setq minute (math-trunc minute)))
1400 (and minute (not (integerp minute)) (math-reject-arg minute 'fixnump))
1401 (and (math-messy-integerp second) (setq second (math-trunc second)))
1402 (and second (not (math-realp second)) (math-reject-arg second 'realp))
1403 (if month
1404 (progn
1405 (and (math-messy-integerp date) (setq date (math-trunc date)))
1406 (and date (not (math-integerp date)) (math-reject-arg date 'integerp))
1407 (if day
1408 (if hour
1409 (list 'date (math-dt-to-date (list date month day hour
1410 (or minute 0)
1411 (or second 0))))
1412 (list 'date (math-dt-to-date (list date month day))))
1413 (list 'date (math-dt-to-date (list (math-this-year) date month)))))
1414 (if (math-realp date)
1415 (list 'date date)
1416 (if (eq (car date) 'date)
1417 (nth 1 date)
bf77c646 1418 (math-reject-arg date 'datep)))))
136211a9
EZ
1419
1420(defun calcFunc-julian (date &optional zone)
1421 (if (math-realp date)
1422 (list 'date (if (math-integerp date)
98e9ffe3
JB
1423 (math-sub date math-julian-date-beginning-int)
1424 (setq date (math-sub date math-julian-date-beginning))
136211a9
EZ
1425 (math-sub date (math-div (calcFunc-tzone zone date)
1426 '(float 864 2)))))
1427 (if (eq (car date) 'date)
1428 (math-add (nth 1 date) (if (math-integerp (nth 1 date))
98e9ffe3
JB
1429 math-julian-date-beginning-int
1430 (math-add math-julian-date-beginning
136211a9
EZ
1431 (math-div (calcFunc-tzone zone date)
1432 '(float 864 2)))))
bf77c646 1433 (math-reject-arg date 'datep))))
136211a9
EZ
1434
1435(defun calcFunc-unixtime (date &optional zone)
1436 (if (math-realp date)
1437 (progn
1438 (setq date (math-add 719164 (math-div date '(float 864 2))))
1439 (list 'date (math-sub date (math-div (calcFunc-tzone zone date)
1440 '(float 864 2)))))
1441 (if (eq (car date) 'date)
1442 (math-add (nth 1 (math-date-parts (nth 1 date) 719164))
1443 (calcFunc-tzone zone date))
bf77c646 1444 (math-reject-arg date 'datep))))
136211a9 1445
98223359
JB
1446
1447;;; Note: Longer names must appear before shorter names which are
1448;;; substrings of them.
1449(defvar math-tzone-names
d55ebb63 1450 '(( "UTC" 0 0)
98223359
JB
1451 ( "MEGT" -1 "MET" "METDST" ) ; Middle Europe
1452 ( "METDST" -1 -1 ) ( "MET" -1 0 )
1453 ( "MEGZ" -1 "MEZ" "MESZ" ) ( "MEZ" -1 0 ) ( "MESZ" -1 -1 )
1454 ( "WEGT" 0 "WET" "WETDST" ) ; Western Europe
1455 ( "WETDST" 0 -1 ) ( "WET" 0 0 )
1456 ( "BGT" 0 "GMT" "BST" ) ( "GMT" 0 0 ) ( "BST" 0 -1 ) ; Britain
1457 ( "NGT" (float 35 -1) "NST" "NDT" ) ; Newfoundland
1458 ( "NST" (float 35 -1) 0 ) ( "NDT" (float 35 -1) -1 )
1459 ( "AGT" 4 "AST" "ADT" ) ( "AST" 4 0 ) ( "ADT" 4 -1 ) ; Atlantic
1460 ( "EGT" 5 "EST" "EDT" ) ( "EST" 5 0 ) ( "EDT" 5 -1 ) ; Eastern
1461 ( "CGT" 6 "CST" "CDT" ) ( "CST" 6 0 ) ( "CDT" 6 -1 ) ; Central
1462 ( "MGT" 7 "MST" "MDT" ) ( "MST" 7 0 ) ( "MDT" 7 -1 ) ; Mountain
1463 ( "PGT" 8 "PST" "PDT" ) ( "PST" 8 0 ) ( "PDT" 8 -1 ) ; Pacific
1464 ( "YGT" 9 "YST" "YDT" ) ( "YST" 9 0 ) ( "YDT" 9 -1 ) ; Yukon
1465 )
1466 "No doc yet. See calc manual for now. ")
1467
94a95495
JB
1468(defvar var-TimeZone nil)
1469
1470;; From cal-dst
1471(defvar calendar-current-time-zone-cache)
1472
1473(defvar math-calendar-tzinfo
1474 nil
1475 "Information about the timezone, retrieved from the calendar.")
1476
1477(defun math-get-calendar-tzinfo ()
1478 "Get information about the timezone from the calendar.
1479The result should be a list of two items about the current time zone:
1480first, the number of seconds difference from GMT
1481second, the number of seconds offset for daylight savings."
1482 (if math-calendar-tzinfo
1483 math-calendar-tzinfo
1484 (require 'cal-dst)
1485 (let ((tzinfo (progn
1486 (calendar-current-time-zone)
1487 calendar-current-time-zone-cache)))
1488 (setq math-calendar-tzinfo
1489 (list (* 60 (abs (nth 0 tzinfo)))
1490 (* 60 (nth 1 tzinfo)))))))
98223359 1491
136211a9
EZ
1492(defun calcFunc-tzone (&optional zone date)
1493 (if zone
1494 (cond ((math-realp zone)
1495 (math-round (math-mul zone 3600)))
1496 ((eq (car zone) 'hms)
1497 (math-round (math-mul (math-from-hms zone 'deg) 3600)))
1498 ((eq (car zone) '+)
1499 (math-add (calcFunc-tzone (nth 1 zone) date)
1500 (calcFunc-tzone (nth 2 zone) date)))
1501 ((eq (car zone) '-)
1502 (math-sub (calcFunc-tzone (nth 1 zone) date)
1503 (calcFunc-tzone (nth 2 zone) date)))
1504 ((eq (car zone) 'var)
1505 (let ((name (upcase (symbol-name (nth 1 zone))))
1506 found)
1507 (if (setq found (assoc name math-tzone-names))
1508 (calcFunc-tzone (math-add (nth 1 found)
1509 (if (integerp (nth 2 found))
1510 (nth 2 found)
1511 (or
1512 (math-daylight-savings-adjust
1513 date (car found))
1514 0)))
1515 date)
1516 (if (equal name "LOCAL")
1517 (calcFunc-tzone nil date)
1518 (math-reject-arg zone "*Unrecognized time zone name")))))
1519 (t (math-reject-arg zone "*Expected a time zone")))
1520 (if (calc-var-value 'var-TimeZone)
1521 (calcFunc-tzone (calc-var-value 'var-TimeZone) date)
94a95495
JB
1522 (let ((tzinfo (math-get-calendar-tzinfo)))
1523 (+ (nth 0 tzinfo)
1524 (* (math-cal-daylight-savings-adjust date) (nth 1 tzinfo)))))))
136211a9 1525
98223359 1526(defvar math-daylight-savings-hook 'math-std-daylight-savings)
136211a9
EZ
1527
1528(defun math-daylight-savings-adjust (date zone &optional dt)
1529 (or date (setq date (nth 1 (calcFunc-now))))
1530 (let (bump)
1531 (if (eq (car-safe date) 'date)
1532 (setq bump 0
1533 date (nth 1 date))
1534 (if (and date (math-realp date))
1535 (let ((zadj (assoc zone math-tzone-names)))
1536 (if zadj (setq bump -1
1537 date (math-sub date (math-div (nth 1 zadj)
1538 '(float 24 0))))))
1539 (math-reject-arg date 'datep)))
1540 (setq date (math-float date))
1541 (or dt (setq dt (math-date-to-dt date)))
1542 (and math-daylight-savings-hook
bf77c646 1543 (funcall math-daylight-savings-hook date dt zone bump))))
136211a9 1544
94a95495
JB
1545;;; Based on part of dst-adjust-time in cal-dst.el
1546;;; For calcFunc-dst, when zone=nil
1547(defun math-cal-daylight-savings-adjust (date)
1548 "Return -1 if DATE is using daylight saving, 0 otherwise."
1549 (require 'cal-dst)
1550 (unless date (setq date (calcFunc-now)))
1551 (let* ((dt (math-date-to-dt date))
1552 (time (cond
1553 ((nth 3 dt)
1554 (nth 3 dt))
1555 ((nth 4 dt)
1556 (+ (nth 3 dt) (/ (nth 4 dt) 60.0)))
1557 (t
1558 0)))
1559 (rounded-abs-date
1560 (+
c9f618db 1561 (calendar-absolute-from-gregorian
94a95495
JB
1562 (list (nth 1 dt) (nth 2 dt) (nth 0 dt)))
1563 (/ (round (* 60 time)) 60.0 24.0))))
1564 (if (dst-in-effect rounded-abs-date)
1565 -1
1566 0)))
1567
136211a9
EZ
1568(defun calcFunc-dsadj (date &optional zone)
1569 (if zone
1570 (or (eq (car-safe zone) 'var)
1571 (math-reject-arg zone "*Time zone variable expected"))
94a95495
JB
1572 (setq zone (calc-var-value 'var-TimeZone)))
1573 (if zone
1574 (progn
1575 (setq zone (and (eq (car-safe zone) 'var)
1576 (upcase (symbol-name (nth 1 zone)))))
1577 (let ((zadj (assoc zone math-tzone-names)))
1578 (or zadj (math-reject-arg zone "*Unrecognized time zone name"))
1579 (if (integerp (nth 2 zadj))
1580 (nth 2 zadj)
1581 (math-daylight-savings-adjust date zone))))
1582 (math-cal-daylight-savings-adjust date)))
1583
1584;; (defun calcFunc-dsadj (date &optional zone)
1585;; (if zone
1586;; (or (eq (car-safe zone) 'var)
1587;; (math-reject-arg zone "*Time zone variable expected"))
1588;; (setq zone (or (calc-var-value 'var-TimeZone)
1589;; (progn
1590;; (calcFunc-tzone)
1591;; (calc-var-value 'var-TimeZone)))))
1592;; (setq zone (and (eq (car-safe zone) 'var)
1593;; (upcase (symbol-name (nth 1 zone)))))
1594;; (let ((zadj (assoc zone math-tzone-names)))
1595;; (or zadj (math-reject-arg zone "*Unrecognized time zone name"))
1596;; (if (integerp (nth 2 zadj))
1597;; (nth 2 zadj)
1598;; (math-daylight-savings-adjust date zone))))
136211a9
EZ
1599
1600(defun calcFunc-tzconv (date z1 z2)
1601 (if (math-realp date)
1602 (nth 1 (calcFunc-tzconv (list 'date date) z1 z2))
bf77c646 1603 (calcFunc-unixtime (calcFunc-unixtime date z1) z2)))
136211a9 1604
136211a9 1605(defun math-std-daylight-savings (date dt zone bump)
16e5f961 1606 "Standard North American daylight saving algorithm.
96cd475f 1607Before 2007, this uses `math-std-daylight-savings-old', where
d55ebb63 1608daylight saving began on the first Sunday of April at 2 a.m.,
96cd475f
JB
1609and ended on the last Sunday of October at 2 a.m.
1610As of 2007, this uses `math-std-daylight-savings-new', where
1611daylight saving begins on the second Sunday of March at 2 a.m.,
1612and ends on the first Sunday of November at 2 a.m."
1613 (if (< (car dt) 2007)
1614 (math-std-daylight-savings-old date dt zone bump)
1615 (math-std-daylight-savings-new date dt zone bump)))
1616
1617(defun math-std-daylight-savings-new (date dt zone bump)
1618 "Standard North American daylight saving algorithm as of 2007.
1619This implements the rules for the U.S. and Canada.
16e5f961
CY
1620Daylight saving begins on the second Sunday of March at 2 a.m.,
1621and ends on the first Sunday of November at 2 a.m."
1622 (cond ((< (nth 1 dt) 3) 0)
1623 ((= (nth 1 dt) 3)
1624 (let ((sunday (math-prev-weekday-in-month date dt 14 0)))
136211a9
EZ
1625 (cond ((< (nth 2 dt) sunday) 0)
1626 ((= (nth 2 dt) sunday)
1627 (if (>= (nth 3 dt) (+ 3 bump)) -1 0))
1628 (t -1))))
16e5f961
CY
1629 ((< (nth 1 dt) 11) -1)
1630 ((= (nth 1 dt) 11)
1631 (let ((sunday (math-prev-weekday-in-month date dt 7 0)))
136211a9
EZ
1632 (cond ((< (nth 2 dt) sunday) -1)
1633 ((= (nth 2 dt) sunday)
1634 (if (>= (nth 3 dt) (+ 2 bump)) 0 -1))
1635 (t 0))))
bf77c646 1636 (t 0)))
136211a9 1637
96cd475f 1638(defun math-std-daylight-savings-old (date dt zone bump)
d55ebb63 1639 "Standard North American daylight saving algorithm before 2007.
96cd475f 1640This implements the rules for the U.S. and Canada.
d55ebb63 1641Daylight saving begins on the first Sunday of April at 2 a.m.,
96cd475f
JB
1642and ends on the last Sunday of October at 2 a.m."
1643 (cond ((< (nth 1 dt) 4) 0)
1644 ((= (nth 1 dt) 4)
1645 (let ((sunday (math-prev-weekday-in-month date dt 7 0)))
1646 (cond ((< (nth 2 dt) sunday) 0)
1647 ((= (nth 2 dt) sunday)
1648 (if (>= (nth 3 dt) (+ 3 bump)) -1 0))
1649 (t -1))))
1650 ((< (nth 1 dt) 10) -1)
1651 ((= (nth 1 dt) 10)
1652 (let ((sunday (math-prev-weekday-in-month date dt 31 0)))
1653 (cond ((< (nth 2 dt) sunday) -1)
1654 ((= (nth 2 dt) sunday)
1655 (if (>= (nth 3 dt) (+ 2 bump)) 0 -1))
1656 (t 0))))
1657 (t 0)))
1658
136211a9
EZ
1659;;; Compute the day (1-31) of the WDAY (0-6) on or preceding the given
1660;;; day of the given month.
1661(defun math-prev-weekday-in-month (date dt day wday)
1662 (or day (setq day (nth 2 dt)))
1663 (if (> day (math-days-in-month (car dt) (nth 1 dt)))
1664 (setq day (math-days-in-month (car dt) (nth 1 dt))))
1665 (let ((zeroth (math-sub (math-floor date) (nth 2 dt))))
bf77c646 1666 (math-sub (nth 1 (calcFunc-newweek (math-add zeroth day))) zeroth)))
136211a9
EZ
1667
1668(defun calcFunc-pwday (date &optional day weekday)
1669 (if (eq (car-safe date) 'date)
1670 (setq date (nth 1 date)))
1671 (or (math-realp date)
1672 (math-reject-arg date 'datep))
1673 (if (math-messy-integerp day) (setq day (math-trunc day)))
1674 (or (integerp day) (math-reject-arg day 'fixnump))
1675 (if (= day 0) (setq day 31))
1676 (and (or (< day 7) (> day 31)) (math-reject-arg day 'range))
bf77c646 1677 (math-prev-weekday-in-month date (math-date-to-dt date) day (or weekday 0)))
136211a9
EZ
1678
1679
1680(defun calcFunc-newweek (date &optional weekday)
1681 (if (eq (car-safe date) 'date)
1682 (setq date (nth 1 date)))
1683 (or (math-realp date)
1684 (math-reject-arg date 'datep))
1685 (or weekday (setq weekday 0))
1686 (and (math-messy-integerp weekday) (setq weekday (math-trunc weekday)))
1687 (or (integerp weekday) (math-reject-arg weekday 'fixnump))
1688 (and (or (< weekday 0) (> weekday 6)) (math-reject-arg weekday 'range))
1689 (setq date (math-floor date))
bf77c646 1690 (list 'date (math-sub date (calcFunc-weekday (math-sub date weekday)))))
136211a9
EZ
1691
1692(defun calcFunc-newmonth (date &optional day)
1693 (or day (setq day 1))
1694 (and (math-messy-integerp day) (setq day (math-trunc day)))
1695 (or (integerp day) (math-reject-arg day 'fixnump))
1696 (and (or (< day 0) (> day 31)) (math-reject-arg day 'range))
c9f618db
JB
1697 (let* ((dt (math-date-to-dt date))
1698 (dim (math-days-in-month (car dt) (nth 1 dt)))
1699 (julian (if calc-gregorian-switch
1700 (math-date-to-dt (math-sub
1701 (or (nth 3 calc-gregorian-switch)
1702 (apply 'math-absolute-from-gregorian-dt calc-gregorian-switch))
1703 1)))))
1704 (if (or (= day 0) (> day dim))
1705 (setq day (1- dim))
1706 (setq day (1- day)))
1707 ;; Adjust if this occurs near the switch to the Gregorian calendar
1708 (if calc-gregorian-switch
1709 (cond
1710 ((and (math-dt-before-p (list (car dt) (nth 1 dt) 1) calc-gregorian-switch)
1711 (math-dt-before-p julian (list (car dt) (nth 1 dt) 1)))
1712 ;; In this case, CALC-GREGORIAN-SWITCH is the first day of the month
1713 (list 'date
1714 (math-dt-to-date (list (car calc-gregorian-switch)
1715 (nth 1 calc-gregorian-switch)
1716 (if (> (+ (nth 2 calc-gregorian-switch) day) dim)
1717 dim
1718 (+ (nth 2 calc-gregorian-switch) day))))))
1719 ((and (eq (car dt) (car calc-gregorian-switch))
1720 (= (nth 1 dt) (nth 1 calc-gregorian-switch)))
1721 ;; In this case, the switch to the Gregorian calendar occurs in the given month
1722 (if (< (+ (nth 2 julian) day) (nth 2 calc-gregorian-switch))
1723 ;; If the DAYth day occurs before the switch, use it
1724 (list 'date (math-dt-to-date (list (car dt) (nth 1 dt) (1+ day))))
1725 ;; Otherwise do some computations
1726 (let ((tm (+ day (- (nth 2 calc-gregorian-switch) (nth 2 julian)))))
1727 (list 'date (math-dt-to-date
1728 (list (car dt)
1729 (nth 1 dt)
1730 ;;
1731 (if (> tm dim) dim tm)))))))
1732 ((and (eq (car dt) (car julian))
1733 (= (nth 1 dt) (nth 1 julian)))
1734 ;; In this case, the current month is truncated because of the switch
1735 ;; to the Gregorian calendar
1736 (list 'date (math-dt-to-date
1737 (list (car dt)
1738 (nth 1 dt)
1739 (if (>= day (nth 2 julian))
1740 (nth 2 julian)
1741 (1+ day))))))
1742 (t
1743 ;; The default
1744 (list 'date (math-add (math-dt-to-date (list (car dt) (nth 1 dt) 1)) day))))
1745 (list 'date (math-add (math-dt-to-date (list (car dt) (nth 1 dt) 1)) day)))))
136211a9
EZ
1746
1747(defun calcFunc-newyear (date &optional day)
c9f618db 1748 (if (eq (car-safe date) 'date) (setq date (nth 1 date)))
136211a9
EZ
1749 (or day (setq day 1))
1750 (and (math-messy-integerp day) (setq day (math-trunc day)))
1751 (or (integerp day) (math-reject-arg day 'fixnump))
c9f618db
JB
1752 (let* ((dt (math-date-to-dt date))
1753 (gregbeg (if calc-gregorian-switch
1754 (or (nth 3 calc-gregorian-switch)
1755 (apply 'math-absolute-from-gregorian-dt calc-gregorian-switch))))
1756 (julianend (if calc-gregorian-switch (math-sub gregbeg 1)))
1757 (julian (if calc-gregorian-switch
1758 (math-date-to-dt julianend))))
136211a9 1759 (if (and (>= day 0) (<= day 366))
c9f618db 1760 (let ((max (if (math-leap-year-p (car dt)) 366 365)))
136211a9 1761 (if (or (= day 0) (> day max)) (setq day max))
c9f618db
JB
1762 (if calc-gregorian-switch
1763 ;; Now to break this down into cases
1764 (cond
1765 ((and (math-dt-before-p (list (car dt) 1 1) calc-gregorian-switch)
1766 (math-dt-before-p julian (list (car dt) 1 1)))
1767 ;; In this case, CALC-GREGORIAN-SWITCH is the first day of the year
1768 (list 'date (math-min (math-add gregbeg (1- day))
1769 (math-dt-to-date (list (car calc-gregorian-switch) 12 31)))))
1770 ((eq (car dt) (car julian))
1771 ;; In this case, the switch to the Gregorian calendar occurs in the given year
1772 (if (Math-lessp (car julian) (car calc-gregorian-switch))
1773 ;; Here, the last Julian day is the last day of the year.
1774 (list 'date (math-min (math-add (math-dt-to-date (list (car dt) 1 1)) (1- day))
1775 julianend))
1776 ;; Otherwise, just make sure the date doesn't go past the end of the year
1777 (list 'date (math-min (math-add (math-dt-to-date (list (car dt) 1 1)) (1- day))
1778 (math-dt-to-date (list (car dt) 12 31))))))
1779 (t
1780 (list 'date (math-add (math-dt-to-date (list (car dt) 1 1))
1781 (1- day)))))
1782 (list 'date (math-add (math-dt-to-date (list (car dt) 1 1))
1783 (1- day)))))
136211a9 1784 (if (and (>= day -12) (<= day -1))
c9f618db
JB
1785 (if (and calc-gregorian-switch
1786 (math-dt-before-p (list (car dt) (- day) 1) calc-gregorian-switch)
1787 (math-dt-before-p julian (list (car dt) (- day) 1)))
1788 (list 'date gregbeg)
1789 (list 'date (math-dt-to-date (list (car dt) (- day) 1))))
1790 (math-reject-arg day 'range)))))
136211a9
EZ
1791
1792(defun calcFunc-incmonth (date &optional step)
1793 (or step (setq step 1))
1794 (and (math-messy-integerp step) (setq step (math-trunc step)))
1795 (or (math-integerp step) (math-reject-arg step 'integerp))
1796 (let* ((dt (math-date-to-dt date))
1797 (year (car dt))
1798 (month (math-add (1- (nth 1 dt)) step))
1799 (extra (calcFunc-idiv month 12))
1800 (day (nth 2 dt)))
1801 (setq month (1+ (math-sub month (math-mul extra 12)))
1802 year (math-add year extra)
1803 day (min day (math-days-in-month year month)))
1804 (and (math-posp (car dt)) (not (math-posp year))
1805 (setq year (math-sub year 1))) ; did we go past the year zero?
1806 (and (math-negp (car dt)) (not (math-negp year))
1807 (setq year (math-add year 1)))
1808 (list 'date (math-dt-to-date
bf77c646 1809 (cons year (cons month (cons day (cdr (cdr (cdr dt))))))))))
136211a9
EZ
1810
1811(defun calcFunc-incyear (date &optional step)
bf77c646 1812 (calcFunc-incmonth date (math-mul (or step 1) 12)))
136211a9
EZ
1813
1814
1815
1816(defun calcFunc-bsub (a b)
1817 (or (eq (car-safe a) 'date)
1818 (math-reject-arg a 'datep))
1819 (if (eq (car-safe b) 'date)
1820 (if (math-lessp (nth 1 a) (nth 1 b))
1821 (math-neg (calcFunc-bsub b a))
1822 (math-setup-holidays b)
1823 (let* ((da (math-to-business-day a))
1824 (db (math-to-business-day b)))
1825 (math-add (math-sub (car da) (car db))
1826 (if (and (cdr db) (not (cdr da))) 1 0))))
bf77c646 1827 (calcFunc-badd a (math-neg b))))
136211a9 1828
3132f345
CW
1829(defvar math-holidays-cache nil)
1830(defvar math-holidays-cache-tag t)
136211a9
EZ
1831(defun calcFunc-badd (a b)
1832 (if (eq (car-safe b) 'date)
1833 (if (eq (car-safe a) 'date)
25f72ec0 1834 (math-reject-arg nil "*Invalid combination in date arithmetic")
136211a9
EZ
1835 (calcFunc-badd b a))
1836 (if (eq (car-safe a) 'date)
1837 (if (Math-realp b)
1838 (if (Math-zerop b)
1839 a
1840 (let* ((d (math-to-business-day a))
1841 (bb (math-add (car d)
1842 (if (and (cdr d) (Math-posp b))
1843 (math-sub b 1) b))))
1844 (or (math-from-business-day bb)
1845 (calcFunc-badd a b))))
1846 (if (eq (car-safe b) 'hms)
1847 (let ((hours (nth 7 math-holidays-cache)))
1848 (setq b (math-div (math-from-hms b 'deg) 24))
1849 (if hours
1850 (setq b (math-div b (cdr hours))))
1851 (calcFunc-badd a b))
25f72ec0 1852 (math-reject-arg nil "*Invalid combination in date arithmetic")))
bf77c646 1853 (math-reject-arg a 'datep))))
136211a9
EZ
1854
1855(defun calcFunc-holiday (a)
bf77c646 1856 (if (cdr (math-to-business-day a)) 1 0))
136211a9 1857
136211a9
EZ
1858;;; Compute the number of business days since Jan 1, 1 AD.
1859
1860(defun math-to-business-day (date &optional need-year)
1861 (if (eq (car-safe date) 'date)
1862 (setq date (nth 1 date)))
1863 (or (Math-realp date)
1864 (math-reject-arg date 'datep))
1865 (let* ((day (math-floor date))
1866 (time (math-sub date day))
1867 (dt (math-date-to-dt day))
1868 (delta 0)
1869 (holiday nil))
1870 (or (not need-year) (eq (car dt) need-year)
1871 (math-reject-arg (list 'date day) "*Generated holiday has wrong year"))
1872 (math-setup-holidays date)
1873 (let ((days (car math-holidays-cache)))
1874 (while (and (setq days (cdr days)) (< (car days) day))
1875 (setq delta (1+ delta)))
1876 (and days (= day (car days))
1877 (setq holiday t)))
1878 (let* ((weekdays (nth 3 math-holidays-cache))
1879 (weeks (1- (/ (+ day 6) 7)))
1880 (wkday (- day 1 (* weeks 7))))
1881 (setq delta (+ delta (* weeks (length weekdays))))
1882 (while (and weekdays (< (car weekdays) wkday))
1883 (setq weekdays (cdr weekdays)
1884 delta (1+ delta)))
1885 (and weekdays (eq wkday (car weekdays))
1886 (setq holiday t)))
1887 (let ((hours (nth 7 math-holidays-cache)))
1888 (if hours
1889 (progn
1890 (setq time (math-div (math-sub time (car hours)) (cdr hours)))
1891 (if (Math-lessp time 0) (setq time 0))
1892 (or (Math-lessp time 1)
1893 (setq time
1894 (math-sub 1
1895 (math-div 1 (math-mul 86400 (cdr hours)))))))))
bf77c646 1896 (cons (math-add (math-sub day delta) time) holiday)))
136211a9
EZ
1897
1898
1899;;; Compute the date a certain number of business days since Jan 1, 1 AD.
f0529b5b 1900;;; If this returns nil, holiday table was adjusted; redo calculation.
136211a9
EZ
1901
1902(defun math-from-business-day (num)
1903 (let* ((day (math-floor num))
1904 (time (math-sub num day)))
1905 (or (integerp day)
1906 (math-reject-arg nil "*Date is outside valid range"))
1907 (math-setup-holidays)
1908 (let ((days (nth 1 math-holidays-cache))
1909 (delta 0))
1910 (while (and (setq days (cdr days)) (< (car days) day))
1911 (setq delta (1+ delta)))
1912 (setq day (+ day delta)))
1913 (let* ((weekdays (nth 3 math-holidays-cache))
1914 (bweek (- 7 (length weekdays)))
1915 (weeks (1- (/ (+ day (1- bweek)) bweek)))
1916 (wkday (- day 1 (* weeks bweek)))
1917 (w 0))
1918 (setq day (+ day (* weeks (length weekdays))))
1919 (while (if (memq w weekdays)
1920 (setq day (1+ day))
1921 (> (setq wkday (1- wkday)) 0))
1922 (setq w (1+ w)))
1923 (let ((hours (nth 7 math-holidays-cache)))
1924 (if hours
1925 (setq time (math-add (math-mul time (cdr hours)) (car hours)))))
1926 (and (not (math-setup-holidays day))
bf77c646 1927 (list 'date (math-add day time))))))
136211a9 1928
98223359
JB
1929;; The variable math-sh-year is local to math-setup-holidays
1930;; and math-setup-year-holiday, but is used by math-setup-add-holidays,
1931;; which is called by math-setup-holidays and math-setup-year-holiday.
1932(defvar math-sh-year)
136211a9
EZ
1933
1934(defun math-setup-holidays (&optional date)
1935 (or (eq (calc-var-value 'var-Holidays) math-holidays-cache-tag)
1936 (let ((h (calc-var-value 'var-Holidays))
1937 (wdnames '( (sun . 0) (mon . 1) (tue . 2) (wed . 3)
1938 (thu . 4) (fri . 5) (sat . 6) ))
1939 (days nil) (weekdays nil) (exprs nil) (limit nil) (hours nil))
1940 (or (math-vectorp h)
1941 (math-reject-arg h "*Holidays variable must be a vector"))
1942 (while (setq h (cdr h))
1943 (cond ((or (and (eq (car-safe (car h)) 'date)
1944 (integerp (nth 1 (car h))))
1945 (and (eq (car-safe (car h)) 'intv)
1946 (eq (car-safe (nth 2 (car h))) 'date))
1947 (eq (car-safe (car h)) 'vec))
1948 (setq days (cons (car h) days)))
1949 ((and (eq (car-safe (car h)) 'var)
1950 (assq (nth 1 (car h)) wdnames))
1951 (setq weekdays (cons (cdr (assq (nth 1 (car h)) wdnames))
1952 weekdays)))
1953 ((and (eq (car-safe (car h)) 'intv)
1954 (eq (car-safe (nth 2 (car h))) 'hms)
1955 (eq (car-safe (nth 3 (car h))) 'hms))
1956 (if hours
1957 (math-reject-arg
1958 (car h) "*Only one hours interval allowed in Holidays"))
1959 (setq hours (math-div (car h) '(hms 24 0 0)))
1960 (if (or (Math-lessp (nth 2 hours) 0)
1961 (Math-lessp 1 (nth 3 hours)))
1962 (math-reject-arg
1963 (car h) "*Hours interval out of range"))
1964 (setq hours (cons (nth 2 hours)
1965 (math-sub (nth 3 hours) (nth 2 hours))))
1966 (if (Math-zerop (cdr hours))
1967 (math-reject-arg
1968 (car h) "*Degenerate hours interval")))
1969 ((or (and (eq (car-safe (car h)) 'intv)
1970 (Math-integerp (nth 2 (car h)))
1971 (Math-integerp (nth 3 (car h))))
1972 (and (integerp (car h))
1973 (> (car h) 1900) (< (car h) 2100)))
1974 (if limit
1975 (math-reject-arg
1976 (car h) "*Only one limit allowed in Holidays"))
1977 (setq limit (calcFunc-vint (car h) '(intv 3 1 2737)))
1978 (if (equal limit '(vec))
1979 (math-reject-arg (car h) "*Limit is out of range")))
1980 ((or (math-expr-contains (car h) '(var y var-y))
1981 (math-expr-contains (car h) '(var m var-m)))
1982 (setq exprs (cons (car h) exprs)))
1983 (t (math-reject-arg
1984 (car h) "*Holidays must contain a vector of holidays"))))
1985 (if (= (length weekdays) 7)
1986 (math-reject-arg nil "*Too many weekend days"))
1987 (setq math-holidays-cache (list (list -1) ; 0: days list
1988 (list -1) ; 1: inverse-days list
1989 nil ; 2: exprs
1990 (sort weekdays '<)
1991 (or limit '(intv 3 1 2737))
1992 nil ; 5: (lo.hi) expanded years
1993 (cons exprs days)
1994 hours) ; 7: business hours
1995 math-holidays-cache-tag (calc-var-value 'var-Holidays))))
1996 (if date
1997 (let ((year (calcFunc-year date))
1998 (limits (nth 5 math-holidays-cache))
1999 (done nil))
2000 (or (eq (calcFunc-in year (nth 4 math-holidays-cache)) 1)
2001 (progn
2002 (or (eq (car-safe date) 'date) (setq date (list 'date date)))
2003 (math-reject-arg date "*Date is outside valid range")))
2004 (unwind-protect
2005 (let ((days (nth 6 math-holidays-cache)))
2006 (if days
98223359 2007 (let ((math-sh-year nil)) ; see below
136211a9
EZ
2008 (setcar (nthcdr 6 math-holidays-cache) nil)
2009 (math-setup-add-holidays (cons 'vec (cdr days)))
2010 (setcar (nthcdr 2 math-holidays-cache) (car days))))
2011 (cond ((not (nth 2 math-holidays-cache))
2012 (setq done t)
2013 nil)
2014 ((not limits)
2015 (setcar (nthcdr 5 math-holidays-cache) (cons year year))
2016 (math-setup-year-holidays year)
2017 (setq done t))
2018 ((< year (car limits))
2019 (message "Computing holidays, %d .. %d"
2020 year (1- (car limits)))
2021 (calc-set-command-flag 'clear-message)
2022 (while (< year (car limits))
2023 (setcar limits (1- (car limits)))
2024 (math-setup-year-holidays (car limits)))
2025 (setq done t))
2026 ((> year (cdr limits))
2027 (message "Computing holidays, %d .. %d"
2028 (1+ (cdr limits)) year)
2029 (calc-set-command-flag 'clear-message)
2030 (while (> year (cdr limits))
2031 (setcdr limits (1+ (cdr limits)))
2032 (math-setup-year-holidays (cdr limits)))
2033 (setq done t))
2034 (t
2035 (setq done t)
2036 nil)))
bf77c646 2037 (or done (setq math-holidays-cache-tag t))))))
136211a9 2038
98223359 2039(defun math-setup-year-holidays (math-sh-year)
136211a9
EZ
2040 (let ((exprs (nth 2 math-holidays-cache)))
2041 (while exprs
98223359 2042 (let* ((var-y math-sh-year)
136211a9
EZ
2043 (var-m nil)
2044 (expr (math-evaluate-expr (car exprs))))
2045 (if (math-expr-contains expr '(var m var-m))
2046 (let ((var-m 0))
2047 (while (<= (setq var-m (1+ var-m)) 12)
2048 (math-setup-add-holidays (math-evaluate-expr expr))))
2049 (math-setup-add-holidays expr)))
bf77c646 2050 (setq exprs (cdr exprs)))))
136211a9 2051
98223359 2052(defun math-setup-add-holidays (days) ; uses "math-sh-year"
136211a9
EZ
2053 (cond ((eq (car-safe days) 'vec)
2054 (while (setq days (cdr days))
2055 (math-setup-add-holidays (car days))))
2056 ((eq (car-safe days) 'intv)
2057 (let ((day (math-ceiling (nth 2 days))))
2058 (or (eq (calcFunc-in day days) 1)
2059 (setq day (math-add day 1)))
2060 (while (eq (calcFunc-in day days) 1)
2061 (math-setup-add-holidays day)
2062 (setq day (math-add day 1)))))
2063 ((eq (car-safe days) 'date)
2064 (math-setup-add-holidays (nth 1 days)))
2065 ((eq days 0))
2066 ((integerp days)
98223359 2067 (let ((b (math-to-business-day days math-sh-year)))
136211a9
EZ
2068 (or (cdr b) ; don't register holidays twice!
2069 (let ((prev (car math-holidays-cache))
2070 (iprev (nth 1 math-holidays-cache)))
2071 (while (and (cdr prev) (< (nth 1 prev) days))
2072 (setq prev (cdr prev) iprev (cdr iprev)))
2073 (setcdr prev (cons days (cdr prev)))
2074 (setcdr iprev (cons (car b) (cdr iprev)))
2075 (while (setq iprev (cdr iprev))
2076 (setcar iprev (1- (car iprev))))))))
2077 ((Math-realp days)
2078 (math-reject-arg (list 'date days) "*Invalid holiday value"))
2079 (t
bf77c646 2080 (math-reject-arg days "*Holiday formula failed to evaluate"))))
136211a9
EZ
2081
2082
2083
2084
2085;;;; Error forms.
2086
2087;;; Build a standard deviation form. [X X X]
2088(defun math-make-sdev (x sigma)
2089 (if (memq (car-safe x) '(date mod sdev intv vec))
2090 (math-reject-arg x 'realp))
2091 (if (memq (car-safe sigma) '(date mod sdev intv vec))
2092 (math-reject-arg sigma 'realp))
2093 (if (or (Math-negp sigma) (memq (car-safe sigma) '(cplx polar)))
2094 (setq sigma (math-abs sigma)))
2095 (if (and (Math-zerop sigma) (Math-scalarp x))
2096 x
bf77c646 2097 (list 'sdev x sigma)))
136211a9 2098(defun calcFunc-sdev (x sigma)
bf77c646 2099 (math-make-sdev x sigma))
136211a9
EZ
2100
2101
2102
2103;;;; Modulo forms.
2104
2105(defun math-normalize-mod (a)
2106 (let ((n (math-normalize (nth 1 a)))
2107 (m (math-normalize (nth 2 a))))
2108 (if (and (math-anglep n) (math-anglep m) (math-posp m))
2109 (math-make-mod n m)
bf77c646 2110 (math-normalize (list 'calcFunc-makemod n m)))))
136211a9
EZ
2111
2112;;; Build a modulo form. [N R R]
2113(defun math-make-mod (n m)
2114 (setq calc-previous-modulo m)
2115 (and n
2116 (cond ((not (Math-anglep m))
2117 (math-reject-arg m 'anglep))
2118 ((not (math-posp m))
2119 (math-reject-arg m 'posp))
2120 ((Math-anglep n)
2121 (if (or (Math-negp n)
2122 (not (Math-lessp n m)))
2123 (list 'mod (math-mod n m) m)
2124 (list 'mod n m)))
2125 ((memq (car n) '(+ - / vec neg))
2126 (math-normalize
2127 (cons (car n)
2128 (mapcar (function (lambda (x) (math-make-mod x m)))
2129 (cdr n)))))
2130 ((and (eq (car n) '*) (Math-anglep (nth 1 n)))
2131 (math-mul (math-make-mod (nth 1 n) m) (nth 2 n)))
2132 ((memq (car n) '(* ^ var calcFunc-subscr))
2133 (math-mul (math-make-mod 1 m) n))
bf77c646 2134 (t (math-reject-arg n 'anglep)))))
136211a9 2135(defun calcFunc-makemod (n m)
bf77c646 2136 (math-make-mod n m))
136211a9
EZ
2137
2138
2139
2140;;;; Interval forms.
2141
2142;;; Build an interval form. [X S X X]
2143(defun math-make-intv (mask lo hi)
2144 (if (memq (car-safe lo) '(cplx polar mod sdev intv vec))
2145 (math-reject-arg lo 'realp))
2146 (if (memq (car-safe hi) '(cplx polar mod sdev intv vec))
2147 (math-reject-arg hi 'realp))
2148 (or (eq (eq (car-safe lo) 'date) (eq (car-safe hi) 'date))
2149 (math-reject-arg (if (eq (car-safe lo) 'date) hi lo) 'datep))
2150 (if (and (or (Math-realp lo) (eq (car lo) 'date))
2151 (or (Math-realp hi) (eq (car hi) 'date)))
2152 (let ((cmp (math-compare lo hi)))
2153 (if (= cmp 0)
2154 (if (= mask 3)
2155 lo
2156 (list 'intv mask lo hi))
2157 (if (> cmp 0)
2158 (if (= mask 3)
2159 (list 'intv 2 lo lo)
2160 (list 'intv mask lo lo))
2161 (list 'intv mask lo hi))))
bf77c646 2162 (list 'intv mask lo hi)))
136211a9
EZ
2163(defun calcFunc-intv (mask lo hi)
2164 (if (math-messy-integerp mask) (setq mask (math-trunc mask)))
2165 (or (natnump mask) (math-reject-arg mask 'fixnatnump))
2166 (or (<= mask 3) (math-reject-arg mask 'range))
bf77c646 2167 (math-make-intv mask lo hi))
136211a9
EZ
2168
2169(defun math-sort-intv (mask lo hi)
2170 (if (Math-lessp hi lo)
2171 (math-make-intv (aref [0 2 1 3] mask) hi lo)
bf77c646 2172 (math-make-intv mask lo hi)))
136211a9
EZ
2173
2174
2175
2176
2177(defun math-combine-intervals (a am b bm c cm d dm)
2178 (let (res)
2179 (if (= (setq res (math-compare a c)) 1)
2180 (setq a c am cm)
2181 (if (= res 0)
2182 (setq am (or am cm))))
2183 (if (= (setq res (math-compare b d)) -1)
2184 (setq b d bm dm)
2185 (if (= res 0)
2186 (setq bm (or bm dm))))
bf77c646 2187 (math-make-intv (+ (if am 2 0) (if bm 1 0)) a b)))
136211a9
EZ
2188
2189
2190(defun math-div-mod (a b m) ; [R R R R] (Returns nil if no solution)
2191 (and (Math-integerp a) (Math-integerp b) (Math-integerp m)
2192 (let ((u1 1) (u3 b) (v1 0) (v3 m))
2193 (while (not (eq v3 0)) ; See Knuth sec 4.5.2, exercise 15
2194 (let* ((q (math-idivmod u3 v3))
2195 (t1 (math-sub u1 (math-mul v1 (car q)))))
2196 (setq u1 v1 u3 v3 v1 t1 v3 (cdr q))))
2197 (let ((q (math-idivmod a u3)))
2198 (and (eq (cdr q) 0)
bf77c646 2199 (math-mod (math-mul (car q) u1) m))))))
136211a9
EZ
2200
2201(defun math-mod-intv (a b)
2202 (let* ((q1 (math-floor (math-div (nth 2 a) b)))
2203 (q2 (math-floor (math-div (nth 3 a) b)))
2204 (m1 (math-sub (nth 2 a) (math-mul q1 b)))
2205 (m2 (math-sub (nth 3 a) (math-mul q2 b))))
2206 (cond ((equal q1 q2)
2207 (math-sort-intv (nth 1 a) m1 m2))
2208 ((and (math-equal-int (math-sub q2 q1) 1)
2209 (math-zerop m2)
2210 (memq (nth 1 a) '(0 2)))
2211 (math-make-intv (nth 1 a) m1 b))
2212 (t
bf77c646 2213 (math-make-intv 2 0 b)))))
136211a9 2214
98223359 2215;; The variables math-exp-str and math-exp-pos are local to
d55ebb63 2216;; math-read-exprs in math-aent.el, but are used by
98223359
JB
2217;; math-read-angle-brackets, which is called (indirectly) by
2218;; math-read-exprs.
2219(defvar math-exp-str)
2220(defvar math-exp-pos)
136211a9
EZ
2221
2222(defun math-read-angle-brackets ()
97660b3e
JB
2223 (let* ((last (or (math-check-for-commas t) (length math-exp-str)))
2224 (str (substring math-exp-str math-exp-pos last))
136211a9
EZ
2225 (res
2226 (if (string-match "\\` *\\([a-zA-Z#][a-zA-Z0-9#]* *,? *\\)*:" str)
2227 (let ((str1 (substring str 0 (1- (match-end 0))))
2228 (str2 (substring str (match-end 0)))
2229 (calc-hashes-used 0))
2230 (setq str1 (math-read-expr (concat "[" str1 "]")))
2231 (if (eq (car-safe str1) 'error)
2232 str1
2233 (setq str2 (math-read-expr str2))
2234 (if (eq (car-safe str2) 'error)
2235 str2
2236 (append '(calcFunc-lambda) (cdr str1) (list str2)))))
2237 (if (string-match "#" str)
2238 (let ((calc-hashes-used 0))
2239 (and (setq str (math-read-expr str))
2240 (if (eq (car-safe str) 'error)
2241 str
2242 (append '(calcFunc-lambda)
2243 (calc-invent-args calc-hashes-used)
2244 (list str)))))
2245 (math-parse-date str)))))
2246 (if (stringp res)
2247 (throw 'syntax res))
2248 (if (eq (car-safe res) 'error)
2249 (throw 'syntax (nth 2 res)))
97660b3e 2250 (setq math-exp-pos (1+ last))
136211a9 2251 (math-read-token)
bf77c646 2252 res))
136211a9 2253
76216e5a
JB
2254(provide 'calc-forms)
2255
bf77c646 2256;;; calc-forms.el ends here