guile feature
[bpt/emacs.git] / lisp / calendar / cal-china.el
CommitLineData
3afbc435 1;;; cal-china.el --- calendar functions for the Chinese calendar
0808d911 2
ba318903 3;; Copyright (C) 1995, 1997, 2001-2014 Free Software Foundation, Inc.
0808d911
ER
4
5;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
dbfca9c4 6;; Maintainer: Glenn Morris <rgm@gnu.org>
0808d911
ER
7;; Keywords: calendar
8;; Human-Keywords: Chinese calendar, calendar, holidays, diary
bd78fa1d 9;; Package: calendar
0808d911
ER
10
11;; This file is part of GNU Emacs.
12
2ed66575 13;; GNU Emacs is free software: you can redistribute it and/or modify
0808d911 14;; it under the terms of the GNU General Public License as published by
2ed66575
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
0808d911
ER
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
2ed66575 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
0808d911
ER
25
26;;; Commentary:
27
b1c57079
GM
28;; See calendar.el.
29
30;; The rules used for the Chinese calendar are those of Baolin Liu
31;; (see L. E. Doggett's article "Calendars" in the Explanatory
32;; Supplement to the Astronomical Almanac, second edition, 1992) for
33;; the calendar as revised at the beginning of the Qing dynasty in
34;; 1644. The nature of the astronomical calculations is such that
35;; precise calculations cannot be made without great expense in time,
36;; so that the calendars produced may not agree perfectly with
37;; published tables--but no two pairs of published tables agree
38;; perfectly either! Liu's rules produce a calendar for 2033 which is
39;; not accepted by all authorities. The date of Chinese New Year is
40;; correct from 1644-2051.
a96a5fca 41
465323b6
GM
42;; Note to maintainers:
43;; Use `chinese-year-cache-init' every few years to recenter the default
44;; value of `chinese-year-cache'.
0808d911 45
465323b6 46;;; Code:
0b0f8fa4 47
829e0295
GM
48(require 'calendar)
49(require 'lunar) ; lunar-new-moon-on-or-after
50;; solar-date-next-longitude brought in by lunar.
51;;;(require 'solar)
5c645a20 52;; calendar-astro-to-absolute and from-absolute are cal-autoloads.
829e0295
GM
53;;;(require 'cal-julian)
54
0808d911 55
0b41781b 56(defgroup calendar-chinese nil
bfcb5172 57 "Chinese calendar support."
0b41781b 58 :prefix "calendar-chinese-"
bfcb5172 59 :group 'calendar)
00e3e480 60
8474aac1
GM
61(define-obsolete-variable-alias 'chinese-calendar-time-zone
62 'calendar-chinese-time-zone "23.1")
63
0b41781b 64(defcustom calendar-chinese-time-zone
0808d911
ER
65 '(if (< year 1928)
66 (+ 465 (/ 40.0 60.0))
67 480)
67a9fd0d 68 "Minutes difference between local standard time for Chinese calendar and UTC.
465323b6
GM
69Default is for Beijing. This is an expression in `year' since it changed at
701928-01-01 00:00:00 from UT+7:45:40 to UT+8."
9e935048 71 :type 'sexp
0b41781b 72 :group 'calendar-chinese)
0808d911 73
712f117b
GM
74;; It gets eval'd.
75;;;###autoload
76(put 'calendar-chinese-time-zone 'risky-local-variable t)
8474aac1
GM
77;;;###autoload
78(put 'chinese-calendar-time-zone 'risky-local-variable t)
712f117b 79
8474aac1
GM
80
81(define-obsolete-variable-alias 'chinese-calendar-location-name
82 'calendar-chinese-location-name "23.1")
0b41781b
GM
83
84;; FIXME unused.
85(defcustom calendar-chinese-location-name "Beijing"
08f7d912 86 "Name of location used for calculation of Chinese calendar."
8db540c5 87 :type 'string
0b41781b
GM
88 :group 'calendar-chinese)
89
8474aac1
GM
90(define-obsolete-variable-alias 'chinese-calendar-daylight-time-offset
91 'calendar-chinese-daylight-time-offset "23.1")
0808d911 92
0b41781b 93(defcustom calendar-chinese-daylight-time-offset 0
4785bf2c
GM
94;; The correct value is as follows, but the Chinese calendrical
95;; authorities do NOT use DST in determining astronomical events:
96;; 60
67a9fd0d
GM
97 "Minutes difference between daylight saving and standard time.
98Default is for no daylight saving time."
8db540c5 99 :type 'integer
0b41781b
GM
100 :group 'calendar-chinese)
101
8474aac1
GM
102(define-obsolete-variable-alias 'chinese-calendar-standard-time-zone-name
103 'calendar-chinese-standard-time-zone-name "23.1")
0808d911 104
0b41781b 105(defcustom calendar-chinese-standard-time-zone-name
0808d911
ER
106 '(if (< year 1928)
107 "PMT"
108 "CST")
08f7d912 109 "Abbreviated name of standard time zone used for Chinese calendar.
9e935048
RS
110This is an expression depending on `year' because it changed
111at 1928-01-01 00:00:00 from `PMT' to `CST'."
112 :type 'sexp
0b41781b 113 :group 'calendar-chinese)
0808d911 114
8474aac1
GM
115(define-obsolete-variable-alias 'chinese-calendar-daylight-time-zone-name
116 'calendar-chinese-daylight-time-zone-name "23.1")
0b41781b
GM
117
118(defcustom calendar-chinese-daylight-time-zone-name "CDT"
08f7d912 119 "Abbreviated name of daylight saving time zone used for Chinese calendar."
8db540c5 120 :type 'string
0b41781b
GM
121 :group 'calendar-chinese)
122
8474aac1
GM
123(define-obsolete-variable-alias 'chinese-calendar-daylight-savings-starts
124 'calendar-chinese-daylight-saving-start "23.1")
0808d911 125
0b41781b 126(defcustom calendar-chinese-daylight-saving-start nil
4785bf2c
GM
127;; The correct value is as follows, but the Chinese calendrical
128;; authorities do NOT use DST in determining astronomical events:
129;; '(cond ((< 1986 year) (calendar-nth-named-day 1 0 4 year 10))
130;; ((= 1986 year) '(5 4 1986))
131;; (t nil))
67a9fd0d
GM
132 "Sexp giving the date on which daylight saving time starts.
133Default is for no daylight saving time. See documentation of
8db540c5
RS
134`calendar-daylight-savings-starts'."
135 :type 'sexp
0b41781b 136 :group 'calendar-chinese)
0808d911 137
8474aac1
GM
138(define-obsolete-variable-alias 'chinese-calendar-daylight-savings-ends
139 'calendar-chinese-daylight-saving-end "23.1")
0b41781b
GM
140
141(defcustom calendar-chinese-daylight-saving-end nil
4785bf2c
GM
142;; The correct value is as follows, but the Chinese calendrical
143;; authorities do NOT use DST in determining astronomical events:
144;; '(if (<= 1986 year) (calendar-nth-named-day 1 0 9 year 11))
67a9fd0d
GM
145 "Sexp giving the date on which daylight saving time ends.
146Default is for no daylight saving time. See documentation of
8db540c5
RS
147`calendar-daylight-savings-ends'."
148 :type 'sexp
0b41781b
GM
149 :group 'calendar-chinese)
150
8474aac1
GM
151(define-obsolete-variable-alias 'chinese-calendar-daylight-savings-starts-time
152 'calendar-chinese-daylight-saving-start-time "23.1")
0808d911 153
0b41781b 154(defcustom calendar-chinese-daylight-saving-start-time 0
67a9fd0d
GM
155 "Number of minutes after midnight that daylight saving time starts.
156Default is for no daylight saving time."
8db540c5 157 :type 'integer
0b41781b 158 :group 'calendar-chinese)
0808d911 159
8474aac1
GM
160(define-obsolete-variable-alias 'chinese-calendar-daylight-savings-ends-time
161 'calendar-chinese-daylight-saving-end-time "23.1")
0b41781b
GM
162
163(defcustom calendar-chinese-daylight-saving-end-time 0
67a9fd0d
GM
164 "Number of minutes after midnight that daylight saving time ends.
165Default is for no daylight saving time."
8db540c5 166 :type 'integer
0b41781b
GM
167 :group 'calendar-chinese)
168
8474aac1
GM
169(define-obsolete-variable-alias 'chinese-calendar-celestial-stem
170 'calendar-chinese-celestial-stem "23.1")
0808d911 171
0b41781b 172(defcustom calendar-chinese-celestial-stem
465323b6 173 ["Jia" "Yi" "Bing" "Ding" "Wu" "Ji" "Geng" "Xin" "Ren" "Gui"]
a8774f05 174 "Prefixes used by `calendar-chinese-sexagesimal-name'."
0b41781b 175 :group 'calendar-chinese
a8774f05
GM
176 :type '(vector (string :tag "Jia")
177 (string :tag "Yi")
178 (string :tag "Bing")
179 (string :tag "Ding")
180 (string :tag "Wu")
181 (string :tag "Ji")
182 (string :tag "Geng")
183 (string :tag "Xin")
184 (string :tag "Ren")
185 (string :tag "Gui")))
186
8474aac1
GM
187(define-obsolete-variable-alias 'chinese-calendar-terrestrial-branch
188 'calendar-chinese-terrestrial-branch "23.1")
0b41781b
GM
189
190(defcustom calendar-chinese-terrestrial-branch
465323b6 191 ["Zi" "Chou" "Yin" "Mao" "Chen" "Si" "Wu" "Wei" "Shen" "You" "Xu" "Hai"]
a8774f05 192 "Suffixes used by `calendar-chinese-sexagesimal-name'."
0b41781b 193 :group 'calendar-chinese
a8774f05
GM
194 :type '(vector (string :tag "Zi")
195 (string :tag "Chou")
196 (string :tag "Yin")
197 (string :tag "Mao")
198 (string :tag "Chen")
199 (string :tag "Si")
200 (string :tag "Wu")
201 (string :tag "Wei")
202 (string :tag "Shen")
203 (string :tag "You")
204 (string :tag "Xu")
205 (string :tag "Hai")))
206
207;;; End of user options.
208
465323b6
GM
209
210(defun calendar-chinese-sexagesimal-name (n)
211 "The N-th name of the Chinese sexagesimal cycle.
212N congruent to 1 gives the first name, N congruent to 2 gives the second name,
213..., N congruent to 60 gives the sixtieth name."
214 (format "%s-%s"
0b41781b
GM
215 (aref calendar-chinese-celestial-stem (% (1- n) 10))
216 (aref calendar-chinese-terrestrial-branch (% (1- n) 12))))
bfcb5172 217
0b41781b 218(defun calendar-chinese-zodiac-sign-on-or-after (d)
67a9fd0d 219 "Absolute date of first new Zodiac sign on or after absolute date D.
0808d911 220The Zodiac signs begin when the sun's longitude is a multiple of 30 degrees."
e803eab7 221 (let* ((year (calendar-extract-year (calendar-gregorian-from-absolute d)))
0b41781b 222 (calendar-time-zone (eval calendar-chinese-time-zone)) ; uses year
0808d911 223 (calendar-daylight-time-offset
0b41781b 224 calendar-chinese-daylight-time-offset)
0808d911 225 (calendar-standard-time-zone-name
0b41781b 226 calendar-chinese-standard-time-zone-name)
0808d911 227 (calendar-daylight-time-zone-name
0b41781b
GM
228 calendar-chinese-daylight-time-zone-name)
229 (calendar-daylight-savings-starts
230 calendar-chinese-daylight-saving-start)
0808d911 231 (calendar-daylight-savings-ends
0b41781b 232 calendar-chinese-daylight-saving-end)
0808d911 233 (calendar-daylight-savings-starts-time
0b41781b 234 calendar-chinese-daylight-saving-start-time)
0808d911 235 (calendar-daylight-savings-ends-time
0b41781b 236 calendar-chinese-daylight-saving-end-time))
0808d911 237 (floor
5c645a20 238 (calendar-astro-to-absolute
465323b6 239 (solar-date-next-longitude (calendar-astro-from-absolute d) 30)))))
0808d911 240
0b41781b 241(defun calendar-chinese-new-moon-on-or-after (d)
67a9fd0d 242 "Absolute date of first new moon on or after absolute date D."
e803eab7 243 (let* ((year (calendar-extract-year (calendar-gregorian-from-absolute d)))
0b41781b 244 (calendar-time-zone (eval calendar-chinese-time-zone))
0808d911 245 (calendar-daylight-time-offset
0b41781b 246 calendar-chinese-daylight-time-offset)
0808d911 247 (calendar-standard-time-zone-name
0b41781b 248 calendar-chinese-standard-time-zone-name)
0808d911 249 (calendar-daylight-time-zone-name
0b41781b
GM
250 calendar-chinese-daylight-time-zone-name)
251 (calendar-daylight-savings-starts
252 calendar-chinese-daylight-saving-start)
0808d911 253 (calendar-daylight-savings-ends
0b41781b 254 calendar-chinese-daylight-saving-end)
0808d911 255 (calendar-daylight-savings-starts-time
0b41781b 256 calendar-chinese-daylight-saving-start-time)
0808d911 257 (calendar-daylight-savings-ends-time
0b41781b 258 calendar-chinese-daylight-saving-end-time))
0808d911 259 (floor
5c645a20 260 (calendar-astro-to-absolute
465323b6 261 (lunar-new-moon-on-or-after (calendar-astro-from-absolute d))))))
0031509c 262
0b41781b 263(defun calendar-chinese-month-list (start end)
0031509c
ER
264 "List of starting dates of Chinese months from START to END."
265 (if (<= start end)
0b41781b 266 (let ((new-moon (calendar-chinese-new-moon-on-or-after start)))
0031509c 267 (if (<= new-moon end)
00e3e480 268 (cons new-moon
0b41781b 269 (calendar-chinese-month-list (1+ new-moon) end))))))
0031509c 270
0b41781b 271(defun calendar-chinese-number-months (list start)
465323b6
GM
272 "Assign month numbers to the lunar months in LIST, starting with START.
273Numbers are assigned sequentially, START, START+1, ..., 11, with
274half numbers used for leap months. First and last months of list
275are never leap months."
276 (when list
277 (cons (list start (car list)) ; first month
278 ;; Remaining months.
279 (if (zerop (- 12 start (length list)))
280 ;; List is too short for a leap month.
0b41781b 281 (calendar-chinese-number-months (cdr list) (1+ start))
465323b6 282 (if (and (cddr list) ; at least two more months...
706531d9 283 (<= (nth 2 list)
0b41781b
GM
284 (calendar-chinese-zodiac-sign-on-or-after
285 (cadr list))))
465323b6
GM
286 ;; Next month is a leap month.
287 (cons (list (+ start 0.5) (cadr list))
0b41781b 288 (calendar-chinese-number-months (cddr list) (1+ start)))
465323b6 289 ;; Next month is not a leap month.
0b41781b 290 (calendar-chinese-number-months (cdr list) (1+ start)))))))
465323b6 291
0b41781b 292(defun calendar-chinese-compute-year (y)
0031509c 293 "Compute the structure of the Chinese year for Gregorian year Y.
00e3e480 294The result is a list of pairs (i d), where month i begins on absolute date d,
0031509c
ER
295of the Chinese months from the Chinese month following the solstice in
296Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y."
0b41781b 297 (let* ((next-solstice (calendar-chinese-zodiac-sign-on-or-after
0031509c
ER
298 (calendar-absolute-from-gregorian
299 (list 12 15 y))))
0b41781b
GM
300 (list (calendar-chinese-month-list
301 (1+ (calendar-chinese-zodiac-sign-on-or-after
302 (calendar-absolute-from-gregorian
303 (list 12 15 (1- y)))))
304 next-solstice))
305 (next-sign (calendar-chinese-zodiac-sign-on-or-after (car list))))
0031509c 306 (if (= (length list) 12)
4785bf2c 307 ;; No room for a leap month, just number them 12, 1, 2, ..., 11.
00e3e480 308 (cons (list 12 (car list))
0b41781b 309 (calendar-chinese-number-months (cdr list) 1))
4785bf2c
GM
310 ;; Now we can assign numbers to the list for y.
311 ;; The first month or two are special.
465323b6 312 (if (or (> (car list) next-sign) (>= next-sign (cadr list)))
4785bf2c 313 ;; First month on list is a leap month, second is not.
00e3e480 314 (append (list (list 11.5 (car list))
465323b6 315 (list 12 (cadr list)))
0b41781b 316 (calendar-chinese-number-months (cddr list) 1))
4785bf2c 317 ;; First month on list is not a leap month.
00e3e480 318 (append (list (list 12 (car list)))
0b41781b 319 (if (>= (calendar-chinese-zodiac-sign-on-or-after (cadr list))
465323b6 320 (nth 2 list))
4785bf2c 321 ;; Second month on list is a leap month.
465323b6 322 (cons (list 12.5 (cadr list))
0b41781b 323 (calendar-chinese-number-months (cddr list) 1))
4785bf2c 324 ;; Second month on list is not a leap month.
0b41781b 325 (calendar-chinese-number-months (cdr list) 1)))))))
0031509c 326
0b41781b 327(defvar calendar-chinese-year-cache
465323b6 328 ;; Maintainers: delete existing value, position point at start of
0b41781b 329 ;; empty line, then call M-: (calendar-chinese-year-cache-init N)
ba579ea6 330 '((2005 (12 731956) (1 731986) (2 732015) (3 732045) (4 732074) (5 732104)
465323b6
GM
331 (6 732133) (7 732163) (8 732193) (9 732222) (10 732252) (11 732281))
332 (2006 (12 732311) (1 732340) (2 732370) (3 732399) (4 732429) (5 732458)
333 (6 732488) (7 732517) (7.5 732547) (8 732576) (9 732606) (10 732636)
334 (11 732665))
335 (2007 (12 732695) (1 732725) (2 732754) (3 732783) (4 732813) (5 732842)
336 (6 732871) (7 732901) (8 732930) (9 732960) (10 732990) (11 733020))
337 (2008 (12 733049) (1 733079) (2 733109) (3 733138) (4 733167) (5 733197)
338 (6 733226) (7 733255) (8 733285) (9 733314) (10 733344) (11 733374))
339 (2009 (12 733403) (1 733433) (2 733463) (3 733493) (4 733522) (5 733551)
340 (5.5 733581) (6 733610) (7 733639) (8 733669) (9 733698) (10 733728)
341 (11 733757))
342 (2010 (12 733787) (1 733817) (2 733847) (3 733876) (4 733906) (5 733935)
343 (6 733965) (7 733994) (8 734023) (9 734053) (10 734082) (11 734112))
344 (2011 (12 734141) (1 734171) (2 734201) (3 734230) (4 734260) (5 734290)
345 (6 734319) (7 734349) (8 734378) (9 734407) (10 734437) (11 734466))
346 (2012 (12 734496) (1 734525) (2 734555) (3 734584) (4 734614) (4.5 734644)
347 (5 734673) (6 734703) (7 734732) (8 734762) (9 734791) (10 734821)
348 (11 734850))
349 (2013 (12 734880) (1 734909) (2 734939) (3 734968) (4 734998) (5 735027)
350 (6 735057) (7 735087) (8 735116) (9 735146) (10 735175) (11 735205))
351 (2014 (12 735234) (1 735264) (2 735293) (3 735323) (4 735352) (5 735382)
352 (6 735411) (7 735441) (8 735470) (9 735500) (9.5 735530) (10 735559)
353 (11 735589))
354 (2015 (12 735618) (1 735648) (2 735677) (3 735707) (4 735736) (5 735765)
355 (6 735795) (7 735824) (8 735854) (9 735884) (10 735914) (11 735943))
356 (2016 (12 735973) (1 736002) (2 736032) (3 736061) (4 736091) (5 736120)
357 (6 736149) (7 736179) (8 736208) (9 736238) (10 736268) (11 736297))
358 (2017 (12 736327) (1 736357) (2 736386) (3 736416) (4 736445) (5 736475)
359 (6 736504) (6.5 736533) (7 736563) (8 736592) (9 736622) (10 736651)
360 (11 736681))
361 (2018 (12 736711) (1 736741) (2 736770) (3 736800) (4 736829) (5 736859)
362 (6 736888) (7 736917) (8 736947) (9 736976) (10 737006) (11 737035))
363 (2019 (12 737065) (1 737095) (2 737125) (3 737154) (4 737184) (5 737213)
364 (6 737243) (7 737272) (8 737301) (9 737331) (10 737360) (11 737389))
365 (2020 (12 737419) (1 737449) (2 737478) (3 737508) (4 737538) (4.5 737568)
366 (5 737597) (6 737627) (7 737656) (8 737685) (9 737715) (10 737744)
ba579ea6
GM
367 (11 737774))
368 (2021 (12 737803) (1 737833) (2 737862) (3 737892) (4 737922) (5 737951)
369 (6 737981) (7 738010) (8 738040) (9 738069) (10 738099) (11 738128))
370 (2022 (12 738158) (1 738187) (2 738217) (3 738246) (4 738276) (5 738305)
371 (6 738335) (7 738365) (8 738394) (9 738424) (10 738453) (11 738483))
372 (2023 (12 738512) (1 738542) (2 738571) (2.5 738601) (3 738630) (4 738659)
373 (5 738689) (6 738719) (7 738748) (8 738778) (9 738808) (10 738837)
374 (11 738867))
375 (2024 (12 738896) (1 738926) (2 738955) (3 738985) (4 739014) (5 739043)
376 (6 739073) (7 739102) (8 739132) (9 739162) (10 739191) (11 739221))
377 (2025 (12 739251) (1 739280) (2 739310) (3 739339) (4 739369) (5 739398)
378 (6 739427) (6.5 739457) (7 739486) (8 739516) (9 739545) (10 739575)
379 (11 739605)))
465323b6
GM
380 "Alist of Chinese year structures as determined by `chinese-year'.
381The default can be nil, but some values are precomputed for efficiency.")
382
0b41781b 383(defun calendar-chinese-year (y)
465323b6
GM
384 "The structure of the Chinese year for Gregorian year Y.
385The result is a list of pairs (i d), where month i begins on absolute date d,
386of the Chinese months from the Chinese month following the solstice in
387Gregorian year Y-1 to the Chinese month of the solstice of Gregorian year Y.
0b41781b
GM
388The list is cached in `calendar-chinese-year-cache' for further use."
389 (let ((list (cdr (assoc y calendar-chinese-year-cache))))
465323b6 390 (or list
0b41781b
GM
391 (setq list (calendar-chinese-compute-year y)
392 calendar-chinese-year-cache (append calendar-chinese-year-cache
465323b6
GM
393 (list (cons y list)))))
394 list))
395
396;; Maintainer use.
0b41781b
GM
397(defun calendar-chinese-year-cache-init (year)
398 "Insert an initialization value for `calendar-chinese-year-cache' after point.
465323b6
GM
399Computes values for 10 years either side of YEAR."
400 (setq year (- year 10))
0b41781b 401 (let (calendar-chinese-year-cache end)
465323b6
GM
402 (save-excursion
403 (insert "'(")
404 (dotimes (n 21)
0b41781b
GM
405 (princ (cons year (calendar-chinese-compute-year year))
406 (current-buffer))
debf91fd
GM
407 (insert (if (= n 20) ")" "\n"))
408 (setq year (1+ year)))
465323b6
GM
409 (setq end (point)))
410 (save-excursion
411 ;; fill-column -/+ 5.
412 (while (and (< (point) end)
413 (re-search-forward "^.\\{65,75\\})" end t))
414 (delete-char 1)
415 (insert "\n")))
416 (indent-region (point) end)))
417
0b41781b 418(defun calendar-chinese-to-absolute (date)
0808d911 419 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
465323b6
GM
420DATE is a Chinese date (cycle year month day). The Gregorian date
421Sunday, December 31, 1 BC is imaginary."
0808d911 422 (let* ((cycle (car date))
465323b6
GM
423 (year (cadr date))
424 (month (nth 2 date))
425 (day (nth 3 date))
4785bf2c
GM
426 (g-year (+ (* (1- cycle) 60) ; years in prior cycles
427 (1- year) ; prior years this cycle
428 -2636))) ; years before absolute date 0
429 (+ (1- day) ; prior days this month
465323b6 430 (cadr ; absolute date of start of this month
0b41781b
GM
431 (assoc month (append (memq (assoc 1 (calendar-chinese-year g-year))
432 (calendar-chinese-year g-year))
433 (calendar-chinese-year (1+ g-year))))))))
434
435(define-obsolete-function-alias 'calendar-absolute-from-chinese
436 'calendar-chinese-to-absolute "23.1")
0808d911
ER
437
438(defun calendar-chinese-from-absolute (date)
439 "Compute Chinese date (cycle year month day) corresponding to absolute DATE.
440The absolute date is the number of days elapsed since the (imaginary)
441Gregorian date Sunday, December 31, 1 BC."
e803eab7 442 (let* ((g-year (calendar-extract-year
0031509c 443 (calendar-gregorian-from-absolute date)))
00e3e480 444 (c-year (+ g-year 2695))
0b41781b
GM
445 (list (append (calendar-chinese-year (1- g-year))
446 (calendar-chinese-year g-year)
447 (calendar-chinese-year (1+ g-year)))))
706531d9 448 (while (<= (cadr (cadr list)) date)
4785bf2c
GM
449 ;; The first month on the list is in Chinese year c-year.
450 ;; Date is on or after start of second month on list...
706531d9 451 (if (= 1 (caar (cdr list)))
4785bf2c 452 ;; Second month on list is a new Chinese year...
00e3e480 453 (setq c-year (1+ c-year)))
4785bf2c 454 ;; ...so first month on list is of no interest.
0031509c 455 (setq list (cdr list)))
00e3e480 456 (list (/ (1- c-year) 60)
c97663f6
GM
457 ;; Remainder of c-year/60 with 60 instead of 0.
458 (1+ (mod (1- c-year) 60))
706531d9
GM
459 (caar list)
460 (1+ (- date (cadr (car list)))))))
0808d911 461
e803eab7 462;; Bound in calendar-generate.
465323b6
GM
463(defvar displayed-month)
464(defvar displayed-year)
465
1d0c7fdf 466;;;###holiday-autoload
0808d911 467(defun holiday-chinese-new-year ()
0d16be53
GM
468 "Date of Chinese New Year, if visible in calendar.
469Returns (((MONTH DAY YEAR) TEXT)), where the date is Gregorian."
0808d911 470 (let ((m displayed-month)
d4ab8ae3
GM
471 (y displayed-year)
472 chinese-new-year)
0d16be53
GM
473 ;; In the Gregorian calendar, CNY falls between Jan 21 and Feb 20.
474 ;; Jan is visible if displayed-month = 12, 1, 2; Feb if d-m = 1, 2, 3.
475 ;; If we shift the calendar forward one month, we can do a
c8b69b0a 476 ;; one-sided test, namely: d-m <= 4 means CNY might be visible.
e803eab7 477 (calendar-increment-month m y 1) ; shift forward a month
d4ab8ae3 478 (and (< m 5)
0b41781b
GM
479 (calendar-date-is-visible-p
480 (setq chinese-new-year
481 (calendar-gregorian-from-absolute
482 (cadr (assoc 1 (calendar-chinese-year y))))))
d4ab8ae3
GM
483 (list
484 (list chinese-new-year
485 (format "Chinese New Year (%s)"
486 (calendar-chinese-sexagesimal-name (+ y 57))))))))
0808d911 487
418c2f01
GM
488;;;###holiday-autoload
489(defun holiday-chinese-qingming ()
490 "Date of Chinese Qingming Festival, if visible in calendar.
491Returns (((MONTH DAY YEAR) TEXT)), where the date is Gregorian."
492 (when (memq displayed-month '(3 4 5)) ; is April visible?
493 (list (list (calendar-gregorian-from-absolute
494 ;; 15 days after Vernal Equinox.
495 (+ 15
496 (calendar-chinese-zodiac-sign-on-or-after
497 (calendar-absolute-from-gregorian
498 (list 3 15 displayed-year)))))
499 "Qingming Festival"))))
500
501;;;###holiday-autoload
502(defun holiday-chinese-winter-solstice ()
503 "Date of Chinese winter solstice, if visible in calendar.
504Returns (((MONTH DAY YEAR) TEXT)), where the date is Gregorian."
505 (when (memq displayed-month '(11 12 1)) ; is December visible?
506 (list (list (calendar-gregorian-from-absolute
507 (calendar-chinese-zodiac-sign-on-or-after
508 (calendar-absolute-from-gregorian
509 (list 12 15 (if (eq displayed-month 1)
510 (1- displayed-year)
511 displayed-year)))))
512 "Winter Solstice Festival"))))
513
514;;;###holiday-autoload
515(defun holiday-chinese (month day string)
516 "Holiday on Chinese MONTH, DAY called STRING.
517If MONTH, DAY (Chinese) is visible, returns the corresponding
518Gregorian date as the list (((month day year) STRING)).
519Returns nil if it is not visible in the current calendar window."
cca065d8
GM
520 (let ((date
521 (calendar-gregorian-from-absolute
522 ;; A basic optimization. Chinese year can only change if
523 ;; Jan or Feb are visible. FIXME can we do more?
524 (if (memq displayed-month '(12 1 2 3))
525 ;; This is calendar-nongregorian-visible-p adapted for
526 ;; the form of chinese dates: (cycle year month day) as
527 ;; opposed to (month day year).
528 (let* ((m1 displayed-month)
529 (y1 displayed-year)
530 (m2 displayed-month)
531 (y2 displayed-year)
532 ;; Absolute date of first/last dates in calendar window.
533 (start-date (progn
534 (calendar-increment-month m1 y1 -1)
535 (calendar-absolute-from-gregorian
536 (list m1 1 y1))))
537 (end-date (progn
538 (calendar-increment-month m2 y2 1)
539 (calendar-absolute-from-gregorian
540 (list m2 (calendar-last-day-of-month m2 y2)
541 y2))))
542 ;; Local date of first/last date in calendar window.
543 (local-start (calendar-chinese-from-absolute start-date))
544 (local-end (calendar-chinese-from-absolute end-date))
545 ;; When Chinese New Year is visible on the far
546 ;; right of the calendar, what is the earliest
547 ;; Chinese month in the previous year that might
548 ;; still visible? This test doesn't have to be precise.
549 (local (if (< month 10) local-end local-start))
550 (cycle (car local))
551 (year (cadr local)))
552 (calendar-chinese-to-absolute (list cycle year month day)))
553 ;; Simple form for when new years are not visible.
554 (+ (cadr (assoc month (calendar-chinese-year displayed-year)))
555 (1- day))))))
418c2f01
GM
556 (if (calendar-date-is-visible-p date)
557 (list (list date string)))))
558
1d0c7fdf 559;;;###cal-autoload
0808d911
ER
560(defun calendar-chinese-date-string (&optional date)
561 "String of Chinese date of Gregorian DATE.
562Defaults to today's date if DATE is not given."
563 (let* ((a-date (calendar-absolute-from-gregorian
564 (or date (calendar-current-date))))
565 (c-date (calendar-chinese-from-absolute a-date))
566 (cycle (car c-date))
465323b6
GM
567 (year (cadr c-date))
568 (month (nth 2 c-date))
569 (day (nth 3 c-date))
0b41781b 570 (this-month (calendar-chinese-to-absolute
0808d911 571 (list cycle year month 1)))
0b41781b 572 (next-month (calendar-chinese-to-absolute
67d80173
ER
573 (list (if (= year 60) (1+ cycle) cycle)
574 (if (= (floor month) 12) (1+ year) year)
c97663f6
GM
575 ;; Remainder of (1+(floor month))/12, with
576 ;; 12 instead of 0.
577 (1+ (mod (floor month) 12))
bc4f7f3d 578 1))))
b4cb70c4 579 (format "Cycle %s, year %s (%s), %smonth %s%s, day %s (%s)"
0808d911 580 cycle
ba2a1fb0 581 year (calendar-chinese-sexagesimal-name year)
0808d911
ER
582 (if (not (integerp month))
583 "second "
584 (if (< 30 (- next-month this-month))
585 "first "
586 ""))
0031509c 587 (floor month)
b4cb70c4 588 (if (integerp month)
ba2a1fb0 589 (format " (%s)" (calendar-chinese-sexagesimal-name
cc6e0522 590 (+ (* 12 year) month 50)))
b4cb70c4 591 "")
ba2a1fb0 592 day (calendar-chinese-sexagesimal-name (+ a-date 15)))))
b4cb70c4 593
1d0c7fdf 594;;;###cal-autoload
0b41781b 595(defun calendar-chinese-print-date ()
0808d911
ER
596 "Show the Chinese date equivalents of date."
597 (interactive)
598 (message "Computing Chinese date...")
599 (message "Chinese date: %s"
600 (calendar-chinese-date-string (calendar-cursor-to-date t))))
601
0b41781b
GM
602(define-obsolete-function-alias 'calendar-print-chinese-date
603 'calendar-chinese-print-date "23.1")
604
605(defun calendar-chinese-months-to-alist (l)
465323b6
GM
606 "Make list of months L into an assoc list."
607 (and l (car l)
608 (if (and (cdr l) (cadr l))
609 (if (= (car l) (floor (cadr l)))
610 (append
611 (list (cons (format "%s (first)" (car l)) (car l))
612 (cons (format "%s (second)" (car l)) (cadr l)))
0b41781b 613 (calendar-chinese-months-to-alist (cddr l)))
465323b6 614 (append
d92bcf94 615 (list (cons (number-to-string (car l)) (car l)))
0b41781b 616 (calendar-chinese-months-to-alist (cdr l))))
d92bcf94 617 (list (cons (number-to-string (car l)) (car l))))))
465323b6 618
0b41781b 619(defun calendar-chinese-months (c y)
465323b6
GM
620 "A list of the months in cycle C, year Y of the Chinese calendar."
621 (memq 1 (append
622 (mapcar (lambda (x)
623 (car x))
e803eab7 624 (calendar-chinese-year (calendar-extract-year
0b41781b
GM
625 (calendar-gregorian-from-absolute
626 (calendar-chinese-to-absolute
627 (list c y 1 1))))))
465323b6
GM
628 (mapcar (lambda (x)
629 (if (> (car x) 11) (car x)))
e803eab7 630 (calendar-chinese-year (calendar-extract-year
0b41781b
GM
631 (calendar-gregorian-from-absolute
632 (calendar-chinese-to-absolute
633 (list (if (= y 60) (1+ c) c)
634 (if (= y 60) 1 y)
635 1 1)))))))))
465323b6 636
1d0c7fdf 637;;;###cal-autoload
0b41781b 638(defun calendar-chinese-goto-date (date &optional noecho)
0808d911 639 "Move cursor to Chinese date DATE.
465323b6 640Echo Chinese date unless NOECHO is non-nil."
0808d911
ER
641 (interactive
642 (let* ((c (calendar-chinese-from-absolute
465323b6 643 (calendar-absolute-from-gregorian (calendar-current-date))))
0808d911 644 (cycle (calendar-read
0031509c 645 "Chinese calendar cycle number (>44): "
bfcb5172 646 (lambda (x) (> x 44))
d92bcf94 647 (number-to-string (car c))))
0808d911 648 (year (calendar-read
0031509c 649 "Year in Chinese cycle (1..60): "
bfcb5172 650 (lambda (x) (and (<= 1 x) (<= x 60)))
d92bcf94 651 (number-to-string (cadr c))))
0b41781b
GM
652 (month-list (calendar-chinese-months-to-alist
653 (calendar-chinese-months cycle year)))
0031509c
ER
654 (month (cdr (assoc
655 (completing-read "Chinese calendar month: "
656 month-list nil t)
657 month-list)))
658 (last (if (= month
465323b6
GM
659 (nth 2
660 (calendar-chinese-from-absolute
661 (+ 29
0b41781b 662 (calendar-chinese-to-absolute
465323b6 663 (list cycle year month 1))))))
0031509c
ER
664 30
665 29))
666 (day (calendar-read
667 (format "Chinese calendar day (1-%d): " last)
bfcb5172 668 (lambda (x) (and (<= 1 x) (<= x last))))))
0808d911
ER
669 (list (list cycle year month day))))
670 (calendar-goto-date (calendar-gregorian-from-absolute
0b41781b
GM
671 (calendar-chinese-to-absolute date)))
672 (or noecho (calendar-chinese-print-date)))
673
674(define-obsolete-function-alias 'calendar-goto-chinese-date
675 'calendar-chinese-goto-date "23.1")
0808d911 676
d7af270b
GM
677(defvar date)
678
8c34d83e 679;; To be called from diary-list-sexp-entries, where DATE is bound.
1d0c7fdf 680;;;###diary-autoload
0808d911
ER
681(defun diary-chinese-date ()
682 "Chinese calendar equivalent of date diary entry."
683 (format "Chinese date: %s" (calendar-chinese-date-string date)))
684
bbdcf64f
LL
685;;;; diary support
686
687(autoload 'calendar-mark-1 "diary-lib")
688(autoload 'diary-mark-entries-1 "diary-lib")
689(autoload 'diary-list-entries-1 "diary-lib")
690(autoload 'diary-insert-entry-1 "diary-lib")
691(autoload 'diary-date-display-form "diary-lib")
692(autoload 'diary-make-date "diary-lib")
693(autoload 'diary-ordinal-suffix "diary-lib")
694(defvar diary-sexp-entry-symbol)
695(defvar entry) ;used by `diary-chinese-anniversary'
696
697(defvar calendar-chinese-month-name-array
698 ["正月" "二月" "三月" "四月" "五月" "六月"
699 "七月" "八月" "九月" "十月" "冬月" "臘月"])
700
701;;; NOTE: In the diary the cycle and year of a Chinese date is
702;;; combined using this formula: (+ (* cycle 100) year).
703;;;
704;;; These two functions convert to and back from this representation.
705(defun calendar-chinese-from-absolute-for-diary (date)
706 (pcase-let ((`(,c ,y ,m ,d) (calendar-chinese-from-absolute date)))
707 (list m d (+ (* c 100) y))))
708
709(defun calendar-chinese-to-absolute-for-diary (date)
710 (pcase-let ((`(,m ,d ,y) date))
711 (calendar-chinese-to-absolute
712 (list (floor y 100) (mod y 100) m d))))
713
714(defun calendar-chinese-mark-date-pattern (month day year &optional color)
715 (calendar-mark-1 month day year
716 #'calendar-chinese-from-absolute-for-diary
717 #'calendar-chinese-to-absolute-for-diary
718 color))
719
720;;;###cal-autoload
721(defun diary-chinese-mark-entries ()
722 "Mark days in the calendar window that have Chinese date diary entries.
723Marks each entry in `diary-file' (or included files) visible in the calendar
724window. See `diary-chinese-list-entries' for more information.
725
726This function is provided for use with `diary-nongregorian-marking-hook'."
727 (diary-mark-entries-1 #'calendar-chinese-mark-date-pattern
728 calendar-chinese-month-name-array
729 diary-chinese-entry-symbol
730 #'calendar-chinese-from-absolute-for-diary))
731
732;;;###cal-autoload
733(defun diary-chinese-list-entries ()
734 "Add any Chinese date entries from the diary file to `diary-entries-list'.
735Chinese date diary entries must be prefixed by `diary-chinese-entry-symbol'
736\(normally a `C'). The same `diary-date-forms' govern the style
737of the Chinese calendar entries. If a Chinese date diary entry begins with
738`diary-nonmarking-symbol', the entry will appear in the diary listing,
739but will not be marked in the calendar.
740
741This function is provided for use with `diary-nongregorian-listing-hook'."
742 (diary-list-entries-1 calendar-chinese-month-name-array
743 diary-chinese-entry-symbol
744 #'calendar-chinese-from-absolute-for-diary))
745
746;;;###cal-autoload
747(defun diary-chinese-anniversary (month day &optional year mark)
748 "Like `diary-anniversary' (which see) but accepts Chinese date."
749 (pcase-let* ((ddate (diary-make-date month day year))
750 (`(,dc ,dy ,dm ,dd) ;diary chinese date
751 (if year
752 (calendar-chinese-from-absolute
753 (calendar-chinese-to-absolute-for-diary ddate))
754 (list nil nil (calendar-extract-month ddate)
755 (calendar-extract-day ddate))))
756 (`(,cc ,cy ,cm ,cd) ;current chinese date
757 (calendar-chinese-from-absolute
758 (calendar-absolute-from-gregorian date)))
759 (diff (if (and dc dy)
760 (+ (* 60 (- cc dc)) (- cy dy))
761 100)))
762 (and (> diff 0) (= dm cm) (= dd cd)
763 (cons mark (format entry diff (diary-ordinal-suffix diff))))))
764
765;;;###cal-autoload
766(defun diary-chinese-insert-anniversary-entry (&optional arg)
767 "Insert an anniversary diary entry for the Chinese date at point.
768Prefix argument ARG makes the entry nonmarking."
769 (interactive "P")
770 (let ((calendar-date-display-form (diary-date-display-form)))
771 (diary-make-entry
772 (format "%s(diary-chinese-anniversary %s)"
773 diary-sexp-entry-symbol
774 (calendar-date-string
775 (calendar-chinese-from-absolute-for-diary
776 (calendar-absolute-from-gregorian (calendar-cursor-to-date t)))))
777 arg)))
778
779;;;###cal-autoload
780(defun diary-chinese-insert-entry (&optional arg)
781 "Insert a diary entry for the Chinese date at point."
782 (interactive "P")
783 (diary-insert-entry-1 nil arg calendar-chinese-month-name-array
784 diary-chinese-entry-symbol
785 #'calendar-chinese-from-absolute-for-diary))
786
787;;;###cal-autoload
788(defun diary-chinese-insert-monthly-entry (&optional arg)
789 "Insert a monthly diary entry for the Chinese date at point."
790 (interactive "P")
791 (diary-insert-entry-1 'monthly arg calendar-chinese-month-name-array
792 diary-chinese-entry-symbol
793 #'calendar-chinese-from-absolute-for-diary))
794
795;;;###cal-autoload
796(defun diary-chinese-insert-yearly-entry (&optional arg)
797 "Insert a yearly diary entry for the Chinese date at point."
798 (interactive "P")
799 (diary-insert-entry-1 'yearly arg calendar-chinese-month-name-array
800 diary-chinese-entry-symbol
801 #'calendar-chinese-from-absolute-for-diary))
802
c86b6d44 803(provide 'cal-china)
0808d911 804
c86b6d44 805;;; cal-china.el ends here