]> gitweb.factorcode.org Git - factor.git/blob - basis/calendar/format/format.factor
calendar.format: use math.parser format directive.
[factor.git] / basis / calendar / format / format.factor
1 ! Copyright (C) 2008, 2010 Slava Pestov, Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays calendar calendar.format.macros
4 combinators io io.streams.string kernel math math.functions
5 math.order math.parser math.parser.private present sequences
6 typed ;
7 IN: calendar.format
8
9 : pad-00 ( n -- str ) number>string 2 CHAR: 0 pad-head ;
10
11 : pad-0000 ( n -- str ) number>string 4 CHAR: 0 pad-head ;
12
13 : pad-00000 ( n -- str ) number>string 5 CHAR: 0 pad-head ;
14
15 : write-00 ( n -- ) pad-00 write ;
16
17 : write-0000 ( n -- ) pad-0000 write ;
18
19 : write-00000 ( n -- ) pad-00000 write ;
20
21 : hh ( time -- ) hour>> write-00 ;
22
23 : mm ( time -- ) minute>> write-00 ;
24
25 : ss ( time -- ) second>> >integer write-00 ;
26
27 : D ( time -- ) day>> number>string write ;
28
29 : DD ( time -- ) day>> write-00 ;
30
31 : DAY ( time -- ) day-of-week day-abbreviation3 write ;
32
33 : MM ( time -- ) month>> write-00 ;
34
35 : MONTH ( time -- ) month>> month-abbreviation write ;
36
37 : YYYY ( time -- ) year>> write-0000 ;
38
39 : YYYYY ( time -- ) year>> write-00000 ;
40
41 : expect ( str -- )
42     read1 swap member? [ "Parse error" throw ] unless ;
43
44 : read-00 ( -- n ) 2 read string>number ;
45
46 : read-000 ( -- n ) 3 read string>number ;
47
48 : read-0000 ( -- n ) 4 read string>number ;
49
50 : hhmm>timestamp ( hhmm -- timestamp )
51     [
52         0 0 0 read-00 read-00 0 instant <timestamp>
53     ] with-string-reader ;
54
55 GENERIC: day. ( obj -- )
56
57 M: integer day. ( n -- )
58     number>string dup length 2 < [ bl ] when write ;
59
60 M: timestamp day. ( timestamp -- )
61     day>> day. ;
62
63 GENERIC: month. ( obj -- )
64
65 M: array month. ( pair -- )
66     first2
67     [ month-name write bl number>string print ]
68     [ 1 zeller-congruence ]
69     [ (days-in-month) day-abbreviations2 " " join print ] 2tri
70     over "   " <repetition> "" concat-as write
71     [
72         [ 1 + day. ] keep
73         1 + + 7 mod zero? [ nl ] [ bl ] if
74     ] with each-integer nl ;
75
76 M: timestamp month. ( timestamp -- )
77     [ year>> ] [ month>> ] bi 2array month. ;
78
79 GENERIC: year. ( obj -- )
80
81 M: integer year. ( n -- )
82     12 [ 1 + 2array month. nl ] with each-integer ;
83
84 M: timestamp year. ( timestamp -- )
85     year>> year. ;
86
87 : timestamp>mdtm ( timestamp -- str )
88     [ { YYYY MM DD hh mm ss } formatted ] with-string-writer ;
89
90 : (timestamp>string) ( timestamp -- )
91     { DAY ", " D " " MONTH " " YYYY " " hh ":" mm ":" ss } formatted ;
92
93 : timestamp>string ( timestamp -- str )
94     [ (timestamp>string) ] with-string-writer ;
95
96 : (write-gmt-offset) ( duration -- )
97     [ hh ] [ mm ] bi ;
98
99 : write-gmt-offset ( gmt-offset -- )
100     dup instant <=> {
101         { +eq+ [ drop "GMT" write ] }
102         { +lt+ [ "-" write before (write-gmt-offset) ] }
103         { +gt+ [ "+" write (write-gmt-offset) ] }
104     } case ;
105
106 : timestamp>rfc822 ( timestamp -- str )
107     #! RFC822 timestamp format
108     #! Example: Tue, 15 Nov 1994 08:12:31 +0200
109     [
110         [ (timestamp>string) bl ]
111         [ gmt-offset>> write-gmt-offset ]
112         bi
113     ] with-string-writer ;
114
115 : timestamp>http-string ( timestamp -- str )
116     #! http timestamp format
117     #! Example: Tue, 15 Nov 1994 08:12:31 GMT
118     >gmt timestamp>rfc822 ;
119
120 : (timestamp>cookie-string) ( timestamp -- )
121     >gmt
122     { DAY ", " DD "-" MONTH "-" YYYY " " hh ":" mm ":" ss " GMT" } formatted ;
123
124 : timestamp>cookie-string ( timestamp -- str )
125     [ (timestamp>cookie-string) ] with-string-writer ;
126
127 : (write-rfc3339-gmt-offset) ( duration -- )
128     [ hh ":" write ] [ mm ] bi ;
129
130 : write-rfc3339-gmt-offset ( duration -- )
131     dup instant <=> {
132         { +eq+ [ drop "Z" write ] }
133         { +lt+ [ "-" write before (write-rfc3339-gmt-offset) ] }
134         { +gt+ [ "+" write (write-rfc3339-gmt-offset) ] }
135     } case ;
136
137 ! Should be enough for anyone, allows to not do a fancy
138 ! algorithm to detect infinite decimals (e.g 1/3)
139 : write-rfc3339-seconds ( timestamp -- )
140     second>> 1 mod [
141         >float "%.6f" format-float [ CHAR: 0 = ] trim
142         dup length 1 > [ write ] [ drop ] if
143     ] unless-zero ;
144
145 : (timestamp>rfc3339) ( timestamp -- )
146     {
147         YYYY "-" MM "-" DD "T" hh ":" mm ":" ss
148         write-rfc3339-seconds
149         [ gmt-offset>> write-rfc3339-gmt-offset ]
150     } formatted ;
151
152 : timestamp>rfc3339 ( timestamp -- str )
153     [ (timestamp>rfc3339) ] with-string-writer ;
154
155 : signed-gmt-offset ( dt ch -- dt' )
156     { { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case time* ;
157
158 : read-rfc3339-gmt-offset ( ch -- dt )
159     {
160         { f [ instant ] }
161         { CHAR: Z [ instant ] }
162         [
163             [
164                 read-00 hours
165                 read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case minutes
166                 time+
167             ] dip signed-gmt-offset
168         ]
169     } case ;
170
171 : read-ymd ( -- y m d )
172     read-0000 "-" expect read-00 "-" expect read-00 ;
173
174 : read-hms ( -- h m s )
175     read-00 ":" expect read-00 ":" expect read-00 ;
176
177 : read-rfc3339-seconds ( s -- s' ch )
178     "+-Z" read-until [
179         [ string>number ] [ length 10^ ] bi / +
180     ] dip ;
181
182 : (rfc3339>timestamp) ( -- timestamp )
183     read-ymd
184     "Tt \t" expect
185     read-hms
186     read1 { { CHAR: . [ read-rfc3339-seconds ] } [ ] } case
187     read-rfc3339-gmt-offset
188     <timestamp> ;
189
190 : rfc3339>timestamp ( str -- timestamp )
191     [ (rfc3339>timestamp) ] with-string-reader ;
192
193 ERROR: invalid-timestamp-format ;
194
195 : check-timestamp ( obj/f -- obj )
196     [ invalid-timestamp-format ] unless* ;
197
198 : read-token ( seps -- token )
199     [ read-until ] keep member? check-timestamp drop ;
200
201 : read-sp ( -- token ) " " read-token ;
202
203 : checked-number ( str -- n )
204     string>number check-timestamp ;
205
206 : parse-rfc822-gmt-offset ( string -- dt )
207     dup "GMT" = [ drop instant ] [
208         unclip [
209             2 cut [ string>number ] bi@ [ hours ] [ minutes ] bi* time+
210         ] dip signed-gmt-offset
211     ] if ;
212
213 : (rfc822>timestamp) ( -- timestamp )
214     timestamp new
215         "," read-token day-abbreviations3 member? check-timestamp drop
216         read1 CHAR: \s assert=
217         read-sp checked-number >>day
218         read-sp month-abbreviations index 1 + check-timestamp >>month
219         read-sp checked-number >>year
220         ":" read-token checked-number >>hour
221         ":" read-token checked-number >>minute
222         read-sp checked-number >>second
223         readln parse-rfc822-gmt-offset >>gmt-offset ;
224
225 : rfc822>timestamp ( str -- timestamp )
226     [ (rfc822>timestamp) ] with-string-reader ;
227
228 : check-day-name ( str -- )
229     [ day-abbreviations3 member? ] [ day-names member? ] bi or
230     check-timestamp drop ;
231
232 : (cookie-string>timestamp-1) ( -- timestamp )
233     timestamp new
234         "," read-token check-day-name
235         read1 CHAR: \s assert=
236         "-" read-token checked-number >>day
237         "-" read-token month-abbreviations index 1 + check-timestamp >>month
238         read-sp checked-number >>year
239         ":" read-token checked-number >>hour
240         ":" read-token checked-number >>minute
241         read-sp checked-number >>second
242         readln parse-rfc822-gmt-offset >>gmt-offset ;
243
244 : cookie-string>timestamp-1 ( str -- timestamp )
245     [ (cookie-string>timestamp-1) ] with-string-reader ;
246
247 : (cookie-string>timestamp-2) ( -- timestamp )
248     timestamp new
249         read-sp check-day-name
250         read-sp month-abbreviations index 1 + check-timestamp >>month
251         read-sp checked-number >>day
252         ":" read-token checked-number >>hour
253         ":" read-token checked-number >>minute
254         read-sp checked-number >>second
255         read-sp checked-number >>year
256         readln parse-rfc822-gmt-offset >>gmt-offset ;
257
258 : cookie-string>timestamp-2 ( str -- timestamp )
259     [ (cookie-string>timestamp-2) ] with-string-reader ;
260
261 : cookie-string>timestamp ( str -- timestamp )
262     {
263         [ cookie-string>timestamp-1 ]
264         [ cookie-string>timestamp-2 ]
265         [ rfc822>timestamp ]
266     } attempt-all-quots ;
267
268 : (ymdhms>timestamp) ( -- timestamp )
269     read-ymd " " expect read-hms instant <timestamp> ;
270
271 : ymdhms>timestamp ( str -- timestamp )
272     [ (ymdhms>timestamp) ] with-string-reader ;
273
274 : (hms>timestamp) ( -- timestamp )
275     0 0 0 read-hms instant <timestamp> ;
276
277 : hms>timestamp ( str -- timestamp )
278     [ (hms>timestamp) ] with-string-reader ;
279
280 : (ymd>timestamp) ( -- timestamp )
281     read-ymd <date-gmt> ;
282
283 : ymd>timestamp ( str -- timestamp )
284     [ (ymd>timestamp) ] with-string-reader ;
285
286 : (timestamp>ymd) ( timestamp -- )
287     { YYYY "-" MM "-" DD } formatted ;
288
289 TYPED: timestamp>ymd ( timestamp: timestamp -- str )
290     [ (timestamp>ymd) ] with-string-writer ;
291
292 : (timestamp>hms) ( timestamp -- )
293     { hh ":" mm ":" ss } formatted ;
294
295 TYPED: timestamp>hms ( timestamp: timestamp -- str )
296     [ (timestamp>hms) ] with-string-writer ;
297
298 : (timestamp>hm) ( timestamp -- )
299     { hh ":" mm } formatted ;
300
301 TYPED: timestamp>hm ( timestamp: timestamp -- str )
302     [ (timestamp>hm) ] with-string-writer ;
303
304 TYPED: timestamp>ymdhms ( timestamp: timestamp -- str )
305     [
306         >gmt
307         { (timestamp>ymd) " " (timestamp>hms) } formatted
308     ] with-string-writer ;
309
310 : file-time-string ( timestamp -- string )
311     [
312         {
313             MONTH " " DD " "
314             [
315                 dup now [ year>> ] same?
316                 [ [ hh ":" write ] [ mm ] bi ] [ YYYYY ] if
317             ]
318         } formatted
319     ] with-string-writer ;
320
321 M: timestamp present timestamp>string ;
322
323 TYPED: duration>hm ( duration: duration -- string )
324     [ duration>hours >integer 24 mod pad-00 ]
325     [ duration>minutes >integer 60 mod pad-00 ] bi ":" glue ;
326
327 TYPED: duration>human-readable ( duration: duration -- string )
328     [
329         [
330             duration>years >integer
331             [
332                 [ number>string write ]
333                 [ 1 > " years, " " year, " ? write ] bi
334             ] unless-zero
335         ] [
336             duration>days >integer 365 mod
337             [
338                 [ number>string write ]
339                 [ 1 > " days, " " day, " ? write ] bi
340             ] unless-zero
341         ] [ duration>hm write ] tri
342     ] with-string-writer ;