第一个用 Emacs Lisp 实现的小功能。
Lisp 诞生于 1958 年,发明人是 John McCarthy,是目前仍在使用的第二古老的高级语言(最早的是 Fortran,诞生于 1957 年)。
Lisp 来源于 LISt Processing
(有调侃说是 Lots of Isolated Stupid Parentheses
),这个古老的语言和目前主流语言例如 C++,Python 和 Java 等有显著区别,下面是一段代码示例:
1 2 3 4 5 6 7 8 9 (defun cal-china-x-winter-solstice-date (date) "Return winter solstice(冬至) date in Gregorian form. If MONTH = 12, return current year's date Else return last year's date" (let* ((cyear (if (= (calendar-extract-month date) 12) (calendar-extract-year date) (1- (calendar-extract-year date))))) (car (rassoc '"冬至" (cal-china-x-solar-term-alist-new cyear)))))
Emacs
也许是用 Lisp 编写的最著名的软件了。Emacs
自带了日历功能,cal-china-x
扩展实现了阴历、节气、生肖等的计算,但没有实现数九天和伏天的计算功能。正好借这个机会实现下功能,练练手。
数九天计算 数九天的计算相对比较简单。从冬至当天开始算一九第一天,每过九天加一九,一直到九九第九天。
思路也比较清楚,对于某一个 date
,先计算其与冬至的天数差。由于冬至始终在阳历的十二月,因此可以分情况讨论:
阳历非十二月,计算与去年冬至的天数差。
阳历为十二月,计算与今年冬至的天数差。此时若 date
在冬至前则差为负数,但负数情况可以不管,因为不属于数九天。
如果天数差在 0~80 天内,则需要显示数九天,具体显示格式为一九(1)
,整个代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (defconst cal-china-x-nine-characters ["一九" "二九" "三九" "四九" "五九" "六九" "七九" "八九" "九九" ]) (defun cal-china-x-winter-solstice-date (date ) "Return winter solstice(冬至) date in Gregorian form. If MONTH = 12, return current year's date Else return last year's date" (let* ((cyear (if (= (calendar-extract-month date) 12 ) (calendar-extract-year date) (1 - (calendar-extract-year date))))) (car (rassoc '"冬至" (cal-china-x-solar-term-alist-new cyear))))) (defun winter-solstice-day-diff (date ) (cal-china-x-days-diff date (cal-china-x-winter-solstice-date date))) (defun cal-china-x-get-several-nines-string (date ) (let ((daygap (winter-solstice-day-diff date))) (if (or (< daygap 0 ) (> daygap 80 )) "" (concat (aref cal-china-x-nine-characters (/ daygap 9 )) "(" (number-to-string (1 + (% daygap 9 ))) ")" ))))
伏天计算 民间有“头伏饺子二伏面,三伏烙饼摊鸡蛋”的习俗,看到这么多面食可以判断这基本是华北、黄淮一带的风俗。伏天计算比较复杂,原因是涉及到两个节气(夏至和立秋),以及每日的干支。具体计算方法是:
从夏至开始,依照干支纪日的排列,第3个庚日出现时,为初伏第一天(夏至三庚数头伏),第4个庚日为中伏第一天,立秋后第1个庚日为末伏第一天。当夏至与立秋之间出现4个庚日时中伏为10天,出现5个庚日则为20天。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 (defun cal-china-x-solar-term-date (date solar-term) "Return solar-term date in Gregorian form." (let* ((cyear (calendar-extract-year date))) (car (rassoc solar-term (cal-china-x-solar-term-alist-new cyear))))) (defun cal-china-x-chinese-day-celestial-stem-number (date ) "String of Chinese date of Gregorian DATE. Defaults to today's date if DATE is not given." (let* ((a-date (calendar-absolute-from-gregorian date))) (% (+ a-date 15 ) 10 ))) (defun cal-china-x-solar-term-celestical-stem (date solar-term) (cal-china-x-chinese-day-celestial-stem-number (cal-china-x-solar-term-date date solar-term))) (defun cal-china-x-day-diff-from-solar-term (date solar-term) (let ((ss-stem (- 7 (cal-china-x-solar-term-celestical-stem date solar-term)))) (if (< ss-stem 0 ) (+ ss-stem 10 ) ss-stem))) (defun cal-china-x-chufu-date (date ) (let* ((ss-date (cal-china-x-solar-term-date date "夏至" )) (ss-year (calendar-extract-year ss-date)) (ss-day (calendar-extract-day ss-date)) (day-diff (+ 20 (cal-china-x-day-diff-from-solar-term date "夏至" ))) (chufu-day (- day-diff (- 30 ss-day)))) (list 7 chufu-day ss-year))) (defun cal-china-x-zhongfu-date (date ) (list 7 (+ (calendar-extract-day (cal-china-x-chufu-date date)) 10 ) (calendar-extract-year date))) (defun cal-china-x-mofu-date (date ) (let* ((ss-date (cal-china-x-solar-term-date date "立秋" )) (ss-year (calendar-extract-year ss-date)) (ss-day (calendar-extract-day ss-date)) (day-diff (cal-china-x-day-diff-from-solar-term date "立秋" )) (mofu-day (+ day-diff ss-day))) (list 8 mofu-day ss-year))) (defun cal-china-x-get-futian-string (date ) (let* ((chufu (cal-china-x-chufu-date date)) (zhongfu (cal-china-x-zhongfu-date date)) (mofu (cal-china-x-mofu-date date)) (chufu-gap (cal-china-x-days-diff date chufu)) (zhongfu-gap (cal-china-x-days-diff date zhongfu)) (mofu-gap (cal-china-x-days-diff date mofu)) ) (if (or (< chufu-gap 0 ) (> mofu-gap 9 )) "" (if (and (>= chufu-gap 0 ) (< zhongfu-gap 0 )) (concat "初伏(" (number-to-string (1 + chufu-gap)) ")" ) (if (and (>= zhongfu-gap 0 ) (< mofu-gap 0 )) (concat "中伏(" (number-to-string (1 + zhongfu-gap)) ")" ) (concat "末伏(" (number-to-string (1 + mofu-gap)) ")" ))))))
注意如果夏至当天是庚日,则算第一个庚日,相当于初伏在夏至20天之后。
另外,cal-china-x
中月和日的天干地支计算有点问题,需要用 Emacs
自带的计算。