aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward M. Reingold1995-09-21 02:47:50 +0000
committerEdward M. Reingold1995-09-21 02:47:50 +0000
commit75af4a4adc4c058bcb2d790154b6e128dd121bc0 (patch)
tree28ade45a43f4edf43740a1e034a6c3296b780520
parent8a45b040852b7982ed0c205553219efba66092a0 (diff)
downloademacs-75af4a4adc4c058bcb2d790154b6e128dd121bc0.tar.gz
emacs-75af4a4adc4c058bcb2d790154b6e128dd121bc0.zip
Added code to support Chinese calendar. Minor fixes as well.
-rw-r--r--lisp/calendar/solar.el307
1 files changed, 176 insertions, 131 deletions
diff --git a/lisp/calendar/solar.el b/lisp/calendar/solar.el
index 055c233b9e7..140b412fea2 100644
--- a/lisp/calendar/solar.el
+++ b/lisp/calendar/solar.el
@@ -1,6 +1,6 @@
1;;; solar.el --- calendar functions for solar events. 1;;; solar.el --- calendar functions for solar events.
2 2
3;; Copyright (C) 1992, 1993 Free Software Foundation, Inc. 3;; Copyright (C) 1992, 1993, 1995 Free Software Foundation, Inc.
4 4
5;; Author: Edward M. Reingold <reingold@cs.uiuc.edu> 5;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
6;; Keywords: calendar 6;; Keywords: calendar
@@ -30,9 +30,11 @@
30;; eqinoxes/solstices. 30;; eqinoxes/solstices.
31 31
32;; Based on the ``Almanac for Computers 1984,'' prepared by the Nautical 32;; Based on the ``Almanac for Computers 1984,'' prepared by the Nautical
33;; Almanac Office, United States Naval Observatory, Washington, 1984 and 33;; Almanac Office, United States Naval Observatory, Washington, 1984, on
34;; on ``Astronomical Formulae for Calculators,'' 3rd ed., by Jean Meeus, 34;; ``Astronomical Formulae for Calculators,'' 3rd ed., by Jean Meeus,
35;; Willmann-Bell, Inc., 1985. 35;; Willmann-Bell, Inc., 1985, and on ``Astronomical Algorithms'' by Jean
36;; Meeus, Willmann-Bell, Inc., 1991.
37
36;; 38;;
37;; WARNINGS: 39;; WARNINGS:
38;; 1. SUNRISE/SUNSET calculations will be accurate only to +/- 2 minutes. 40;; 1. SUNRISE/SUNSET calculations will be accurate only to +/- 2 minutes.
@@ -84,7 +86,7 @@ sufficient), + north, - south, such as 40.7 for New York City, or the value
84can be a vector [degrees minutes north/south] such as [40 50 north] for New 86can be a vector [degrees minutes north/south] such as [40 50 north] for New
85York City. 87York City.
86 88
87This variable should be set in `site-init.el'.") 89This variable should be set in site-local.el.")
88 90
89;;;###autoload 91;;;###autoload
90(defvar calendar-longitude nil 92(defvar calendar-longitude nil
@@ -95,7 +97,7 @@ sufficient), + east, - west, such as -73.9 for New York City, or the value
95can be a vector [degrees minutes east/west] such as [73 55 west] for New 97can be a vector [degrees minutes east/west] such as [73 55 west] for New
96York City. 98York City.
97 99
98This variable should be set in `site-init.el'.") 100This variable should be set in site-local.el.")
99 101
100(defsubst calendar-latitude () 102(defsubst calendar-latitude ()
101 "Convert calendar-latitude to a signed decimal fraction, if needed." 103 "Convert calendar-latitude to a signed decimal fraction, if needed."
@@ -139,7 +141,7 @@ This variable should be set in `site-init.el'.")
139For example, \"New York City\". Default value is just the latitude, longitude 141For example, \"New York City\". Default value is just the latitude, longitude
140pair. 142pair.
141 143
142This variable should be set in `site-init.el'.") 144This variable should be set in site-local.el.")
143 145
144(defvar solar-n-hemi-seasons 146(defvar solar-n-hemi-seasons
145 '("Vernal Equinox" "Summer Solstice" "Autumnal Equinox" "Winter Solstice") 147 '("Vernal Equinox" "Summer Solstice" "Autumnal Equinox" "Winter Solstice")
@@ -173,13 +175,13 @@ Returns nil if nothing was entered."
173 (string-to-int x)))) 175 (string-to-int x))))
174 176
175(defsubst solar-sin-degrees (x) 177(defsubst solar-sin-degrees (x)
176 (sin (degrees-to-radians x))) 178 (sin (degrees-to-radians (mod x 360.0))))
177 179
178(defsubst solar-cosine-degrees (x) 180(defsubst solar-cosine-degrees (x)
179 (cos (degrees-to-radians x))) 181 (cos (degrees-to-radians (mod x 360.0))))
180 182
181(defun solar-tangent-degrees (x) 183(defsubst solar-tangent-degrees (x)
182 (tan (degrees-to-radians x))) 184 (tan (degrees-to-radians (mod x 360.0))))
183 185
184(defun solar-xy-to-quadrant (x y) 186(defun solar-xy-to-quadrant (x y)
185 "Determines the quadrant of the point X, Y." 187 "Determines the quadrant of the point X, Y."
@@ -316,60 +318,8 @@ that location on that day."
316 (+ (- local-mean-sunset (solar-degrees-to-hours (calendar-longitude))) 318 (+ (- local-mean-sunset (solar-degrees-to-hours (calendar-longitude)))
317 (/ calendar-time-zone 60.0)))))) 319 (/ calendar-time-zone 60.0))))))
318 320
319(defun solar-adj-time-for-dst (date time &optional style)
320 "Adjust decimal fraction standard TIME on DATE to account for dst.
321Returns a list (date adj-time zone) where `date' and `time' are the values
322adjusted for `zone'; here `date' is a list (month day year), `time' is a
323decimal fraction time, and `zone' is a string.
324
325Optional parameter STYLE forces the result time to be standard time when its
326value is 'standard and daylight savings time (if available) when its value is
327'daylight.
328
329Conversion to daylight savings time is done according to
330`calendar-daylight-savings-starts', `calendar-daylight-savings-ends',
331`calendar-daylight-savings-starts-time',
332`calendar-daylight-savings-ends-time', and
333`calendar-daylight-savings-offset'."
334
335 (let* ((year (extract-calendar-year date))
336 (rounded-abs-date (+ (calendar-absolute-from-gregorian date)
337 (/ (round (* 60 time)) 60.0 24.0)))
338 (dst-starts (and calendar-daylight-savings-starts
339 (+ (calendar-absolute-from-gregorian
340 (eval calendar-daylight-savings-starts))
341 (/ calendar-daylight-savings-starts-time
342 60.0 24.0))))
343 (dst-ends (and calendar-daylight-savings-ends
344 (+ (calendar-absolute-from-gregorian
345 (eval calendar-daylight-savings-ends))
346 (/ (- calendar-daylight-savings-ends-time
347 calendar-daylight-time-offset)
348 60.0 24.0))))
349 (dst (and (not (eq style 'standard))
350 (or (eq style 'daylight)
351 (and dst-starts dst-ends
352 (or (and (< dst-starts dst-ends);; northern hemi.
353 (<= dst-starts rounded-abs-date)
354 (< rounded-abs-date dst-ends))
355 (and (< dst-ends dst-starts);; southern hemi.
356 (or (< rounded-abs-date dst-ends)
357 (<= dst-starts rounded-abs-date)))))
358 (and dst-starts (not dst-ends)
359 (<= dst-starts rounded-abs-date))
360 (and dst-ends (not dst-starts)
361 (< rounded-abs-date dst-ends)))))
362 (time-zone (if dst
363 calendar-daylight-time-zone-name
364 calendar-standard-time-zone-name))
365 (time (+ rounded-abs-date
366 (if dst (/ calendar-daylight-time-offset 24.0 60.0) 0))))
367 (list (calendar-gregorian-from-absolute (truncate time))
368 (* 24.0 (- time (truncate time)))
369 time-zone)))
370
371(defun solar-time-string (time time-zone) 321(defun solar-time-string (time time-zone)
372 "Printable form for decimal fraction TIME on DATE. 322 "Printable form for decimal fraction TIME in TIME-ZONE.
373Format used is given by `calendar-time-display-form'." 323Format used is given by `calendar-time-display-form'."
374 (let* ((time (round (* 60 time))) 324 (let* ((time (round (* 60 time)))
375 (24-hours (/ time 60)) 325 (24-hours (/ time 60))
@@ -382,9 +332,9 @@ Format used is given by `calendar-time-display-form'."
382(defun solar-sunrise-sunset (date) 332(defun solar-sunrise-sunset (date)
383 "String giving local times of sunrise and sunset on Gregorian DATE." 333 "String giving local times of sunrise and sunset on Gregorian DATE."
384 (let* ((rise (solar-sunrise date)) 334 (let* ((rise (solar-sunrise date))
385 (adj-rise (if rise (solar-adj-time-for-dst date rise))) 335 (adj-rise (if rise (dst-adjust-time date rise)))
386 (set (solar-sunset date)) 336 (set (solar-sunset date))
387 (adj-set (if set (solar-adj-time-for-dst date set)))) 337 (adj-set (if set (dst-adjust-time date set))))
388 (format "%s, %s at %s" 338 (format "%s, %s at %s"
389 (if (and rise (calendar-date-equal date (car adj-rise))) 339 (if (and rise (calendar-date-equal date (car adj-rise)))
390 (concat "Sunrise " (apply 'solar-time-string (cdr adj-rise))) 340 (concat "Sunrise " (apply 'solar-time-string (cdr adj-rise)))
@@ -394,59 +344,148 @@ Format used is given by `calendar-time-display-form'."
394 "no sunset") 344 "no sunset")
395 (eval calendar-location-name)))) 345 (eval calendar-location-name))))
396 346
397(defun solar-apparent-longitude-of-sun (date) 347(defun solar-date-next-longitude (d l)
398 "Apparent longitude of the sun on Gregorian DATE." 348 "First moment on or after Julian day number D when sun's longitude is a
399 (let* ((time (/ (- (calendar-absolute-from-gregorian date) 349multiple of L degrees at calendar-location-name with that location's
400 (calendar-absolute-from-gregorian '(1 0.5 1900))) 350local time (including any daylight savings rules).
401 36525)) 351
402 (l (+ 279.69668 352L must be an integer divisor of 360.
403 (* 36000.76892 time) 353
404 (* 0.0003025 time time))) 354Result is in local time expressed astronomical (Julian) day numbers.
405 (m (+ 358.47583 355
406 (* 35999.04975 time) 356The values of calendar-daylight-savings-starts,
407 (* -0.000150 time time) 357calendar-daylight-savings-starts-time, calendar-daylight-savings-ends,
408 (* -0.0000033 time time time))) 358calendar-daylight-savings-ends-time, calendar-daylight-time-offset, and
409 (c (+ (* (+ 1.919460 359calendar-time-zone are used to interpret local time."
410 (* -0.004789 time) 360 (let* ((long)
411 (* -0.000014 time time)) 361 (start d)
412 (solar-sin-degrees m)) 362 (start-long (solar-longitude d))
413 (* (+ 0.020094 363 (next (mod (* l (1+ (floor (/ start-long l)))) 360))
414 (* -0.000100 time)) 364 (end (+ d (* (/ l 360.0) 400)))
415 (solar-sin-degrees (* 2 m))) 365 (end-long (solar-longitude end)))
416 (* 0.000293 366 (while ;; bisection search for nearest minute
417 (solar-sin-degrees (* 3 m))))) 367 (< 0.00001 (- end start))
418 (L (+ l c)) 368 ;; start <= d < end
419 (omega (+ 259.18 369 ;; start-long <= next < end-long when next != 0
420 (* -1934.142 time))) 370 ;; when next = 0, we look for the discontinuity (start-long is near 360
421 (app (+ L 371 ;; and end-long is small (less than l).
422 -0.00569 372 (setq d (/ (+ start end) 2.0))
423 (* -0.00479 373 (setq long (solar-longitude d))
424 (solar-sin-degrees omega))))) 374 (if (or (and (/= next 0) (< long next))
425 app)) 375 (and (= next 0) (< l long)))
376 (progn
377 (setq start d)
378 (setq start-long long))
379 (setq end d)
380 (setq end-long long)))
381 (/ (+ start end) 2.0)))
382
383(defun solar-longitude (d)
384 "Longitude of sun on astronomical (Julian) day number D.
385Accurary is about 0.01 degree (about 365.25*24*60*0.01/360 = 15 minutes).
386
387The values of calendar-daylight-savings-starts,
388calendar-daylight-savings-starts-time, calendar-daylight-savings-ends,
389calendar-daylight-savings-ends-time, calendar-daylight-time-offset, and
390calendar-time-zone are used to interpret local time."
391 (let* ((a-d (calendar-absolute-from-astro d))
392 (date (calendar-gregorian-from-absolute (floor a-d)))
393 (time (* 24 (- a-d (truncate a-d))))
394 (rounded-abs-date (+ (calendar-absolute-from-gregorian date)
395 (/ (round (* 60 time)) 60.0 24.0)))
396 ;; get local standard time
397 (a-d (- rounded-abs-date
398 (if (dst-in-effect rounded-abs-date)
399 (/ calendar-daylight-time-offset 24.0 60.0) 0)))
400 ;; get Universal Time
401 (a-d (- a-d (/ calendar-time-zone 60.0 24.0)))
402 (date (calendar-astro-from-absolute a-d))
403 ;; get Ephemeris Time
404 (date (+ date (solar-ephemeris-correction
405 (extract-calendar-year
406 (calendar-gregorian-from-absolute
407 (floor
408 (calendar-absolute-from-astro
409 date)))))))
410 (T (/ (- date 2451545.0) 36525.0))
411 (Lo (mod (+ 280.46645 (* 36000.76983 T) (* 0.0003032 T T)) 360.0))
412 (M (mod (+ 357.52910
413 (* 35999.05030 T)
414 (* -0.0001559 T T)
415 (* -0.00000048 T T T))
416 360.0))
417 (e (+ 0.016708617 (* -0.000042037 T) (* -0.0000001236 T T)))
418 (C (+ (* (+ 1.914600 (* -0.004817 T) (* -0.000014 T T))
419 (solar-sin-degrees M))
420 (* (+ 0.019993 (* -0.000101 T)) (solar-sin-degrees (* 2 M)))
421 (* 0.000290 (solar-sin-degrees (* 3 M)))))
422 (true-longitude (+ Lo C))
423 (omega (+ 125.04 (* -1934.136 T)))
424 (apparent-longitude (mod
425 (+ true-longitude
426 -0.00569
427 (* -0.00478 (solar-sin-degrees omega)))
428 360.0)))
429 apparent-longitude))
426 430
427(defun solar-ephemeris-correction (year) 431(defun solar-ephemeris-correction (year)
428 "Difference in minutes between Ephemeris time and UTC in YEAR. 432 "Ephemeris time minus Universal Time at astronomical (Julian) day D.
429Value is only an approximation." 433Result is in days For the years 1800-1987, the maximum error is 1.9 seconds.
430 (let ((T (/ (- year 1900) 100.0))) 434For the other years, the maximum error is about 30 seconds."
431 (+ 0.41 (* 1.2053 T) (* 0.4992 T T)))) 435 (cond ((and (<= 1988 year) (< year 2020))
432 436 (/ (+ year -2000 67.0) 60.0 60.0 24.0))
433(defun solar-equinoxes/solstices (k year) 437 ((and (<= 1900 year) (< year 1988))
434 "Date of equinox/solstice K for YEAR. K=0, spring equinox; K=1, summer 438 (let* ((theta (/ (- (calendar-astro-from-absolute
435solstice; K=2, fall equinox; K=3, winter solstice. Accurate to within 439 (calendar-absolute-from-gregorian
436several minutes." 440 (list 7 1 year)))
437 (let ((date (list (+ 3 (* k 3)) 21 year)) 441 (calendar-astro-from-absolute
438 app 442 (calendar-absolute-from-gregorian
439 (correction 1000)) 443 '(1 1 1900))))
440 (while (> correction 0.00001) 444 36525.0))
441 (setq app (mod (solar-apparent-longitude-of-sun date) 360)) 445 (theta2 (* theta theta))
442 (setq correction (* 58 (solar-sin-degrees (- (* k 90) app)))) 446 (theta3 (* theta2 theta))
443 (setq date (list (extract-calendar-month date) 447 (theta4 (* theta2 theta2))
444 (+ (extract-calendar-day date) correction) 448 (theta5 (* theta3 theta2)))
445 year))) 449 (+ -0.00002
446 (list (extract-calendar-month date) 450 (* 0.000297 theta)
447 (+ (extract-calendar-day date) (/ calendar-time-zone 60.0 24.0) 451 (* 0.025184 theta2)
448 (- (/ (solar-ephemeris-correction year) 60.0 24.0))) 452 (* -0.181133 theta3)
449 year))) 453 (* 0.553040 theta4)
454 (* -0.861938 theta5)
455 (* 0.677066 theta3 theta3)
456 (* -0.212591 theta4 theta3))))
457 ((and (<= 1800 year) (< year 1900))
458 (let* ((theta (/ (- (calendar-astro-from-absolute
459 (calendar-absolute-from-gregorian
460 (list 7 1 year)))
461 (calendar-astro-from-absolute
462 (calendar-absolute-from-gregorian
463 '(1 1 1900))))
464 36525.0))
465 (theta2 (* theta theta))
466 (theta3 (* theta2 theta))
467 (theta4 (* theta2 theta2))
468 (theta5 (* theta3 theta2)))
469 (+ -0.000009
470 (* 0.003844 theta)
471 (* 0.083563 theta2)
472 (* 0.865736 theta3)
473 (* 4.867575 theta4)
474 (* 15.845535 theta5)
475 (* 31.332267 theta3 theta3)
476 (* 38.291999 theta4 theta3)
477 (* 28.316289 theta4 theta4)
478 (* 11.636204 theta4 theta5)
479 (* 2.043794 theta5 theta5))))
480 ((and (<= 1620 year) (< year 1800))
481 (let ((x (/ (- year 1600) 10.0)))
482 (/ (+ (* 2.19167 x x) (* -40.675 x) 196.58333) 60.0 60.0 24.0)))
483 (t (let* ((tmp (- (calendar-astro-from-absolute
484 (calendar-absolute-from-gregorian
485 (list 1 1 year)))
486 2382148))
487 (second (- (/ (* tmp tmp) 41048480.0) 15)))
488 (/ second 60.0 60.0 24.0)))))
450 489
451;;;###autoload 490;;;###autoload
452(defun sunrise-sunset (&optional arg) 491(defun sunrise-sunset (&optional arg)
@@ -545,7 +584,7 @@ No diary entry if there is no sunset on that date."
545 (if (= (% (calendar-absolute-from-gregorian date) 7) 5);; Friday 584 (if (= (% (calendar-absolute-from-gregorian date) 7) 5);; Friday
546 (let* ((sunset (solar-sunset date)) 585 (let* ((sunset (solar-sunset date))
547 (light (if sunset 586 (light (if sunset
548 (solar-adj-time-for-dst 587 (dst-adjust-time
549 date 588 date
550 (- sunset (/ 18.0 60.0)))))) 589 (- sunset (/ 18.0 60.0))))))
551 (if (and light (calendar-date-equal date (car light))) 590 (if (and light (calendar-date-equal date (car light)))
@@ -569,19 +608,25 @@ Requires floating point."
569 (if calendar-time-zone calendar-daylight-savings-ends)) 608 (if calendar-time-zone calendar-daylight-savings-ends))
570 (calendar-time-zone (if calendar-time-zone calendar-time-zone 0)) 609 (calendar-time-zone (if calendar-time-zone calendar-time-zone 0))
571 (k (1- (/ m 3))) 610 (k (1- (/ m 3)))
572 (date (solar-equinoxes/solstices k y)) 611 (d (solar-date-next-longitude
573 (s-hemi (and calendar-latitude (< (calendar-latitude) 0))) 612 (calendar-astro-from-absolute
574 (day (extract-calendar-day date)) 613 (calendar-absolute-from-gregorian
575 (adj (solar-adj-time-for-dst 614 (list (+ 3 (* k 3)) 15 y)))
576 (list (extract-calendar-month date) 615 90))
577 (truncate day) 616 (abs-day (calendar-absolute-from-astro d)))
578 (extract-calendar-year date)) 617 (list
579 (* 24 (- day (truncate day)))))) 618 (list (calendar-gregorian-from-absolute (floor abs-day))
580 (list (list (car adj) 619 (format "%s %s"
581 (format "%s %s" 620 (nth k (if (and calendar-latitude
582 (nth k (if s-hemi solar-s-hemi-seasons 621 (< (calendar-latitude) 0))
583 solar-n-hemi-seasons)) 622 solar-s-hemi-seasons
584 (apply 'solar-time-string (cdr adj)))))))) 623 solar-n-hemi-seasons))
624 (solar-time-string
625 (* 24 (- abs-day (floor abs-day)))
626 (if (dst-in-effect abs-day)
627 calendar-daylight-time-zone-name
628 calendar-standard-time-zone-name))))))))
629
585 630
586(provide 'solar) 631(provide 'solar)
587 632