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