Common Lisp 有两种不同的查看时间的方法:国际标准时间,即日常使用的时间;运行时间,即 CPU 处理的时间。
国际标准时间
国际标准时间是指距离格林威治时间1900年1月1日0:00过了多少秒。函数 GET-UNIVERSAL-TIME
会返回当前的时间标准时间:
CL-USER> (get-universal-time)
3794283195
这个数字不是那么好理解,可以使用函数 DECODE-UNIVERSAL-TIME
将其转换为正常的日历上的时间:
CL-USER> (decode-universal-time (get-universal-time))
31
35
15
27
3
2020
4
NIL
-8
输出的结果的值分别代表:秒、分、时、日、月、年、周几、夏令时和时区。其中周几是从 0 开始计数的,也就是说如果结果是 4 的话,那么就表示是周五。时区的话即为当前时间加上时区就是格林威治时间。所以目前的话是 2020年3月27号 15:35:31,周五,东八区,无夏令时。
CL-USER> (get-decoded-time)
等效于
CL-USER> (decode-universal-time (get-universal-time))
以下是个使用这些函数的程序:
CL-USER> (defconstant *day-names*
'("Monday" "Tuesday" "Wednesday"
"Thursday" "Friday" "Saturday"
"Sunday"))
*DAY-NAMES*
CL-USER> (multiple-value-bind
(second minute hour day month year day-of-week dst-p tz)
(get-decoded-time)
(format t "It is now ~2,'0d:~2,'0d:~2,'0d of ~a, ~d/~2,'0d/~d (GMT~@d)"
hour
minute
second
(nth day-of-week *day-names*)
month
day
year
(- tz)))
It is now 15:48:38 of Friday, 3/27/2020 (GMT+8)
既然有 DECODE-UNIVERSAL-TIME
,那么也就有 ENCODE-UNIVERSAL-TIME
:
CL-USER> (encode-universal-time 48 47 15 27 3 2020)
3794284068
有了这些后,就可以计算一些临时的时间距离了,如地月时间(地球到月球的时间):
CL-USER> (setq *moon* (encode-universal-time 0 17 16 20 7 1969 4))
2194805820
CL-USER> (setq *takeoff* (encode-universal-time 0 38 11 28 1 1986 5))
2716303080
CL-USER> (- *takeoff* *moon*)
521497260
内部时间
内部时间是使用计算机时钟来计算Lisp环境中的时间。与世界标准时间是不同的。首先,内部时间可以计算你启动 Lisp 实例的时间,计算机启动的时间。同时内部时间的单位使用的是微秒,而不是秒。可以使用 INTERNAL-TIME-UNITS-PER-SECOND
来查看:
CL-USER> internal-time-units-per-second
1000
同时运行时间和内部时间是有区别的。
CL-USER> (let ((real1 (get-internal-real-time))
(run1 (get-internal-run-time)))
(... your call here ...)
(let ((run2 (get-internal-run-time))
(real2 (get-internal-real-time)))
(format t "Computation took:~%")
(format t " ~f seconds of real time~%"
(/ (- real2 real1) internal-time-units-per-second))
(format t " ~f seconds of run time~%"
(/ (- run2 run1) internal-time-units-per-second))))
;; use sleep to see the difference
CL-USER> (defmacro timing (&body forms)
(let ((real1 (gensym))
(real2 (gensym))
(run1 (gensym))
(run2 (gensym))
(result (gensym)))
`(let* ((,real1 (get-internal-real-time))
(,run1 (get-internal-run-time))
(,result (progn ,@forms))
(,run2 (get-internal-run-time))
(,real2 (get-internal-real-time)))
(format *debug-io* ";;; Computation took:~%")
(format *debug-io* ";;; ~f seconds of real time~%"
(/ (- ,real2 ,real1) internal-time-units-per-second))
(format t ";;; ~f seconds of run time~%"
(/ (- ,run2 ,run1) internal-time-units-per-second))
,result)))
TIMING
CL-USER> (timing (sleep 1))
;;; Computation took: 0.994 seconds of real time 0.0 seconds of run
;;; time
NIL
以下是 Allegro Common Lisp 6.0 的一个例子:
CL-USER> (let ((numbers (loop for i from 1 to 100 collect (random 1.0))))
(time (sort numbers #'<)))
; cpu time (non-gc) 0 msec user, 10 msec system
; cpu time (gc) 0 msec user, 0 msec system
; cpu time (total) 0 msec user, 10 msec system
; real time 9 msec
; space allocation:
; 3,586 cons cells, 11,704 other bytes, 0 static bytes
计算周几
上文中的计算国际标准时间不适用与1900年之前的日期
CL-USER> (defun day-of-week (day month year)
"Returns the day of the week as an integer. Monday is 0."
(nth-value
6
(decode-universal-time
(encode-universal-time 0 0 0 day month year 0)
0)))
DAY-OF-WEEK
CL-USER> (day-of-week 23 12 1965)
3
CL-USER> (day-of-week 1 1 1900)
0
CL-USER> (day-of-week 31 12 1899)
Type-error in KERNEL::OBJECT-NOT-TYPE-ERROR-HANDLER:
1899 is not of type (OR (MOD 100) (INTEGER 1900))
Gerald Doussot 提供了一个计算1900年之前的周几的函数:
(defun day-of-week (day month year)
"Returns the day of the week as an integer. Sunday is 0. Works for years after 1752."
(let ((offset '(0 3 2 5 0 3 5 1 4 6 2 4)))
(when (< month 3)
(decf year 1))
(mod
(truncate (+ year
(/ year 4)
(/ (- year)
100)
(/ year 400)
(nth (1- month) offset)
day
-1))
7)))
local-time
库
local-time
库是 quicklisp 中较常用库。通常,这个库可以:
- 打印不同格式的时间戳
- 解析时间的字符串
- 时间运算
- 可以在 Unix 时间、时间戳和世界标准时间中进行转换
如,将 Unix 时间转换成可读的:
(defun unix-time-to-human-string (unix-time)
(local-time:format-timestring
nil
(local-time:unix-to-timestamp unix-time)
:format local-time:+asctime-format+))
获取当前时间
now
today
(ql:quickload :local-time)
(use-package :local-time)
(local-time:now)
@2020-03-27T16:16:33.195844+08:00
(local-time:today)
@2020-03-27T08:00:00.000000+08:00
格式化时间字符串
(local-time:now)
@2020-03-27T16:16:33.195844+08:00
(format nil "~a" (local-time:now))
"2020-03-27T16:18:13.046527+08:00"
(local-time:format-timestring nil (local-time:now))
"2020-03-27T16:19:02.555770+08:00"
接受 :format
参数,默认为 +iso-8601-format+
。
+rfc-1123-format+
(local-time:format-timestring nil (local-time:now) :format local-time:+rfc-1123-format+)
"Fri, 27 Mar 2020 16:21:13 +0800"
+asctime-format+
(local-time:format-timestring nil (local-time:now) :format local-time:+asctime-format+)
"Fri Mar 27 16:22:45 2020"
+iso-week-date-format+
(local-time:format-timestring nil (local-time:now) :format local-time:+iso-week-date-format+)
"2020-W13-5"
自定义时间格式
(local-time:format-timestring nil (local-time:now) :format '(:year "-" :month "-" :day))
2020-3-27
具体参数见:https://common-lisp.net/project/local-time/manual.html#Parsing-and-Formatting
+rfc-1123-format+
定义如下:
(defparameter +rfc-1123-format+
;; Sun, 06 Nov 1994 08:49:37 GMT
'(:short-weekday ", " (:day 2) #\space :short-month #\space (:year 4) #\space
(:hour 2) #\: (:min 2) #\: (:sec 2) #\space :gmt-offset-hhmm)
"See the RFC 1123 for the details about the possible values of the timezone field.")
解析时间字符串
parse-timestring
其说明文档为:
Parses a timestring and returns the corresponding timestamp. Parsing begins at start and stops at the end position. If there are invalid characters within timestring and fail-on-error is T, then an invalid-timestring error is signaled, otherwise NIL is returned.
If there is no timezone specified in timestring then offset is used as the default timezone offset (in seconds).
例子:
(local-time:parse-timestring "2020-03-27T16:19:02.555770+08:00")
;; @2020-03-27T16:19:02.555770+08:00
(local-time:parse-timestring "2020-3-17")
;; @2020-03-27T08:00:00.000000+08:00
;; using :date-separator
(local-time:parse-timestring "2020/3/17" :date-separator #\/)
;; @2020-03-27T08:00:00.000000+08:00
其他的参数:
- 开始结束位置
fail-on-error
(默认为t
)(allow-missing-elements t)
(allow-misssing-date-part allow-missing-elements)
(allow-missing-time-part allow-missing-elements)
(allow-missing-timezone-part allow-missing-elements)
(offset 0)
如果 “Fri Mar 27 16:36:32 2020” 这个格式转换出错的话,可以使用 cl-date-time-parser
库
(ql:quickload :cl-date-time-parser)
(use-package :cl-date-time-parser)
(cl-date-time-parser:parse-date-time "Fri Mar 27 16:36:32 2020")
;; 3794315792
;; 0
local-time
库
(local-time:universal-to-timestamp *)
@2020-03-28T00:36:32.000000+08:00