Emacs 中的时间计算
我每天都要面对 Emacs 好多个小时,也逐渐熟悉了它的许多特性,其中之一就是时间的计算。我们可以在 Python 中很方便地使用 time
模块进行日期计算,新的 C++11 中引入的 std::chrono
让时间计算也很简洁,当然,这些对于成熟的 Emacs Lisp 来说,更不是问题。
时间包含两部分内容,一个是时间长度——时、分、秒,一个是时间点——某年某月某日。如果要编写一段小程序,在每天可以自动显示儿子成长的时间,就需要把他出生的时间点作为参数,自动提取当前时间进行计算即可。如何实现呢?
Emacs Lisp 中的时间结构如下:
`(SEC-HIGH SEC-LOW MICROSEC PICOSEC)
后面的两个值可依次省略。浮点时间以秒为单位,计算公式如下:
- t =
SEC-HIGH
* 2**16 +SEC-LOW
+MICROSEC
* 1e-6 +PICOSEC
* 1e-9
Emacs Lisp 中有关时间操作的基本函数有:
current-time-string &optional time-value
current-time
float-time &optional time-value
current-time-zone &optional time-value
Emacs 中进行时间计算的基本函数:
decode-time &optional time
– 时间编码,将 time 值转换成历法信息。encode-time seconds minutes hour day month year &optional zone
– 时间解码,与decode-time
功能相反。date-to-time string
format-time-string format-string &optional time universal
, libcseconds-to-time seconds
format-seconds format-string seconds
对于日历计算,在 Emacs 内部转换始终使用格里高利历,历法日期包含九项内容:
(SECONDS MINUTES HOUR DAY MONTH YEAR DOW DST ZONE)
其中:
SECONDS
,MINUTES
,HOUR
,DAY
,MONTH
,YEAR
意思不用多说了。DOW
, 指的是 the day of week, 即每周的第几天,其中 0 表示周日。DST
, 即 daylight saving time, 夏令时修正ZONE
, 指的是时区,即与格林威治时间相关的秒数。
上述内容除了 DAY
与 MONTH
外,均从 0 开始计数。特别注意的是: 0 年表示 1
B.C., 因此 -37 则表示公元前 38 年。
除了公历外, Emacs 还支持犹太历、伊斯兰历、玛雅历等,当然包括中国的农历。
那么,计算成长时间的方法就很简单了,相应的 elisp 程序如下:
(setq lubo-birth-time (encode-time 0 50 8 12 12 2011)) ;; 出生日期 (setq the-date-time (current-time)) ;; 当前时间 (setq life-time (time-subtract the-date-time lubo-birth-time)) ;; 时间差 (format-seconds "%Y, %D, %H, %M%z" (float-time life-time))
其实,一天严格来说并不是 86400 秒,一年也不是严格的 365 天,因此,在计算日期差时,可以更精确一些。可以定义了一个函数,用于交互式显示成长日期:
;; 显示儿子的成长时间 (defun lubo-live-time () "Display the live time of my son." (interactive) (let* ((birth-time (encode-time 0 50 8 12 12 2011)) (live-time (time-subtract (current-time) birth-time)) (lt-secs (float-time live-time))) (message (format "Lubo: %d days; %.2f months; %.2f weeks; -- %s" (floor (/ lt-secs 86400)) (/ lt-secs 2628000) ;; 1 y = 12 m, 1 m ~= 30.4166667 d (/ lt-secs 604800) (format-seconds "%Y, %D, %H, %M%z" lt-secs) ))))
把上述代码添加到 .emacs
中,这样,随时想到儿子,我按一下 M-x lubo-live-time
命令,就可以很开心地看到一串字符了:
Lubo: 637 days; 20.95 months; 91.04 weeks; -- 1 year, 272 days, 7 hours, 33 minutes