root/lang/commonlisp/xyzzy-compat/timestamp.lisp

Revision 18704, 9.3 kB (checked in by g000001, 4 months ago)

lang/commonlisp/xyzzy-compat: parse-date-stringが読みづらい流れのコードになっていたので修正。

Line 
1;;; -*- Mode: Lisp -*-
2
3;;; License: MIT license.
4
5;;;
6;;; This file was part of xyzzy.
7;;;
8
9;;; Permission is hereby granted, free of charge, to any person obtaining
10;;; a copy of this software and associated documentation files (the
11;;; "Software"), to deal in the Software without restriction, including
12;;; without limitation the rights to use, copy, modify, merge, publish,
13;;; distribute, sublicense, and/or sell copies of the Software, and to
14;;; permit persons to whom the Software is furnished to do so, subject to
15;;; the following conditions:
16;;;
17;;; The above copyright notice and this permission notice shall be
18;;; included in all copies or substantial portions of the Software.
19;;;
20;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24;;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25;;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26;;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28;;;
29;;; format-date
30;;;
31;;; a: 短い形式の曜日
32;;; A: 長い形式の曜日
33;;; b: 短い形式の月
34;;; B: 長い形式の月
35;;; d: 日(00〜59)                 # (0〜59)
36;;; e: 和暦の年(01〜)             # (1〜)
37;;; E: 和暦の年(元, 02〜)         # (元, 2〜)
38;;; g: 元号(明治,大正,昭和,平成)  # (明,大,昭,平)
39;;; G: 元号(M, T, S, H)
40;;; H: 時(00〜23)                 # (0〜23)
41;;; I: 12時間の時(01〜12)         # (1〜12)
42;;; i: Internet Time(000〜999)
43;;; m: 月(01〜12)                 # (1〜12)
44;;; M: 分(00〜59)                 # (0〜59)
45;;; p: 午前/午後
46;;; P: AM/PM                      # am/pm
47;;; S: 秒(00〜59)                 # (0〜59)
48;;; v: 曜日(日本語)
49;;; y: 年(2桁)
50;;; Y: 年(4桁)
51;;; z: タイムゾーン名(JST-9)
52;;; Z: タイムゾーン(+0900)        # (+09:00)
53
54;;; Commentary:
55;;;
56;;; * xyzzyのlisp/timestmp.lをCommon Lispに移植したものです。
57;;;   cl-ppcreに依存しています。
58
59(in-package :xyzzy)
60
61(defmacro defconstant* (sym value &optional doc)
62  `(defconstant ,sym (if (boundp ',sym)
63                         (symbol-value ',sym)
64                         ,value)
65     ,@(when doc (list doc))))
66
67(defconstant* +abbreviated-weekday-names+
68  #("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
69
70(defconstant* +full-weekday-names+
71  #("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
72
73(defconstant* +japanese-weekday-names+ "月火水木金土日")
74
75(defconstant* +abbreviated-month-names+
76  #("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
77
78(defconstant* +full-month-names+
79  #("January" "February" "March" "April" "May" "June"
80    "July" "August" "September" "October" "November" "December"))
81
82(defvar *timezone-name* "JST")
83
84;; 元号と西暦の対応表(たぶん合ってる)
85(defconstant* +japanese-era-list+
86  '(("平成" "H" 1989 1 8)
87    ("昭和" "S" 1926 12 25)
88    ("大正" "T" 1912 7 30)
89    ;; Common LispではGMT1900年より前は存在しない
90    ;; ("明治" "M" 1868 5 9)
91   ))
92
93(defconstant* +japanese-era+
94  (mapcar #'(lambda (x)
95              (list (encode-universal-time
96                     0 0 0 (fifth x) (fourth x) (third x) -9)
97                    (third x) (first x) (second x)))
98          +japanese-era-list+))
99
100(defun get-japanese-era (universal-time year)
101  (let ((x (find universal-time +japanese-era+ :test #'>= :key #'car)))
102    (if x
103        (cons (+ (- year (cadr x)) 1) (cddr x))
104      (list (- year 1867) "明治" "M")  ; いまいち
105    )))
106
107(defun format-date (s fmt &optional (universal-time (get-universal-time)))
108  (multiple-value-bind (sec min hour day mon year dow daylight tz)
109      (decode-universal-time universal-time)
110    (declare (ignore daylight))
111    (do ((i 0 (+ i 1))
112         (l (length fmt))
113         (jp nil))
114        ((= i l))
115      (let ((c (elt fmt i)))
116        (cond ((char= c #\%)
117               (let ((pound nil))
118                 (incf i)
119                 (when (= i l)
120                   (return))
121                 (setq c (elt fmt i))
122                 (when (char= c #\#)
123                   (setq pound t)
124                   (incf i)
125                   (when (= i l)
126                     (return))
127                   (setq c (elt fmt i)))
128                 (let ((fmtd (if pound "~d" "~2,'0d")))
129                   (case c
130                     (#\a
131                      (princ (svref +abbreviated-weekday-names+ dow) s))
132                     (#\A
133                      (princ (svref +full-weekday-names+ dow) s))
134                     (#\b
135                      (princ (svref +abbreviated-month-names+ (- mon 1)) s))
136                     (#\B
137                      (princ (svref +full-month-names+ (- mon 1)) s))
138                     (#\d
139                      (format s fmtd day))
140                     (#\e
141                      (unless jp
142                        (setq jp (get-japanese-era universal-time year)))
143                      (format s fmtd (car jp)))
144                     (#\E
145                      (unless jp
146                        (setq jp (get-japanese-era universal-time year)))
147                      (if (= (car jp) 1)
148                          (princ "元" s)
149                        (format s fmtd (car jp))))
150                     (#\g
151                      (unless jp
152                        (setq jp (get-japanese-era universal-time year)))
153                      (princ (if pound (svref (cadr jp) 0) (cadr jp)) s))
154                     (#\G
155                      (unless jp
156                        (setq jp (get-japanese-era universal-time year)))
157                      (princ (caddr jp) s))
158                     (#\H
159                      (format s fmtd hour))
160                     (#\I
161                      (let ((h (mod hour 12)))
162                        (format s fmtd (if (zerop h) 12 h))))
163                     (#\i
164                      (format s "~3,'0d"
165                              (truncate (rem (+ universal-time 3600) 86400) 86.4)))
166                     (#\m
167                      (format s fmtd mon))
168                     (#\M
169                      (format s fmtd min))
170                     (#\p
171                      (princ (if (< hour 12) "午前" "午後") s))
172                     (#\P
173                      (if pound
174                          (princ (if (< hour 12) "am" "pm") s)
175                        (princ (if (< hour 12) "AM" "PM") s)))
176                     (#\S
177                      (format s fmtd sec))
178                     (#\v
179                      (princ (aref +japanese-weekday-names+ dow) s))
180                     (#\y
181                      (format s "~2,'0d" (mod year 100)))
182                     (#\Y
183                      (princ year s))
184                     (#\z
185                      (format s "~A~D" *timezone-name* tz))
186                     (#\Z
187                      (let ((x (abs tz)))
188                        (format s "~:[+~;-~]~2,'0d~:[~;:~]~2,'0d"
189                                (plusp tz) (truncate x) pound
190                                (mod (truncate (* x 60)) 60))))
191                     (t
192                      (write-char c s))))))
193              (t
194               (write-char c s)))))))
195
196(defun format-date-string (fmt &optional (universal-time (get-universal-time)))
197  (with-output-to-string (s)
198    (format-date s fmt universal-time)))
199
200(defvar *date-formats*
201  '("%a, %d %b %Y %H:%M:%S %Z"
202    "%a, %d %b %Y %H:%M:%S %z"
203    "%a %b %d %H:%M:%S %Y"
204    "%d %b %Y %H:%M:%S %Z"
205    "%d %b %Y %H:%M:%S %z"
206    "%Y-%m-%dT%H:%M:%S%#Z"
207    "%B %d, %Y"
208    "%b %d %Y"
209    "%Y-%m-%d"
210    "%d %b %y"
211    "%y/%m/%d"
212    "%y-%m-%d"
213    "%g%#e年%#m月%#d日 %v曜日"
214    "%g%#e年%#m月%#d日"
215    "%Y年%#m月%#d日(%v)"
216    "%Y年%#m月%#d日"
217    "%y年%#m月%#d日(%v)"
218    "%y年%#m月%#d日"
219    " %H:%M:%S"
220    " %#H:%M:%S"
221    " %#I:%M:%S %P"
222    " %#H時%#M分%#S秒"
223    " %p%#I時%#M分%#S秒"
224    "@%i"
225    ))
226
227(defun add-date-format (fmt)
228  (pushnew fmt *date-formats* :test #'string=))
229
230#|(defun insert-date-string ()
231  (interactive "*")
232  (multiple-value-bind (result data)
233      (dialog-box '(dialog 0 0 260 120
234                    (:caption "日付と時刻")
235                    (:font 9 "MS UI Gothic")
236                    (:control
237                     (:listbox list nil #x50a10001 4 5 192 114)
238                     (:button IDOK "OK" #x50030001 205 5 52 14)
239                     (:button IDCANCEL "キャンセル" #x50030000 205 22 52 14)))
240                  (list (cons 'list (mapcar #'format-date-string *date-formats*)))
241                  '((list :must-match t :enable (IDOK))))
242    (when result
243      (insert (cdr (assoc 'list data))))))|#
244
245(defvar *date-format-regexp*
246  (ppcre:create-scanner "([0-9][0-9]?)/([0-9][0-9]?)/([0-9][0-9]?) +([0-9][0-9]?):([0-9][0-9]?):([0-9][0-9]?)"))
247
248(defun parse-date-string (string)
249  (if (stringp string)
250      (multiple-value-bind (win parts)
251          (ppcre:scan-to-strings *date-format-regexp* string)
252        (when win
253          (handler-case
254              (apply #'encode-universal-time
255                     (nreverse (map 'list #'parse-integer parts)))
256            (error () nil))))
257      nil))
258
259; usage
260;(format-date-string (nth 12 *date-formats*)
261;                    (get-universal-time))
262;=> "平成20年8月19日 火曜日"
263
264;(parse-date-string "1999/01/01 17:00:0")
265;=> 3124166400
Note: See TracBrowser for help on using the browser.