]> gitweb.factorcode.org Git - factor.git/blobdiff - basis/calendar/format/format.factor
Support Link Time Optimization (off by default)
[factor.git] / basis / calendar / format / format.factor
index ac2e902c86a82d4fc650284594ddec94bf56ac15..a85f24af7207974120dbd25b883d2c1fc7460041 100644 (file)
-! Copyright (C) 2008, 2010 Slava Pestov, Doug Coleman.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors arrays calendar calendar.format.macros\r
-combinators io io.streams.string kernel math math.functions\r
-math.order math.parser present sequences typed ;\r
-IN: calendar.format\r
-\r
-: pad-00 ( n -- str ) number>string 2 CHAR: 0 pad-head ;\r
-\r
-: pad-0000 ( n -- str ) number>string 4 CHAR: 0 pad-head ;\r
-\r
-: pad-00000 ( n -- str ) number>string 5 CHAR: 0 pad-head ;\r
-\r
-: write-00 ( n -- ) pad-00 write ;\r
-\r
-: write-0000 ( n -- ) pad-0000 write ;\r
-\r
-: write-00000 ( n -- ) pad-00000 write ;\r
-\r
-: hh ( time -- ) hour>> write-00 ;\r
-\r
-: mm ( time -- ) minute>> write-00 ;\r
-\r
-: ss ( time -- ) second>> >integer write-00 ;\r
-\r
-: D ( time -- ) day>> number>string write ;\r
-\r
-: DD ( time -- ) day>> write-00 ;\r
-\r
-: DAY ( time -- ) day-of-week day-abbreviation3 write ;\r
-\r
-: MM ( time -- ) month>> write-00 ;\r
-\r
-: MONTH ( time -- ) month>> month-abbreviation write ;\r
-\r
-: YYYY ( time -- ) year>> write-0000 ;\r
-\r
-: YYYYY ( time -- ) year>> write-00000 ;\r
-\r
-: expect ( str -- )\r
-    read1 swap member? [ "Parse error" throw ] unless ;\r
-\r
-: read-00 ( -- n ) 2 read string>number ;\r
-\r
-: read-000 ( -- n ) 3 read string>number ;\r
-\r
-: read-0000 ( -- n ) 4 read string>number ;\r
-\r
-: hhmm>timestamp ( hhmm -- timestamp )\r
-    [\r
-        0 0 0 read-00 read-00 0 instant <timestamp>\r
-    ] with-string-reader ;\r
-\r
-GENERIC: day. ( obj -- )\r
-\r
-M: integer day. ( n -- )\r
-    number>string dup length 2 < [ bl ] when write ;\r
-\r
-M: timestamp day. ( timestamp -- )\r
-    day>> day. ;\r
-\r
-GENERIC: month. ( obj -- )\r
-\r
-M: array month. ( pair -- )\r
-    first2\r
-    [ month-name write bl number>string print ]\r
-    [ 1 zeller-congruence ]\r
-    [ (days-in-month) day-abbreviations2 " " join print ] 2tri\r
-    over "   " <repetition> "" concat-as write\r
-    [\r
-        [ 1 + day. ] keep\r
-        1 + + 7 mod zero? [ nl ] [ bl ] if\r
-    ] with each-integer nl ;\r
-\r
-M: timestamp month. ( timestamp -- )\r
-    [ year>> ] [ month>> ] bi 2array month. ;\r
-\r
-GENERIC: year. ( obj -- )\r
-\r
-M: integer year. ( n -- )\r
-    12 [ 1 + 2array month. nl ] with each-integer ;\r
-\r
-M: timestamp year. ( timestamp -- )\r
-    year>> year. ;\r
-\r
-: timestamp>mdtm ( timestamp -- str )\r
-    [ { YYYY MM DD hh mm ss } formatted ] with-string-writer ;\r
-\r
-: (timestamp>string) ( timestamp -- )\r
-    { DAY ", " D " " MONTH " " YYYY " " hh ":" mm ":" ss } formatted ;\r
-\r
-: timestamp>string ( timestamp -- str )\r
-    [ (timestamp>string) ] with-string-writer ;\r
-\r
-: (write-gmt-offset) ( duration -- )\r
-    [ hh ] [ mm ] bi ;\r
-\r
-: write-gmt-offset ( gmt-offset -- )\r
-    dup instant <=> {\r
-        { +eq+ [ drop "GMT" write ] }\r
-        { +lt+ [ "-" write before (write-gmt-offset) ] }\r
-        { +gt+ [ "+" write (write-gmt-offset) ] }\r
-    } case ;\r
-\r
-: timestamp>rfc822 ( timestamp -- str )\r
-    #! RFC822 timestamp format\r
-    #! Example: Tue, 15 Nov 1994 08:12:31 +0200\r
-    [\r
-        [ (timestamp>string) " " write ]\r
-        [ gmt-offset>> write-gmt-offset ]\r
-        bi\r
-    ] with-string-writer ;\r
-\r
-: timestamp>http-string ( timestamp -- str )\r
-    #! http timestamp format\r
-    #! Example: Tue, 15 Nov 1994 08:12:31 GMT\r
-    >gmt timestamp>rfc822 ;\r
-\r
-: (timestamp>cookie-string) ( timestamp -- )\r
-    >gmt\r
-    { DAY ", " DD "-" MONTH "-" YYYY " " hh ":" mm ":" ss " GMT" } formatted ;\r
-\r
-: timestamp>cookie-string ( timestamp -- str )\r
-    [ (timestamp>cookie-string) ] with-string-writer ;\r
-\r
-: (write-rfc3339-gmt-offset) ( duration -- )\r
-    [ hh ":" write ] [ mm ] bi ;\r
-\r
-: write-rfc3339-gmt-offset ( duration -- )\r
-    dup instant <=> {\r
-        { +eq+ [ drop "Z" write ] }\r
-        { +lt+ [ "-" write before (write-rfc3339-gmt-offset) ] }\r
-        { +gt+ [ "+" write (write-rfc3339-gmt-offset) ] }\r
-    } case ;\r
-    \r
-: (timestamp>rfc3339) ( timestamp -- )\r
-    {\r
-        YYYY "-" MM "-" DD "T" hh ":" mm ":" ss\r
-        [ gmt-offset>> write-rfc3339-gmt-offset ]\r
-    } formatted ;\r
-\r
-: timestamp>rfc3339 ( timestamp -- str )\r
-    [ (timestamp>rfc3339) ] with-string-writer ;\r
-\r
-: signed-gmt-offset ( dt ch -- dt' )\r
-    { { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case time* ;\r
-\r
-: read-rfc3339-gmt-offset ( ch -- dt )\r
-    dup CHAR: Z = [ drop instant ] [\r
-        [\r
-            read-00 hours\r
-            read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case minutes\r
-            time+\r
-        ] dip signed-gmt-offset\r
-    ] if ;\r
-\r
-: read-ymd ( -- y m d )\r
-    read-0000 "-" expect read-00 "-" expect read-00 ;\r
-\r
-: read-hms ( -- h m s )\r
-    read-00 ":" expect read-00 ":" expect read-00 ;\r
-\r
-: read-rfc3339-seconds ( s -- s' ch )\r
-    "+-Z" read-until [\r
-        [ string>number ] [ length 10^ ] bi / +\r
-    ] dip ;\r
-\r
-: (rfc3339>timestamp) ( -- timestamp )\r
-    read-ymd\r
-    "Tt" expect\r
-    read-hms\r
-    read1 { { CHAR: . [ read-rfc3339-seconds ] } [ ] } case\r
-    read-rfc3339-gmt-offset\r
-    <timestamp> ;\r
-\r
-: rfc3339>timestamp ( str -- timestamp )\r
-    [ (rfc3339>timestamp) ] with-string-reader ;\r
-\r
-ERROR: invalid-timestamp-format ;\r
-\r
-: check-timestamp ( obj/f -- obj )\r
-    [ invalid-timestamp-format ] unless* ;\r
-\r
-: read-token ( seps -- token )\r
-    [ read-until ] keep member? check-timestamp drop ;\r
-\r
-: read-sp ( -- token ) " " read-token ;\r
-\r
-: checked-number ( str -- n )\r
-    string>number check-timestamp ;\r
-\r
-: parse-rfc822-gmt-offset ( string -- dt )\r
-    dup "GMT" = [ drop instant ] [\r
-        unclip [ \r
-            2 cut [ string>number ] bi@ [ hours ] [ minutes ] bi* time+\r
-        ] dip signed-gmt-offset\r
-    ] if ;\r
-\r
-: (rfc822>timestamp) ( -- timestamp )\r
-    timestamp new\r
-        "," read-token day-abbreviations3 member? check-timestamp drop\r
-        read1 CHAR: \s assert=\r
-        read-sp checked-number >>day\r
-        read-sp month-abbreviations index 1 + check-timestamp >>month\r
-        read-sp checked-number >>year\r
-        ":" read-token checked-number >>hour\r
-        ":" read-token checked-number >>minute\r
-        read-sp checked-number >>second\r
-        readln parse-rfc822-gmt-offset >>gmt-offset ;\r
-\r
-: rfc822>timestamp ( str -- timestamp )\r
-    [ (rfc822>timestamp) ] with-string-reader ;\r
-\r
-: check-day-name ( str -- )\r
-    [ day-abbreviations3 member? ] [ day-names member? ] bi or\r
-    check-timestamp drop ;\r
-\r
-: (cookie-string>timestamp-1) ( -- timestamp )\r
-    timestamp new\r
-        "," read-token check-day-name\r
-        read1 CHAR: \s assert=\r
-        "-" read-token checked-number >>day\r
-        "-" read-token month-abbreviations index 1 + check-timestamp >>month\r
-        read-sp checked-number >>year\r
-        ":" read-token checked-number >>hour\r
-        ":" read-token checked-number >>minute\r
-        read-sp checked-number >>second\r
-        readln parse-rfc822-gmt-offset >>gmt-offset ;\r
-\r
-: cookie-string>timestamp-1 ( str -- timestamp )\r
-    [ (cookie-string>timestamp-1) ] with-string-reader ;\r
-\r
-: (cookie-string>timestamp-2) ( -- timestamp )\r
-    timestamp new\r
-        read-sp check-day-name\r
-        read-sp month-abbreviations index 1 + check-timestamp >>month\r
-        read-sp checked-number >>day\r
-        ":" read-token checked-number >>hour\r
-        ":" read-token checked-number >>minute\r
-        read-sp checked-number >>second\r
-        read-sp checked-number >>year\r
-        readln parse-rfc822-gmt-offset >>gmt-offset ;\r
-\r
-: cookie-string>timestamp-2 ( str -- timestamp )\r
-    [ (cookie-string>timestamp-2) ] with-string-reader ;\r
-\r
-: cookie-string>timestamp ( str -- timestamp )\r
-    {\r
-        [ cookie-string>timestamp-1 ]\r
-        [ cookie-string>timestamp-2 ]\r
-        [ rfc822>timestamp ]\r
-    } attempt-all-quots ;\r
-\r
-: (ymdhms>timestamp) ( -- timestamp )\r
-    read-ymd " " expect read-hms instant <timestamp> ;\r
-\r
-: ymdhms>timestamp ( str -- timestamp )\r
-    [ (ymdhms>timestamp) ] with-string-reader ;\r
-\r
-: (hms>timestamp) ( -- timestamp )\r
-    0 0 0 read-hms instant <timestamp> ;\r
-\r
-: hms>timestamp ( str -- timestamp )\r
-    [ (hms>timestamp) ] with-string-reader ;\r
-\r
-: (ymd>timestamp) ( -- timestamp )\r
-    read-ymd 0 0 0 instant <timestamp> ;\r
-\r
-: ymd>timestamp ( str -- timestamp )\r
-    [ (ymd>timestamp) ] with-string-reader ;\r
-\r
-: (timestamp>ymd) ( timestamp -- )\r
-    { YYYY "-" MM "-" DD } formatted ;\r
-\r
-TYPED: timestamp>ymd ( timestamp: timestamp -- str )\r
-    [ (timestamp>ymd) ] with-string-writer ;\r
-\r
-: (timestamp>hms) ( timestamp -- )\r
-    { hh ":" mm ":" ss } formatted ;\r
-\r
-TYPED: timestamp>hms ( timestamp: timestamp -- str )\r
-    [ (timestamp>hms) ] with-string-writer ;\r
-\r
-TYPED: timestamp>ymdhms ( timestamp: timestamp -- str )\r
-    [\r
-        >gmt\r
-        { (timestamp>ymd) " " (timestamp>hms) } formatted\r
-    ] with-string-writer ;\r
-\r
-: file-time-string ( timestamp -- string )\r
-    [\r
-        {\r
-            MONTH " " DD " "\r
-            [\r
-                dup now [ year>> ] bi@ =\r
-                [ [ hh ":" write ] [ mm ] bi ] [ YYYYY ] if\r
-            ]\r
-        } formatted\r
-    ] with-string-writer ;\r
-\r
-M: timestamp present timestamp>string ;\r
+! Copyright (C) 2008, 2010 Slava Pestov, Doug Coleman.
+! See https://factorcode.org/license.txt for BSD license.
+USING: accessors calendar calendar.english combinators
+formatting grouping io io.streams.string kernel make math
+math.order math.parser present quotations ranges sequences
+splitting strings words ;
+IN: calendar.format
+
+MACRO: formatted ( spec -- quot )
+    [
+        {
+            { [ dup word? ] [ 1quotation ] }
+            { [ dup quotation? ] [ ] }
+            [ [ nip write ] curry ]
+        } cond
+    ] map [ cleave ] curry ;
+
+: pad-00 ( n -- str ) number>string 2 CHAR: 0 pad-head ;
+
+: pad-0000 ( n -- str ) number>string 4 CHAR: 0 pad-head ;
+
+: write-00 ( n -- ) pad-00 write ;
+
+: write-0000 ( n -- ) pad-0000 write ;
+
+: hh ( timestamp -- ) hour>> write-00 ;
+
+: mm ( timestamp -- ) minute>> write-00 ;
+
+: ss ( timestamp -- ) second>> >integer write-00 ;
+
+: ss.SSSSSS ( timestamp -- ) second>> "%09.6f" printf ;
+
+: hhmm ( timestamp -- ) [ hh ] [ mm ] bi ;
+
+: hh:mm ( timestamp -- ) { hh ":" mm } formatted ;
+
+: hh:mm:ss ( timestamp -- ) { hh ":" mm ":" ss } formatted ;
+
+: hh:mm:ss.SSSSSS ( timestamp -- ) { hh ":" mm ":" ss.SSSSSS } formatted ;
+
+: D ( timestamp -- ) day>> number>string write ;
+
+: DD ( timestamp -- ) day>> write-00 ;
+
+: DAY ( timestamp -- ) day-of-week day-abbreviation3 write ;
+
+: MM ( timestamp -- ) month>> write-00 ;
+
+: MONTH ( timestamp -- ) month>> month-abbreviation write ;
+
+: YYYY ( timestamp -- ) year>> write-0000 ;
+
+: YYYY-MM-DD ( timestamp -- ) { YYYY "-" MM "-" DD } formatted ;
+
+GENERIC: day. ( obj -- )
+
+M: integer day.
+    number>string dup length 2 < [ bl ] when write ;
+
+M: timestamp day.
+    day>> day. ;
+
+<PRIVATE
+
+: center. ( str n -- )
+    over length [-] 2/ CHAR: \s <string> write print ;
+
+: month-header. ( year month -- )
+    [ number>string ] [ month-name ] bi* swap " " glue 20 center. ;
+
+: days-header. ( -- )
+    day-abbreviations2 join-words print ;
+
+: days. ( year month -- )
+    [ 1 (day-of-week) dup [ "   " write ] times ]
+    [ (days-in-month) ] 2bi [1..b] [
+        [ day. ] [ + 7 mod zero? [ nl ] [ bl ] if ] bi
+    ] with each nl ;
+
+PRIVATE>
+
+: month. ( timestamp -- )
+    [ year>> ] [ month>> ] bi
+    [ month-header. ] [ days-header. days. ] 2bi ;
+
+GENERIC: year. ( obj -- )
+
+M: integer year.
+    dup number>string 64 center. nl 12 [1..b] [
+        [
+            [ month-name 20 center. ]
+            [ days-header. days. nl nl ] bi
+        ] with-string-writer split-lines
+    ] with map 3 <groups>
+    [ first3 [ "%-20s  %-20s  %-20s\n" printf ] 3each ] each ;
+
+M: timestamp year. year>> year. ;
+
+: timestamp>mdtm ( timestamp -- str )
+    [ { YYYY MM DD hh mm ss } formatted ] with-string-writer ;
+
+: timestamp>ymd ( timestamp -- str )
+    [ YYYY-MM-DD ] with-string-writer ;
+
+: timestamp>hms ( timestamp -- str )
+    [ hh:mm:ss ] with-string-writer ;
+
+: timestamp>ymdhms ( timestamp -- str )
+    [ >gmt { YYYY-MM-DD " " hh:mm:ss } formatted ] with-string-writer ;
+
+: write-gmt-offset-hhmm ( gmt-offset -- )
+    [ hour>> dup 0 < "-" "+" ? write abs write-00 ] [ mm ] bi ;
+
+: write-gmt-offset-hh:mm ( gmt-offset -- )
+    [ hour>> dup 0 < "-" "+" ? write abs write-00 ":" write ] [ mm ] bi ;
+
+: write-gmt-offset ( gmt-offset -- )
+    dup instant = [ drop "GMT" write ] [ write-gmt-offset-hhmm ] if ;
+
+: write-gmt-offset-z ( gmt-offset -- )
+    dup instant = [ drop "Z" write ] [ write-gmt-offset-hh:mm ] if ;
+
+: write-rfc1036 ( timestamp -- )
+    {
+        DAY ", " DD "-" MONTH "-" YYYY " " hh:mm:ss " "
+        [ gmt-offset>> write-gmt-offset ]
+    } formatted ;
+
+: timestamp>rfc1036 ( timestamp -- str )
+    [ write-rfc1036 ] with-string-writer ;
+
+! RFC850 obsoleted by RFC1036
+ALIAS: write-rfc850 write-rfc1036
+ALIAS: timestamp>rfc850 timestamp>rfc1036
+
+: write-rfc2822 ( timestamp -- )
+    {
+        DAY ", " D " " MONTH " " YYYY " " hh:mm:ss " "
+        [ gmt-offset>> write-gmt-offset ]
+    } formatted ;
+
+: timestamp>rfc2822 ( timestamp -- str )
+    [ write-rfc2822 ] with-string-writer ;
+
+! RFC822 obsoleted by RFC2822
+ALIAS: write-rfc822 write-rfc2822
+ALIAS: timestamp>rfc822 timestamp>rfc2822
+
+: write-rfc3339 ( timestamp -- )
+    {
+        YYYY-MM-DD "T" hh:mm:ss.SSSSSS
+        [ gmt-offset>> write-gmt-offset-z ]
+    } formatted ;
+
+: timestamp>rfc3339 ( timestamp -- str )
+    [ write-rfc3339 ] with-string-writer ;
+
+: write-iso8601 ( timestamp -- )
+    {
+        YYYY-MM-DD "T" hh:mm:ss.SSSSSS
+        [ gmt-offset>> write-gmt-offset-hh:mm ]
+    } formatted ;
+
+: timestamp>iso8601 ( timestamp -- str )
+    [ write-iso8601 ] with-string-writer ;
+
+: write-ctime ( timestamp -- )
+    {
+        DAY " " MONTH " " DD " " hh:mm:ss " " YYYY
+    } formatted ;
+
+: timestamp>ctime-string ( timestamp -- str )
+    [ write-ctime ] with-string-writer ;
+
+: timestamp>git-string ( timestamp -- str )
+    [
+        {
+            DAY " " MONTH " " D " " hh:mm:ss " " YYYY " "
+            [ gmt-offset>> write-gmt-offset-hhmm ]
+        } formatted
+    ] with-string-writer ;
+
+: timestamp>http-string ( timestamp -- str )
+    >gmt timestamp>rfc2822 ;
+
+: timestamp>cookie-string ( timestamp -- str )
+    >gmt timestamp>rfc1036 ;
+
+: write-timestamp ( timestamp -- )
+    { DAY ", " D " " MONTH " " YYYY " " hh:mm:ss } formatted ;
+
+: timestamp>string ( timestamp -- str )
+    [ write-timestamp ] with-string-writer ;
+
+M: timestamp present timestamp>string ;
+
+: duration>hm ( duration -- str )
+    [ duration>hours >integer 24 mod pad-00 ]
+    [ duration>minutes >integer 60 mod pad-00 ] bi ":" glue ;
+
+: duration>hms ( duration -- str )
+    [ duration>hm ]
+    [ duration>seconds >integer 60 mod pad-00 ] bi ":" glue ;
+
+: duration>human-readable ( duration -- string )
+    [
+        {
+            [
+                duration>years >integer
+                [
+                    [ number>string ]
+                    [ 1 > " years" " year" ? append , ] bi
+                ] unless-zero
+            ] [
+                duration>days >integer 365 mod
+                [
+                    [ number>string ]
+                    [ 1 > " days" " day" ? append , ] bi
+                ] unless-zero
+            ] [
+                duration>hours >integer 24 mod
+                [
+                    [ number>string ]
+                    [ 1 > " hours" " hour" ? append , ] bi
+                ] unless-zero
+            ] [
+                duration>minutes >integer 60 mod
+                [
+                    [ number>string ]
+                    [ 1 > " minutes" " minute" ? append , ] bi
+                ] unless-zero
+            ] [
+                duration>seconds >integer 60 mod
+                [
+                    number>string " seconds" append ,
+                ] unless-zero
+            ]
+        } cleave
+    ] { } make [ "0 seconds" ] [
+        unclip-last-slice over empty? [ nip ] [
+            [ ", " join ] [ " and " glue ] bi*
+        ] if
+    ] if-empty ;
+
+GENERIC: elapsed-time ( seconds -- string )
+
+M: integer elapsed-time
+    dup 0 < [ "negative seconds" throw ] when [
+        {
+            { 60 "s" }
+            { 60 "m" }
+            { 24 "h" }
+            {  7 "d" }
+            { 52 "w" }
+            {  f "y" }
+        } [
+            [ first [ /mod ] [ dup ] if* ] [ second ] bi swap
+            dup 0 > [ number>string prepend , ] [ 2drop ] if
+        ] each drop
+    ] { } make [ "0s" ] [ reverse join-words ] if-empty ;
+
+M: real elapsed-time
+    >integer elapsed-time ;
+
+M: duration elapsed-time
+    duration>seconds elapsed-time ;
+
+M: timestamp elapsed-time
+    ago elapsed-time ;
+
+: relative-time-offset ( seconds -- string )
+    abs {
+        { [ dup 1 < ] [ drop "just now" ] }
+        { [ dup 60 < ] [ drop "less than a minute" ] }
+        { [ dup 120 < ] [ drop "about a minute" ] }
+        { [ dup 2700 < ] [ 60 /i "%d minutes" sprintf ] }
+        { [ dup 7200 < ] [ drop "about an hour" ] }
+        { [ dup 86400 < ] [ 3600 /i "%d hours" sprintf ] }
+        { [ dup 172800 < ] [ drop "1 day" ] }
+        [ 86400 /i "%d days" sprintf ]
+    } cond ;
+
+GENERIC: relative-time ( seconds -- string )
+
+M: real relative-time
+    [ relative-time-offset ] [
+        dup abs 1 < [
+            drop
+        ] [
+            0 < "hence" "ago" ? " " glue
+        ] if
+    ] bi ;
+
+M: duration relative-time
+    duration>seconds relative-time ;
+
+M: timestamp relative-time
+    ago relative-time ;