逆引きxyzzy lisp(日付と時刻) - circleratio/xyzzy GitHub Wiki
ユニバーサルタイム形式(1900年1月1日0時0分0秒からの秒数)での取得なら下記。
(get-universal-time)
=> 3498307254
月、日、時、分、秒などを分けて取得するなら get-decoded-time を使う。 この関数は多値を返すので、multiple-value-list マクロでリストにすると扱いやすい。
(multiple-value-list (get-decoded-time))
=> (45 21 21 5 2 2011 5 nil -9)
値は下記の順序: second, minute, hour, date, month, year, day of week (0 = Monday), T (daylight savings times) or NIL (standard time), and timezone.
(format-date-string "%Y/%m/%d")
optional引数として時間を指定可能. 下記は24時間前の日付を表示する例.
(format-date-string "%Y/%m/%d" (- (get-universal-time) (* 60 60 24))
フォーマットとして指定できるのは下記。 :%a: 短い形式の曜日 :%A: 長い形式の曜日 :%b: 短い形式の月 :%B: 長い形式の月 :%d: 日(00~31) :%e: 和暦の年(01~) :%E: 和暦の年(元, 02~) :%g: 元号(明治,大正,昭和,平成) :%G: 元号(M, T, S, H) :%H: 時(00~23) :%I: 12時間の時(01~12) :%i: Internet Time(000~999) :%m: 月(01~12) :%M: 分(00~59) :%p: 午前/午後 :%P: AM/PM :%S: 秒(00~59) :%v: 曜日(日本語) :%y: 年(2桁) :%Y: 年(4桁) :%z: タイムゾーン名(JST-9) :%Z: タイムゾーン(+0900)
ユニバーサルタイム形式にしてから任意の秒数を加減すればよい。
(format-date-string
"%Y/%m/%d"
(+ (encode-universal-time 30 59 23 2 5 2011) ; 2011年5月2日 23時59分30秒
(* 24 60 60)))
=> "2011/05/03"
ユニバーサルタイム形式に変換してから差を取ればよい。 次の例は元旦から5月2日までの日数を求めたもの。
(truncate (/ (- (encode-universal-time 0 0 12 2 5 2011) ; 2011年5月2日 12時0分0秒
(encode-universal-time 0 0 0 1 1 2011)) ; 2011年1月1日 0時0分0秒
(* 24 60 60.0)))
=> 121
0.5
xyzzy の日付フォーマット指定は強力で、日本語の曜日が指示できる。
(format-date-string "%v")
=> "土"
他のLisp実装系で、これができない環境では下記のようにする。
(nth (multiple-value-bind
(second minute hour day month year day-of-week daylight-p tz) ; day-of-week は 0(月曜)から始まる
(get-decoded-time)
day-of-week)
'("月" "火" "水" "木" "金" "土" "日"))
=> "土"
encode-universal-time ではチェックできない。 次の例では日付の範囲がおかしいが、それでもエラーにならず、ユニバーサルタイムの値が返ってくる。
(encode-universal-time 0 0 0 33 1 2001) ; 2001年1月33日
=> 3190028400
しかたがないので真面目にチェックする(ただし紀元前は対象外)。 うるう年には注意。
(defun valid-date (year month day)
(and (>= year 0)
(>= month 1)
(<= month 12)
(>= day 1)
(let ((end-of-month
(+ (nth (1- month) '(31 28 31 30 31 60 31 31 30 31 30 31))
(if (and (eql month 2) (leap-year-p year))
1
0))))
(<= day end-of-month))))
(defun leap-year-p (year)
(or (and (eql (mod year 4) 0)
(not (eql (mod year 100) 0)))
(eql (mod year 400) 0)))
(valid-date 2100 2 29) ; 平年
=> nil
(valid-date 2000 2 29) ; うるう年
=> t
(valid-date 2011 1 32) ; 日付の範囲がおかしい
=> nil
ユニバーサルタイムで計算する。
(defun date-diff-day (year month day diff-day) ; 年月日および差となる日数(負の値もOK)
(multiple-value-bind
(s m h d m y dow daylight tz)
(decode-universal-time
(+ (encode-universal-time 0 0 0 day month year)
(* diff-day 24 60 60)))
(values y m d)))
(date-diff-day 2011 2 5 -5)
=> 2011
1
31
剰余を使って計算。 ただし、月ごとに末日が異なる点に注意が必要(1月31日の1ヵ月後を「2月31日」とするわけにはいかない)。 以下の処理では、日数オーバーの場合は単純に切り詰めるようにした(2月31日→2月28日/29日(うるう年の場合))。
(defun date-diff-month (year month day diff-month)
(let* ((remainder (mod (+ diff-month month) 12))
(quotient (/ (- (+ diff-month month) remainder) 12))
(result-year (+ year quotient))
(result-month (if (eql remainder 0) 12 remainder))
(result-day (adjust-end-of-month result-year result-month day)))
(values result-year result-month result-day)))
(defun adjust-end-of-month (year month day)
(let ((end-of-month
(+ (nth (1- month) '(31 28 31 30 31 60 31 31 30 31 30 31))
(if (and (eql month 2) (leap-year-p year))
1
0))))
(if (<= day end-of-month)
day
end-of-month)))
(defun leap-year-p (year)
(or (and (eql (mod year 4) 0)
(not (eql (mod year 100) 0)))
(eql (mod year 400) 0)))
(date-diff-month 2010 1 31 1)
=> 2010
2
28
(date-diff-month 2010 1 31 -14)
=> 2008
11
30
定義に従って判定する。
(defun leap-year-p (year)
(or (and (eql (mod year 4) 0)
(not (eql (mod year 100) 0)))
(eql (mod year 400) 0)))
leap-year-p
(leap-year-p 2000)
=> t
(leap-year-p 2010)
=> nil
(leap-year-p 2012)
=> t
(leap-year-p 2100)
=> nil
(defun string-date (str)
(if (string-match "\\([0-9]\\{4\\}\\)/\\([0-9]\\{1,2\\}\\)/\\([0-9]\\{1,2\\}\\)" str)
(let ((y (read-from-string (match-string 1)))
(m (read-from-string (match-string 2)))
(d (read-from-string (match-string 3))))
(encode-universal-time 0 0 0 d m y))))
(string-date "2011/4/20")
=> 3512214000
(decode-universal-time 3512214000)
=> 0
0
0
20
4
2011
2
nil
-9
5秒おきに timer-func を実行する(そして止める)には次のようにする。
(defun timer-func ()
(message "test!"))
(start-timer 5 'timer-func)
=> t
(stop-timer 'timer-func)
=> t
1回だけ実行するなら、
(start-timer 5 'timer-func t)
=> t
とする。
キー入力があった場合に動作再開するなら sit-for, 待ったままにするなら sleep-for を使う。
(sit-for 3)
=> nil
(sleep-for 3)
=> nil
ミリ秒単位で時間を取得できるのは(たぶん)get-internal-real-time のみ。
(defun count-time (func &rest args)
(let ((stime (get-internal-real-time)))
(apply func args)
(/ (- (get-internal-real-time) stime) 1000.0)))
(count-time #'sit-for 3)
=> 3.027