(chinese-calendar-time-zone): Likewise.
[bpt/emacs.git] / lisp / calendar / cal-china.el
CommitLineData
c86b6d44 1;;; cal-china.el --- calendar functions for the Chinese calendar.
0808d911
ER
2
3;; Copyright (C) 1995 Free Software Foundation, Inc.
4
5;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
6;; Keywords: calendar
7;; Human-Keywords: Chinese calendar, calendar, holidays, diary
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
0808d911
ER
25
26;;; Commentary:
27
28;; This collection of functions implements the features of calendar.el,
0031509c
ER
29;; diary.el, and holidays.el that deal with the Chinese calendar. The rules
30;; used for the Chinese calendar are those of Baolin Liu (see L. E. Doggett's
31;; article "Calendars" in the Explanatory Supplement to the Astronomical
32;; Almanac, second edition, 1992) for the calendar as revised at the beginning
67d80173
ER
33;; of the Qing dynasty in 1644. The nature of the astronomical calculations
34;; is such that precise calculations cannot be made without great expense in
35;; time, so that the calendars produced may not agree perfectly with published
36;; tables--but no two pairs of published tables agree perfectly either! Liu's
37;; rules produce a calendar for 2033 which is not accepted by all authorities.
38;; The date of Chinese New Year is correct from 1644-2051.
0031509c
ER
39
40;; Comments, corrections, and improvements should be sent to
0808d911
ER
41;; Edward M. Reingold Department of Computer Science
42;; (217) 333-6733 University of Illinois at Urbana-Champaign
43;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
44;; Urbana, Illinois 61801
45
46;;; Code:
47
48(require 'lunar)
49
0808d911
ER
50(defvar chinese-calendar-celestial-stem
51 ["Jia" "Yi" "Bing" "Ding" "Wu" "Ji" "Geng" "Xin" "Ren" "Gui"])
52
00e3e480
ER
53(defvar chinese-calendar-terrestrial-branch
54 ["Zi" "Chou" "Yin" "Mao" "Chen" "Si" "Wu" "Wei" "Shen" "You" "Xu" "Hai"])
55
8db540c5 56(defcustom chinese-calendar-time-zone
0808d911
ER
57 '(if (< year 1928)
58 (+ 465 (/ 40.0 60.0))
59 480)
60 "*Number of minutes difference between local standard time for Chinese
61calendar and Coordinated Universal (Greenwich) Time. Default is for Beijing.
62This is an expression in `year' since it changed at 1928-01-01 00:00:00 from
8db540c5 63UT+7:45:40 to UT+8."
9e935048 64 :type 'sexp
8db540c5 65 :group 'chinese-calendar)
0808d911 66
8db540c5
RS
67(defcustom chinese-calendar-location-name "Beijing"
68 "*Name of location used for calculation of Chinese calendar."
69 :type 'string
70 :group 'chinese-calendar)
0808d911 71
8db540c5 72(defcustom chinese-calendar-daylight-time-offset 0
00e3e480
ER
73; The correct value is as follows, but the Chinese calendrical
74; authorities do NOT use DST in determining astronomical events:
75; 60
0808d911 76 "*Number of minutes difference between daylight savings and standard time
8db540c5
RS
77for Chinese calendar. Default is for no daylight savings time."
78 :type 'integer
79 :group 'chinese-calendar)
0808d911 80
8db540c5 81(defcustom chinese-calendar-standard-time-zone-name
0808d911
ER
82 '(if (< year 1928)
83 "PMT"
84 "CST")
9e935048
RS
85 "*Abbreviated name of standard time zone used for Chinese calendar.
86This is an expression depending on `year' because it changed
87at 1928-01-01 00:00:00 from `PMT' to `CST'."
88 :type 'sexp
8db540c5 89 :group 'chinese-calendar)
0808d911 90
8db540c5
RS
91(defcustom chinese-calendar-daylight-time-zone-name "CDT"
92 "*Abbreviated name of daylight-savings time zone used for Chinese calendar."
93 :type 'string
94 :group 'chinese-calendar)
0808d911 95
8db540c5 96(defcustom chinese-calendar-daylight-savings-starts nil
00e3e480
ER
97; The correct value is as follows, but the Chinese calendrical
98; authorities do NOT use DST in determining astronomical events:
99; '(cond ((< 1986 year) (calendar-nth-named-day 1 0 4 year 10))
100; ((= 1986 year) '(5 4 1986))
101; (t nil))
0808d911 102 "*Sexp giving the date on which daylight savings time starts for Chinese
00e3e480 103calendar. Default is for no daylight savings time. See documentation of
8db540c5
RS
104`calendar-daylight-savings-starts'."
105 :type 'sexp
106 :group 'chinese-calendar)
0808d911 107
8db540c5 108(defcustom chinese-calendar-daylight-savings-ends nil
00e3e480
ER
109; The correct value is as follows, but the Chinese calendrical
110; authorities do NOT use DST in determining astronomical events:
111; '(if (<= 1986 year) (calendar-nth-named-day 1 0 9 year 11))
0808d911 112 "*Sexp giving the date on which daylight savings time ends for Chinese
00e3e480 113calendar. Default is for no daylight savings time. See documentation of
8db540c5
RS
114`calendar-daylight-savings-ends'."
115 :type 'sexp
116 :group 'chinese-calendar)
0808d911 117
8db540c5 118(defcustom chinese-calendar-daylight-savings-starts-time 0
0808d911 119 "*Number of minutes after midnight that daylight savings time starts for
8db540c5
RS
120Chinese calendar. Default is for no daylight savings time."
121 :type 'integer
122 :group 'chinese-calendar)
0808d911 123
8db540c5 124(defcustom chinese-calendar-daylight-savings-ends-time 0
0808d911 125 "*Number of minutes after midnight that daylight savings time ends for
8db540c5
RS
126Chinese calendar. Default is for no daylight savings time."
127 :type 'integer
128 :group 'chinese-calendar)
0808d911
ER
129
130(defun chinese-zodiac-sign-on-or-after (d)
131 "Absolute date of first new Zodiac sign on or after absolute date d.
132The Zodiac signs begin when the sun's longitude is a multiple of 30 degrees."
133 (let* ((year (extract-calendar-year
0031509c 134 (calendar-gregorian-from-absolute d)))
0808d911
ER
135 (calendar-time-zone (eval chinese-calendar-time-zone))
136 (calendar-daylight-time-offset
137 chinese-calendar-daylight-time-offset)
138 (calendar-standard-time-zone-name
139 chinese-calendar-standard-time-zone-name)
140 (calendar-daylight-time-zone-name
141 chinese-calendar-daylight-time-zone-name)
142 (calendar-calendar-daylight-savings-starts
143 chinese-calendar-daylight-savings-starts)
144 (calendar-daylight-savings-ends
145 chinese-calendar-daylight-savings-ends)
146 (calendar-daylight-savings-starts-time
147 chinese-calendar-daylight-savings-starts-time)
148 (calendar-daylight-savings-ends-time
149 chinese-calendar-daylight-savings-ends-time))
150 (floor
151 (calendar-absolute-from-astro
152 (solar-date-next-longitude
153 (calendar-astro-from-absolute d)
154 30)))))
155
156(defun chinese-new-moon-on-or-after (d)
157 "Absolute date of first new moon on or after absolute date d."
158 (let* ((year (extract-calendar-year
159 (calendar-gregorian-from-absolute d)))
160 (calendar-time-zone (eval chinese-calendar-time-zone))
161 (calendar-daylight-time-offset
162 chinese-calendar-daylight-time-offset)
163 (calendar-standard-time-zone-name
164 chinese-calendar-standard-time-zone-name)
165 (calendar-daylight-time-zone-name
166 chinese-calendar-daylight-time-zone-name)
167 (calendar-calendar-daylight-savings-starts
168 chinese-calendar-daylight-savings-starts)
169 (calendar-daylight-savings-ends
170 chinese-calendar-daylight-savings-ends)
171 (calendar-daylight-savings-starts-time
172 chinese-calendar-daylight-savings-starts-time)
173 (calendar-daylight-savings-ends-time
174 chinese-calendar-daylight-savings-ends-time))
175 (floor
176 (calendar-absolute-from-astro
177 (lunar-new-moon-on-or-after
178 (calendar-astro-from-absolute d))))))
179
0031509c 180(defvar chinese-year-cache
00e3e480
ER
181 '((1989 (12 726110) (1 726139) (2 726169) (3 726198) (4 726227) (5 726257)
182 (6 726286) (7 726316) (8 726345) (9 726375) (10 726404) (11 726434))
183 (1990 (12 726464) (1 726494) (2 726523) (3 726553) (4 726582) (5 726611)
184 (5.5 726641) (6 726670) (7 726699) (8 726729) (9 726758) (10 726788)
185 (11 726818))
186 (1991 (12 726848) (1 726878) (2 726907) (3 726937) (4 726966) (5 726995)
187 (6 727025) (7 727054) (8 727083) (9 727113) (10 727142) (11 727172))
188 (1992 (12 727202) (1 727232) (2 727261) (3 727291) (4 727321) (5 727350)
189 (6 727379) (7 727409) (8 727438) (9 727467) (10 727497) (11 727526))
190 (1993 (12 727556) (1 727586) (2 727615) (3 727645) (3.5 727675) (4 727704)
191 (5 727734) (6 727763) (7 727793) (8 727822) (9 727851) (10 727881)
192 (11 727910))
193 (1994 (12 727940) (1 727969) (2 727999) (3 728029) (4 728059) (5 728088)
194 (6 728118) (7 728147) (8 728177) (9 728206) (10 728235) (11 728265))
195 (1995 (12 728294) (1 728324) (2 728353) (3 728383) (4 728413) (5 728442)
196 (6 728472) (7 728501) (8 728531) (8.5 728561) (9 728590) (10 728619)
197 (11 728649))
198 (1996 (12 728678) (1 728708) (2 728737) (3 728767) (4 728796) (5 728826)
199 (6 728856) (7 728885) (8 728915) (9 728944) (10 728974) (11 729004))
200 (1997 (12 729033) (1 729062) (2 729092) (3 729121) (4 729151) (5 729180)
201 (6 729210) (7 729239) (8 729269) (9 729299) (10 729328) (11 729358))
202 (1998 (12 729388) (1 729417) (2 729447) (3 729476) (4 729505) (5 729535)
203 (5.5 729564) (6 729593) (7 729623) (8 729653) (9 729682) (10 729712)
204 (11 729742))
205 (1999 (12 729771) (1 729801) (2 729831) (3 729860) (4 729889) (5 729919)
206 (6 729948) (7 729977) (8 730007) (9 730036) (10 730066) (11 730096))
207 (2000 (12 730126) (1 730155) (2 730185) (3 730215) (4 730244) (5 730273)
208 (6 730303) (7 730332) (8 730361) (9 730391) (10 730420) (11 730450)))
0031509c
ER
209 "An assoc list of Chinese year structures as determined by `chinese-year'.
210
211Values are computed as needed, but to save time, the initial value consists
212of the precomputed years 1989-2000. The code works just as well with this
213set to nil initially (which is how the value for 1989-2000 was computed).")
214
215(defun chinese-year (y)
216 "The structure of the Chinese year for Gregorian year Y.
00e3e480 217The result is a list of pairs (i d), where month i begins on absolute date d,
0031509c
ER
218of the Chinese months from the Chinese month following the solstice in
219Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y.
220
221The list is cached for further use."
222 (let ((list (cdr (assoc y chinese-year-cache))))
223 (if (not list)
224 (progn
225 (setq list (compute-chinese-year y))
226 (setq chinese-year-cache
00e3e480 227 (append chinese-year-cache (list (cons y list))))))
0031509c
ER
228 list))
229
67d80173 230(defun number-chinese-months (list start)
0031509c 231 "Assign month numbers to the lunar months in LIST, starting with START.
67d80173
ER
232Numbers are assigned sequentially, START, START+1, ..., 11, with half
233numbers used for leap months.
0031509c 234
67d80173 235First month of list will never be a leap month, nor will the last."
0031509c 236 (if list
67d80173
ER
237 (if (zerop (- 12 start (length list)))
238 ;; List is too short for a leap month
00e3e480 239 (cons (list start (car list))
67d80173 240 (number-chinese-months (cdr list) (1+ start)))
0031509c 241 (cons
67d80173 242 ;; First month
00e3e480 243 (list start (car list))
67d80173 244 ;; Remaining months
0031509c 245 (if (and (cdr (cdr list));; at least two more months...
0031509c
ER
246 (<= (car (cdr (cdr list)))
247 (chinese-zodiac-sign-on-or-after (car (cdr list)))))
67d80173 248 ;; Next month is a leap month
00e3e480 249 (cons (list (+ start 0.5) (car (cdr list)))
67d80173
ER
250 (number-chinese-months (cdr (cdr list)) (1+ start)))
251 ;; Next month is not a leap month
0031509c
ER
252 (number-chinese-months (cdr list) (1+ start)))))))
253
254(defun chinese-month-list (start end)
255 "List of starting dates of Chinese months from START to END."
256 (if (<= start end)
257 (let ((new-moon (chinese-new-moon-on-or-after start)))
258 (if (<= new-moon end)
00e3e480
ER
259 (cons new-moon
260 (chinese-month-list (1+ new-moon) end))))))
0031509c 261
0031509c
ER
262(defun compute-chinese-year (y)
263 "Compute the structure of the Chinese year for Gregorian year Y.
00e3e480 264The result is a list of pairs (i d), where month i begins on absolute date d,
0031509c
ER
265of the Chinese months from the Chinese month following the solstice in
266Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y."
267 (let* ((next-solstice (chinese-zodiac-sign-on-or-after
268 (calendar-absolute-from-gregorian
269 (list 12 15 y))))
270 (list (chinese-month-list (1+ (chinese-zodiac-sign-on-or-after
271 (calendar-absolute-from-gregorian
272 (list 12 15 (1- y)))))
67d80173
ER
273 next-solstice))
274 (next-sign (chinese-zodiac-sign-on-or-after (car list))))
0031509c
ER
275 (if (= (length list) 12)
276 ;; No room for a leap month, just number them 12, 1, 2, ..., 11
00e3e480 277 (cons (list 12 (car list))
67d80173
ER
278 (number-chinese-months (cdr list) 1))
279 ;; Now we can assign numbers to the list for y
280 ;; The first month or two are special
281 (if (or (> (car list) next-sign) (>= next-sign (car (cdr list))))
282 ;; First month on list is a leap month, second is not
00e3e480
ER
283 (append (list (list 11.5 (car list))
284 (list 12 (car (cdr list))))
67d80173
ER
285 (number-chinese-months (cdr (cdr list)) 1))
286 ;; First month on list is not a leap month
00e3e480 287 (append (list (list 12 (car list)))
67d80173
ER
288 (if (>= (chinese-zodiac-sign-on-or-after (car (cdr list)))
289 (car (cdr (cdr list))))
290 ;; Second month on list is a leap month
00e3e480 291 (cons (list 12.5 (car (cdr list)))
67d80173
ER
292 (number-chinese-months (cdr (cdr list)) 1))
293 ;; Second month on list is not a leap month
294 (number-chinese-months (cdr list) 1)))))))
0031509c 295
0808d911
ER
296(defun calendar-absolute-from-chinese (date)
297 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
298The Gregorian date Sunday, December 31, 1 BC is imaginary."
299 (let* ((cycle (car date))
300 (year (car (cdr date)))
301 (month (car (cdr (cdr date))))
302 (day (car (cdr (cdr (cdr date)))))
303 (g-year (+ (* (1- cycle) 60);; years in prior cycles
0031509c
ER
304 (1- year) ;; prior years this cycle
305 -2636))) ;; years before absolute date 0
306 (+ (1- day);; prior days this month
00e3e480
ER
307 (car
308 (cdr ;; absolute date of start of this month
309 (assoc month (append (memq (assoc 1 (chinese-year g-year))
310 (chinese-year g-year))
311 (chinese-year (1+ g-year)))))))))
0808d911
ER
312
313(defun calendar-chinese-from-absolute (date)
314 "Compute Chinese date (cycle year month day) corresponding to absolute DATE.
315The absolute date is the number of days elapsed since the (imaginary)
316Gregorian date Sunday, December 31, 1 BC."
0031509c
ER
317 (let* ((g-year (extract-calendar-year
318 (calendar-gregorian-from-absolute date)))
00e3e480 319 (c-year (+ g-year 2695))
0031509c
ER
320 (list (append (chinese-year (1- g-year))
321 (chinese-year g-year)
322 (chinese-year (1+ g-year)))))
00e3e480
ER
323 (while (<= (car (cdr (car (cdr list)))) date)
324 ;; the first month on the list is in Chinese year c-year
325 ;; date is on or after start of second month on list...
0031509c 326 (if (= 1 (car (car (cdr list))))
00e3e480
ER
327 ;; second month on list is a new Chinese year
328 (setq c-year (1+ c-year)))
329 ;; ...so first month on list is of no interest
0031509c 330 (setq list (cdr list)))
00e3e480
ER
331 (list (/ (1- c-year) 60)
332 (calendar-mod c-year 60)
0031509c 333 (car (car list))
00e3e480 334 (1+ (- date (car (cdr (car list))))))))
0808d911
ER
335
336(defun holiday-chinese-new-year ()
337 "Date of Chinese New Year."
338 (let ((m displayed-month)
339 (y displayed-year))
340 (increment-calendar-month m y 1)
341 (if (< m 5)
342 (let ((chinese-new-year
343 (calendar-gregorian-from-absolute
00e3e480 344 (car (cdr (assoc 1 (chinese-year y)))))))
0808d911 345 (if (calendar-date-is-visible-p chinese-new-year)
b4cb70c4
ER
346 (list
347 (list chinese-new-year
348 (format "Chinese New Year (%s)"
ba2a1fb0 349 (calendar-chinese-sexagesimal-name (+ y 57))))))))))
0808d911
ER
350
351(defun calendar-chinese-date-string (&optional date)
352 "String of Chinese date of Gregorian DATE.
353Defaults to today's date if DATE is not given."
354 (let* ((a-date (calendar-absolute-from-gregorian
355 (or date (calendar-current-date))))
356 (c-date (calendar-chinese-from-absolute a-date))
357 (cycle (car c-date))
358 (year (car (cdr c-date)))
359 (month (car (cdr (cdr c-date))))
360 (day (car (cdr (cdr (cdr c-date)))))
361 (this-month (calendar-absolute-from-chinese
362 (list cycle year month 1)))
363 (next-month (calendar-absolute-from-chinese
67d80173
ER
364 (list (if (= year 60) (1+ cycle) cycle)
365 (if (= (floor month) 12) (1+ year) year)
366 (calendar-mod (1+ (floor month)) 12)
367 1)))
0031509c 368 (m-cycle (% (+ (* year 5) (floor month)) 60)))
b4cb70c4 369 (format "Cycle %s, year %s (%s), %smonth %s%s, day %s (%s)"
0808d911 370 cycle
ba2a1fb0 371 year (calendar-chinese-sexagesimal-name year)
0808d911
ER
372 (if (not (integerp month))
373 "second "
374 (if (< 30 (- next-month this-month))
375 "first "
376 ""))
0031509c 377 (floor month)
b4cb70c4 378 (if (integerp month)
ba2a1fb0 379 (format " (%s)" (calendar-chinese-sexagesimal-name
b4cb70c4
ER
380 (+ (* 5 year) month 44)))
381 "")
ba2a1fb0 382 day (calendar-chinese-sexagesimal-name (+ a-date 15)))))
b4cb70c4 383
ba2a1fb0
PE
384(defun calendar-chinese-sexagesimal-name (n)
385 "The N-th name of the Chinese sexagesimal cycle.
b4cb70c4
ER
386N congruent to 1 gives the first name, N congruent to 2 gives the second name,
387..., N congruent to 60 gives the sixtieth name."
388 (format "%s-%s"
389 (aref chinese-calendar-celestial-stem (% (1- n) 10))
390 (aref chinese-calendar-terrestrial-branch (% (1- n) 12))))
0808d911
ER
391
392(defun calendar-print-chinese-date ()
393 "Show the Chinese date equivalents of date."
394 (interactive)
395 (message "Computing Chinese date...")
396 (message "Chinese date: %s"
397 (calendar-chinese-date-string (calendar-cursor-to-date t))))
398
399(defun calendar-goto-chinese-date (date &optional noecho)
400 "Move cursor to Chinese date DATE.
401Echo Chinese date unless NOECHO is t."
402 (interactive
403 (let* ((c (calendar-chinese-from-absolute
404 (calendar-absolute-from-gregorian
405 (calendar-current-date))))
406 (cycle (calendar-read
0031509c 407 "Chinese calendar cycle number (>44): "
0808d911
ER
408 '(lambda (x) (> x 44))
409 (int-to-string (car c))))
410 (year (calendar-read
0031509c 411 "Year in Chinese cycle (1..60): "
0808d911
ER
412 '(lambda (x) (and (<= 1 x) (<= x 60)))
413 (int-to-string (car (cdr c)))))
0031509c
ER
414 (month-list (make-chinese-month-assoc-list
415 (chinese-months cycle year)))
416 (month (cdr (assoc
417 (completing-read "Chinese calendar month: "
418 month-list nil t)
419 month-list)))
420 (last (if (= month
421 (car (cdr (cdr
422 (calendar-chinese-from-absolute
423 (+ 29
424 (calendar-absolute-from-chinese
425 (list cycle year month 1))))))))
426 30
427 29))
428 (day (calendar-read
429 (format "Chinese calendar day (1-%d): " last)
430 '(lambda (x) (and (<= 1 x) (<= x last))))))
0808d911
ER
431 (list (list cycle year month day))))
432 (calendar-goto-date (calendar-gregorian-from-absolute
433 (calendar-absolute-from-chinese date)))
434 (or noecho (calendar-print-chinese-date)))
435
0031509c
ER
436(defun chinese-months (c y)
437 "A list of the months in cycle C, year Y of the Chinese calendar."
438 (let* ((l (memq 1 (append
439 (mapcar '(lambda (x)
440 (car x))
441 (chinese-year (extract-calendar-year
442 (calendar-gregorian-from-absolute
443 (calendar-absolute-from-chinese
444 (list c y 1 1))))))
445 (mapcar '(lambda (x)
446 (if (> (car x) 11) (car x)))
447 (chinese-year (extract-calendar-year
448 (calendar-gregorian-from-absolute
449 (calendar-absolute-from-chinese
450 (list (if (= y 60) (1+ c) c)
451 (if (= y 60) 1 y)
452 1 1))))))))))
453 l))
454
455(defun make-chinese-month-assoc-list (l)
456 "Make list of months L into an assoc list."
457 (if (and l (car l))
458 (if (and (cdr l) (car (cdr l)))
459 (if (= (car l) (floor (car (cdr l))))
460 (append
461 (list (cons (format "%s (first)" (car l)) (car l))
462 (cons (format "%s (second)" (car l)) (car (cdr l))))
463 (make-chinese-month-assoc-list (cdr (cdr l))))
464 (append
465 (list (cons (int-to-string (car l)) (car l)))
466 (make-chinese-month-assoc-list (cdr l))))
467 (list (cons (int-to-string (car l)) (car l))))))
468
0808d911
ER
469(defun diary-chinese-date ()
470 "Chinese calendar equivalent of date diary entry."
471 (format "Chinese date: %s" (calendar-chinese-date-string date)))
472
c86b6d44 473(provide 'cal-china)
0808d911 474
c86b6d44 475;;; cal-china.el ends here