aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward M. Reingold1995-10-23 22:19:07 +0000
committerEdward M. Reingold1995-10-23 22:19:07 +0000
commit0031509cd8dab1f17b465bcda61bc29329c877e7 (patch)
treef5e913636a9298936efe71843288103a337a0a14
parenta5038ae59521c7572095584a8972bd45a79e631b (diff)
downloademacs-0031509cd8dab1f17b465bcda61bc29329c877e7.tar.gz
emacs-0031509cd8dab1f17b465bcda61bc29329c877e7.zip
Completely rewritten!
-rw-r--r--lisp/calendar/cal-china.el331
1 files changed, 243 insertions, 88 deletions
diff --git a/lisp/calendar/cal-china.el b/lisp/calendar/cal-china.el
index 1f4c4027f93..59c6d061a18 100644
--- a/lisp/calendar/cal-china.el
+++ b/lisp/calendar/cal-china.el
@@ -25,9 +25,18 @@
25;;; Commentary: 25;;; Commentary:
26 26
27;; This collection of functions implements the features of calendar.el, 27;; This collection of functions implements the features of calendar.el,
28;; diary.el, and holidays.el that deal with the Chinese calendar. It was 28;; diary.el, and holidays.el that deal with the Chinese calendar. The rules
29;; written by 29;; used for the Chinese calendar are those of Baolin Liu (see L. E. Doggett's
30 30;; article "Calendars" in the Explanatory Supplement to the Astronomical
31;; Almanac, second edition, 1992) for the calendar as revised at the beginning
32;; of the Qing dynasty in 1644. Liu's rules produce a calendar for 2033 which
33;; is not accepted by all authorities. Furthermore, the nature of the
34;; astronomical calculations is such that precise calculations cannot be made
35;; without great expense in time, so that the calendars produced may not agree
36;; perfectly with published tables--but no two pairs of published tables agree
37;; perfectly either!
38
39;; Comments, corrections, and improvements should be sent to
31;; Edward M. Reingold Department of Computer Science 40;; Edward M. Reingold Department of Computer Science
32;; (217) 333-6733 University of Illinois at Urbana-Champaign 41;; (217) 333-6733 University of Illinois at Urbana-Champaign
33;; reingold@cs.uiuc.edu 1304 West Springfield Avenue 42;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
@@ -101,8 +110,7 @@ Chinese calendar. Default is for no daylight savings time.")
101 "Absolute date of first new Zodiac sign on or after absolute date d. 110 "Absolute date of first new Zodiac sign on or after absolute date d.
102The Zodiac signs begin when the sun's longitude is a multiple of 30 degrees." 111The Zodiac signs begin when the sun's longitude is a multiple of 30 degrees."
103 (let* ((year (extract-calendar-year 112 (let* ((year (extract-calendar-year
104 (calendar-gregorian-from-absolute 113 (calendar-gregorian-from-absolute d)))
105 (floor (calendar-absolute-from-astro d)))))
106 (calendar-time-zone (eval chinese-calendar-time-zone)) 114 (calendar-time-zone (eval chinese-calendar-time-zone))
107 (calendar-daylight-time-offset 115 (calendar-daylight-time-offset
108 chinese-calendar-daylight-time-offset) 116 chinese-calendar-daylight-time-offset)
@@ -148,6 +156,159 @@ The Zodiac signs begin when the sun's longitude is a multiple of 30 degrees."
148 (lunar-new-moon-on-or-after 156 (lunar-new-moon-on-or-after
149 (calendar-astro-from-absolute d)))))) 157 (calendar-astro-from-absolute d))))))
150 158
159(defvar chinese-year-cache
160 '((1989 (12 . 726110) (1 . 726139) (2 . 726169) (3 . 726198) (4 . 726227)
161 (5 . 726257) (6 . 726286) (7 . 726316) (8 . 726345) (9 . 726375)
162 (10 . 726404) (11 . 726434))
163 (1990 (12 . 726464) (1 . 726494) (2 . 726523) (3 . 726553) (4 . 726582)
164 (5 . 726611) (5.5 . 726641) (6 . 726670) (7 . 726699) (8 . 726729)
165 (9 . 726758) (10 . 726788) (11 . 726818))
166 (1991 (12 . 726848) (1 . 726878) (2 . 726907) (3 . 726937) (4 . 726966)
167 (5 . 726995) (6 . 727025) (7 . 727054) (8 . 727083) (9 . 727113)
168 (10 . 727142) (11 . 727172))
169 (1992 (12 . 727202) (1 . 727232) (2 . 727261) (3 . 727291) (4 . 727321)
170 (5 . 727350) (6 . 727379) (7 . 727409) (8 . 727438) (9 . 727467)
171 (10 . 727497) (11 . 727526))
172 (1993 (12 . 727556) (1 . 727586) (2 . 727615) (3 . 727645) (3.5 . 727675)
173 (4 . 727704) (5 . 727734) (6 . 727763) (7 . 727793) (8 . 727822)
174 (9 . 727851) (10 . 727881) (11 . 727910))
175 (1994 (12 . 727940) (1 . 727969) (2 . 727999) (3 . 728029) (4 . 728059)
176 (5 . 728088) (6 . 728118) (7 . 728147) (8 . 728177) (9 . 728206)
177 (10 . 728235) (11 . 728265))
178 (1995 (12 . 728294) (1 . 728324) (2 . 728353) (3 . 728383) (4 . 728413)
179 (5 . 728442) (6 . 728472) (7 . 728501) (8 . 728531) (8.5 . 728561)
180 (9 . 728590) (10 . 728619) (11 . 728649))
181 (1996 (12 . 728678) (1 . 728708) (2 . 728737) (3 . 728767) (4 . 728796)
182 (5 . 728826) (6 . 728856) (7 . 728885) (8 . 728915) (9 . 728944)
183 (10 . 728974) (11 . 729004))
184 (1997 (12 . 729033) (1 . 729062) (2 . 729092) (3 . 729121) (4 . 729151)
185 (5 . 729180) (6 . 729210) (7 . 729239) (8 . 729269) (9 . 729299)
186 (10 . 729328) (11 . 729358))
187 (1998 (12 . 729388) (1 . 729417) (2 . 729447) (3 . 729476) (4 . 729505)
188 (5 . 729535) (5.5 . 729564) (6 . 729593) (7 . 729623) (8 . 729653)
189 (9 . 729682) (10 . 729712) (11 . 729742))
190 (1999 (12 . 729771) (1 . 729801) (2 . 729831) (3 . 729860) (4 . 729889)
191 (5 . 729919) (6 . 729948) (7 . 729977) (8 . 730007) (9 . 730036)
192 (10 . 730066) (11 . 730096))
193 (2000 (12 . 730126) (1 . 730155) (2 . 730185) (3 . 730215) (4 . 730244)
194 (5 . 730273) (6 . 730303) (7 . 730332) (8 . 730361) (9 . 730391)
195 (10 . 730420) (11 . 730450)))
196 "An assoc list of Chinese year structures as determined by `chinese-year'.
197
198Values are computed as needed, but to save time, the initial value consists
199of the precomputed years 1989-2000. The code works just as well with this
200set to nil initially (which is how the value for 1989-2000 was computed).")
201
202(defun chinese-year (y)
203 "The structure of the Chinese year for Gregorian year Y.
204The result is a list of pairs (i . d), where month i begins on absolute date d,
205of the Chinese months from the Chinese month following the solstice in
206Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y.
207
208The list is cached for further use."
209 (let ((list (cdr (assoc y chinese-year-cache))))
210 (if (not list)
211 (progn
212 (setq list (compute-chinese-year y))
213 (setq chinese-year-cache
214 (append chinese-year-cache (list (cons y list))))))
215 list))
216
217(defun number-chinese-months (list start &optional no-leap-months)
218 "Assign month numbers to the lunar months in LIST, starting with START.
219
220If optional parameter NO-LEAP-MONTHS is true, just number the months
221sequentially, ignoring the usual leap month rule.
222
223First month of list will never be a leap month, nor will the last.
224
225Numbers are assigned sequentially mod 12 (but using 12 instead of 0)."
226 (if list
227 (if no-leap-months
228 (cons (cons (calendar-mod start 12) (car list))
229 (number-chinese-months (cdr list) (1+ start) t))
230 (cons
231 ;; first month
232 (cons (calendar-mod start 12) (car list))
233 ;; remaining months
234 (if (and (cdr (cdr list));; at least two more months...
235 ;; ... and next one is a leap month
236 (<= (car (cdr (cdr list)))
237 (chinese-zodiac-sign-on-or-after (car (cdr list)))))
238 (cons (cons (+ (calendar-mod start 12) 0.5) (car (cdr list)))
239 (number-chinese-months (cdr (cdr list)) (1+ start) t))
240 ;; Otherwise, just number the months
241 (number-chinese-months (cdr list) (1+ start)))))))
242
243(defun chinese-month-list (start end)
244 "List of starting dates of Chinese months from START to END."
245 (if (<= start end)
246 (let ((new-moon (chinese-new-moon-on-or-after start)))
247 (if (<= new-moon end)
248 (append (list new-moon)
249 (chinese-month-list (1+ new-moon) end))))))
250
251(defun chinese-leap-months (list low high)
252 "Return list of leap months in LIST with indices in range LOW to HIGH.
253
254A leap month has a non-integer index."
255 (if list
256 (let ((index (car (car list))))
257 (if (and (/= index (floor index))
258 (<= low index)
259 (<= index high))
260 (cons index (chinese-leap-months (cdr list) low high))
261 (chinese-leap-months (cdr list) low high)))))
262
263(defun compute-chinese-year (y)
264 "Compute the structure of the Chinese year for Gregorian year Y.
265The result is a list of pairs (i . d), where month i begins on absolute date d,
266of the Chinese months from the Chinese month following the solstice in
267Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y."
268 (let* ((next-solstice (chinese-zodiac-sign-on-or-after
269 (calendar-absolute-from-gregorian
270 (list 12 15 y))))
271 (list (chinese-month-list (1+ (chinese-zodiac-sign-on-or-after
272 (calendar-absolute-from-gregorian
273 (list 12 15 (1- y)))))
274 next-solstice)))
275 (if (= (length list) 12)
276 ;; No room for a leap month, just number them 12, 1, 2, ..., 11
277 (number-chinese-months list 0 t)
278 (let* ((had-leap-month (chinese-leap-months (chinese-year (1- y)) 1 10))
279 (numbered-list)
280 (next-sign;; On or after first month on list
281 (chinese-zodiac-sign-on-or-after (car list))))
282 ;; Now we can assign numbers to the list for y
283 ;; The first month or two are special
284 (if (and (<= (car list) next-sign) (< next-sign (car (cdr list))))
285 (progn;; First month on list is not a leap month
286 (setq numbered-list (list (cons 12 (car list))))
287 (setq list (cdr list))
288 (setq next-sign (chinese-zodiac-sign-on-or-after (car list))))
289 ;; First month on list might be a leap month...
290 (if (not had-leap-month);; ... it is a leap month
291 (progn;; First month on list is a leap month, so second is not
292 (setq numbered-list (list (cons 11.5 (car list))
293 (cons 12 (car (cdr list)))))
294 (setq list (cdr (cdr list)))
295 (setq had-leap-month t))))
296 (if (and (>= next-sign (car (cdr list)))
297 (not had-leap-month))
298 (progn;; Second month on list is a leap month
299 (setq numbered-list
300 (append numbered-list (list (cons 12.5 (car list)))))
301 (setq list (cdr list))))
302 ;; At this point we have a list of new moons for months 1 to 11 for y.
303 ;; We need to see which are leap months.
304 (if (= (length list) 11)
305 ;; There can be no leap months, just number them 1..11
306 (append numbered-list (number-chinese-months list 1 t))
307 ;; There is a leap month, but it can't be the first one because that
308 ;; would be 12.5 which we already considered. It also can't be the
309 ;; last one because that has the solstice in it.
310 (append numbered-list (number-chinese-months list 1)))))))
311
151(defun calendar-absolute-from-chinese (date) 312(defun calendar-absolute-from-chinese (date)
152 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE. 313 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
153The Gregorian date Sunday, December 31, 1 BC is imaginary." 314The Gregorian date Sunday, December 31, 1 BC is imaginary."
@@ -156,85 +317,32 @@ The Gregorian date Sunday, December 31, 1 BC is imaginary."
156 (month (car (cdr (cdr date)))) 317 (month (car (cdr (cdr date))))
157 (day (car (cdr (cdr (cdr date))))) 318 (day (car (cdr (cdr (cdr date)))))
158 (g-year (+ (* (1- cycle) 60);; years in prior cycles 319 (g-year (+ (* (1- cycle) 60);; years in prior cycles
159 (1- year);; prior years this cycle 320 (1- year) ;; prior years this cycle
160 -2636));; years before absolute date 0 321 -2636))) ;; years before absolute date 0
161 (new-year (chinese-new-year g-year)) 322 (+ (1- day);; prior days this month
162 (current-month new-year) 323 (cdr ;; absolute date of start of this month
163 (current-month-number 1) 324 (assoc month (append (memq (assoc 1 (chinese-year g-year))
164 (next-month (chinese-new-moon-on-or-after (1+ new-year))) 325 (chinese-year g-year))
165 (next-sign (chinese-zodiac-sign-on-or-after 326 (chinese-year (1+ g-year))))))))
166 (1+ (chinese-zodiac-sign-on-or-after current-month))))
167 (had-leap-month nil))
168 (while (< current-month-number month)
169 ;; current-month < next-month <= next-sign
170 (setq current-month next-month)
171 (setq next-month (chinese-new-moon-on-or-after (1+ current-month)))
172 (if (and (<= next-month next-sign) (not had-leap-month))
173 (progn;; leap month
174 (setq current-month-number (+ current-month-number 0.5))
175 (setq had-leap-month t))
176 (setq current-month-number (floor (1+ current-month-number)))
177 (setq next-sign (chinese-zodiac-sign-on-or-after (1+ next-sign)))))
178 (+ current-month (1- day))))
179 327
180(defun calendar-chinese-from-absolute (date) 328(defun calendar-chinese-from-absolute (date)
181 "Compute Chinese date (cycle year month day) corresponding to absolute DATE. 329 "Compute Chinese date (cycle year month day) corresponding to absolute DATE.
182The absolute date is the number of days elapsed since the (imaginary) 330The absolute date is the number of days elapsed since the (imaginary)
183Gregorian date Sunday, December 31, 1 BC." 331Gregorian date Sunday, December 31, 1 BC."
184 (let* ((greg-date (calendar-gregorian-from-absolute date)) 332 (let* ((g-year (extract-calendar-year
185 (greg-year (1- (extract-calendar-year greg-date))) 333 (calendar-gregorian-from-absolute date)))
186 (greg-year 334 (chinese-year (+ g-year 2695))
187 (+ greg-year 335 (list (append (chinese-year (1- g-year))
188 (calendar-sum y greg-year 336 (chinese-year g-year)
189 (>= date (chinese-new-year (1+ y))) 1)) ) 337 (chinese-year (1+ g-year)))))
190 (chinese-year (+ greg-year 2697)) 338 (while (<= (cdr (car (cdr list))) date)
191 (cycle (/ (1- chinese-year) 60)) ;; previous cycles 339 (if (= 1 (car (car (cdr list))))
192 (year (calendar-mod chinese-year 60));; years this cycle 340 (setq chinese-year (1+ chinese-year)))
193 (current-month (chinese-new-year greg-year)) 341 (setq list (cdr list)))
194 (month 1) 342 (list (/ (1- chinese-year) 60)
195 (next-month (chinese-new-moon-on-or-after (1+ current-month))) 343 (calendar-mod chinese-year 60)
196 (next-sign (chinese-zodiac-sign-on-or-after 344 (car (car list))
197 (1+ (chinese-zodiac-sign-on-or-after current-month)))) 345 (1+ (- date (cdr (car list)))))))
198 (had-leap-month nil))
199 (while (<= next-month date)
200 ;; current-month < next-month <= next-sign
201 (setq current-month next-month)
202 (setq next-month (chinese-new-moon-on-or-after (1+ current-month)))
203 (if (and (<= next-month next-sign) (not had-leap-month))
204 (progn;; leap month
205 (setq month (+ month 0.5))
206 (setq had-leap-month t))
207 (setq month (floor (1+ month)))
208 (setq next-sign (chinese-zodiac-sign-on-or-after (1+ next-sign)))))
209 (list cycle year month (1+ (- date current-month)))))
210
211(defun chinese-new-year (year)
212 "The absolute date of Chinese New Year in Gregorian YEAR."
213 (let* ((last-solstice (chinese-zodiac-sign-on-or-after
214 (calendar-absolute-from-gregorian
215 (list 12 15 (1- year)))))
216 (twelfth-new-moon;; twelfth month of previous year
217 (chinese-new-moon-on-or-after (1+ last-solstice)))
218 (thirteenth-new-moon;; maybe leap month, maybe New Year
219 (chinese-new-moon-on-or-after (1+ twelfth-new-moon)))
220 (fourteenth-new-moon;; maybe New Year, maybe second month
221 (chinese-new-moon-on-or-after (1+ thirteenth-new-moon)))
222 (next-solstice (chinese-zodiac-sign-on-or-after
223 (calendar-absolute-from-gregorian (list 12 15 year))))
224 (new-moons (+ 3 (calendar-sum m 0
225 (< (chinese-new-moon-on-or-after
226 (+ fourteenth-new-moon (* 29 m)))
227 next-solstice)
228 1))))
229 (if (and (= new-moons 14)
230 (< (chinese-zodiac-sign-on-or-after
231 (calendar-absolute-from-gregorian (list 2 15 year)))
232 thirteenth-new-moon)
233 (<= fourteenth-new-moon
234 (chinese-zodiac-sign-on-or-after
235 (calendar-absolute-from-gregorian (list 3 15 year)))))
236 fourteeth-new-moon
237 thirteenth-new-moon)))
238 346
239(defun holiday-chinese-new-year () 347(defun holiday-chinese-new-year ()
240 "Date of Chinese New Year." 348 "Date of Chinese New Year."
@@ -244,7 +352,7 @@ Gregorian date Sunday, December 31, 1 BC."
244 (if (< m 5) 352 (if (< m 5)
245 (let ((chinese-new-year 353 (let ((chinese-new-year
246 (calendar-gregorian-from-absolute 354 (calendar-gregorian-from-absolute
247 (chinese-new-year y)))) 355 (cdr (assoc 1 (chinese-year y))))))
248 (if (calendar-date-is-visible-p chinese-new-year) 356 (if (calendar-date-is-visible-p chinese-new-year)
249 (list (list chinese-new-year 357 (list (list chinese-new-year
250 (format "Chinese New Year (%s-%s)" 358 (format "Chinese New Year (%s-%s)"
@@ -266,9 +374,8 @@ Defaults to today's date if DATE is not given."
266 (this-month (calendar-absolute-from-chinese 374 (this-month (calendar-absolute-from-chinese
267 (list cycle year month 1))) 375 (list cycle year month 1)))
268 (next-month (calendar-absolute-from-chinese 376 (next-month (calendar-absolute-from-chinese
269 (list cycle year (1+ month) 1))) 377 (list cycle year (1+ (floor month)) 1)))
270 (month (floor month)) 378 (m-cycle (% (+ (* year 5) (floor month)) 60)))
271 (m-cycle (% (+ (* year 5) month) 60)))
272 (format "Cycle %s, year %s (%s-%s), %smonth %s, day %s (%s-%s)" 379 (format "Cycle %s, year %s (%s-%s), %smonth %s, day %s (%s-%s)"
273 cycle 380 cycle
274 year 381 year
@@ -279,7 +386,7 @@ Defaults to today's date if DATE is not given."
279 (if (< 30 (- next-month this-month)) 386 (if (< 30 (- next-month this-month))
280 "first " 387 "first "
281 "")) 388 ""))
282 month 389 (floor month)
283 day 390 day
284 (aref chinese-calendar-celestial-stem (% (+ a-date 4) 10)) 391 (aref chinese-calendar-celestial-stem (% (+ a-date 4) 10))
285 (aref chinese-calendar-terrestrial-branch (% (+ a-date 2) 12))))) 392 (aref chinese-calendar-terrestrial-branch (% (+ a-date 2) 12)))))
@@ -299,20 +406,68 @@ Echo Chinese date unless NOECHO is t."
299 (calendar-absolute-from-gregorian 406 (calendar-absolute-from-gregorian
300 (calendar-current-date)))) 407 (calendar-current-date))))
301 (cycle (calendar-read 408 (cycle (calendar-read
302 "Cycle number (>44): " 409 "Chinese calendar cycle number (>44): "
303 '(lambda (x) (> x 44)) 410 '(lambda (x) (> x 44))
304 (int-to-string (car c)))) 411 (int-to-string (car c))))
305 (year (calendar-read 412 (year (calendar-read
306 "Year in cycle (1..60): " 413 "Year in Chinese cycle (1..60): "
307 '(lambda (x) (and (<= 1 x) (<= x 60))) 414 '(lambda (x) (and (<= 1 x) (<= x 60)))
308 (int-to-string (car (cdr c))))) 415 (int-to-string (car (cdr c)))))
309 (month (read-minibuffer "Month: ")) 416 (month-list (make-chinese-month-assoc-list
310 (day (read-minibuffer "Day: "))) 417 (chinese-months cycle year)))
418 (month (cdr (assoc
419 (completing-read "Chinese calendar month: "
420 month-list nil t)
421 month-list)))
422 (last (if (= month
423 (car (cdr (cdr
424 (calendar-chinese-from-absolute
425 (+ 29
426 (calendar-absolute-from-chinese
427 (list cycle year month 1))))))))
428 30
429 29))
430 (day (calendar-read
431 (format "Chinese calendar day (1-%d): " last)
432 '(lambda (x) (and (<= 1 x) (<= x last))))))
311 (list (list cycle year month day)))) 433 (list (list cycle year month day))))
312 (calendar-goto-date (calendar-gregorian-from-absolute 434 (calendar-goto-date (calendar-gregorian-from-absolute
313 (calendar-absolute-from-chinese date))) 435 (calendar-absolute-from-chinese date)))
314 (or noecho (calendar-print-chinese-date))) 436 (or noecho (calendar-print-chinese-date)))
315 437
438(defun chinese-months (c y)
439 "A list of the months in cycle C, year Y of the Chinese calendar."
440 (let* ((l (memq 1 (append
441 (mapcar '(lambda (x)
442 (car x))
443 (chinese-year (extract-calendar-year
444 (calendar-gregorian-from-absolute
445 (calendar-absolute-from-chinese
446 (list c y 1 1))))))
447 (mapcar '(lambda (x)
448 (if (> (car x) 11) (car x)))
449 (chinese-year (extract-calendar-year
450 (calendar-gregorian-from-absolute
451 (calendar-absolute-from-chinese
452 (list (if (= y 60) (1+ c) c)
453 (if (= y 60) 1 y)
454 1 1))))))))))
455 l))
456
457(defun make-chinese-month-assoc-list (l)
458 "Make list of months L into an assoc list."
459 (if (and l (car l))
460 (if (and (cdr l) (car (cdr l)))
461 (if (= (car l) (floor (car (cdr l))))
462 (append
463 (list (cons (format "%s (first)" (car l)) (car l))
464 (cons (format "%s (second)" (car l)) (car (cdr l))))
465 (make-chinese-month-assoc-list (cdr (cdr l))))
466 (append
467 (list (cons (int-to-string (car l)) (car l)))
468 (make-chinese-month-assoc-list (cdr l))))
469 (list (cons (int-to-string (car l)) (car l))))))
470
316(defun diary-chinese-date () 471(defun diary-chinese-date ()
317 "Chinese calendar equivalent of date diary entry." 472 "Chinese calendar equivalent of date diary entry."
318 (format "Chinese date: %s" (calendar-chinese-date-string date))) 473 (format "Chinese date: %s" (calendar-chinese-date-string date)))