1 ! Copyright (C) 2009 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs combinators combinators.short-circuit
4 combinators.smart fry io.encodings.utf8 io.files kernel
5 math.parser math.statistics memoize namespaces sequences
6 splitting unicode calendar arrays ;
9 CONSTANT: zoneinfo-paths
11 "vocab:zoneinfo/africa"
12 "vocab:zoneinfo/antarctica"
14 "vocab:zoneinfo/australasia"
15 "vocab:zoneinfo/europe"
16 "vocab:zoneinfo/northamerica"
17 "vocab:zoneinfo/pacificnew"
18 "vocab:zoneinfo/solar87"
19 "vocab:zoneinfo/solar88"
20 "vocab:zoneinfo/solar89"
21 "vocab:zoneinfo/southamerica"
22 "vocab:zoneinfo/systemv"
23 "vocab:zoneinfo/leapseconds"
28 TUPLE: raw-zone name gmt-offset rules/save format until ;
29 TUPLE: raw-rule name from to type in on at-time save letters ;
30 TUPLE: raw-link from to ;
31 TUPLE: raw-leap year month day hms corr r/s ;
34 TUPLE: rule name from to at-time ;
36 : rule-to ( m string -- m n )
43 : parse-rule ( seq -- rule )
57 ] input<sequence raw-rule boa ;
59 : parse-zone ( seq -- zone )
66 } cleave raw-zone boa ;
68 : parse-partial-zone ( seq -- zone )
69 [ last-zone get name>> ] dip
75 } cleave raw-zone boa ;
77 : parse-link ( seq -- link )
84 ] input<sequence raw-link boa ;
86 : parse-leap ( seq -- link )
97 ] input<sequence raw-leap boa ;
99 : parse-line ( seq -- tuple )
102 { "zone" [ parse-zone dup last-zone set ] }
103 { "rule" [ parse-rule ] }
104 { "link" [ parse-link ] }
105 { "leap" [ parse-leap ] }
106 [ drop harvest parse-partial-zone ]
109 : parse-zoneinfo-file ( path -- seq )
111 [ "#" split1 drop ] map harvest
112 [ "\t " split harvest ] map harvest
113 [ [ parse-line ] map ] with-scope ;
115 MEMO: zoneinfo-files ( -- seq )
116 zoneinfo-paths [ parse-zoneinfo-file ] map ;
118 MEMO: zoneinfo-array ( -- seq )
119 zoneinfo-files concat ;
121 : raw-rule-map ( -- assoc )
122 zoneinfo-array [ raw-rule? ] filter [ name>> ] collect-by ;
124 : raw-zone-map ( -- assoc )
125 zoneinfo-array [ raw-zone? ] filter [ name>> ] collect-by ;
127 GENERIC: zone-matches? ( string rule -- ? )
129 M: raw-rule zone-matches? name>> = ;
130 M: raw-zone zone-matches? name>> = ;
131 M: raw-link zone-matches? from>> = ;
132 M: raw-leap zone-matches? 2drop f ;
134 : find-rules ( string -- rules )
136 [ [ to>> "max" = ] filter ] assoc-map at ;
138 ERROR: zone-not-found name ;
140 : find-zone ( string -- rules )
142 [ last ] assoc-map ?at [ zone-not-found ] unless ;
144 : find-zone-rules ( string -- zone rules )
145 find-zone dup rules/save>> find-rules ;
147 : number>value ( n -- n' )
155 : on>value ( n -- n' )
156 ! "3", "Thu>=8" always >=, "lastFri"
158 { [ dup 3 swap ?nth CHAR: > = ] [
159 3 cut 2 tail [ day-abbreviation3-predicate ] [ string>number ] bi* 2array
161 { [ dup "last" head? ] [ 4 tail day-abbreviation3-index ] }
165 : raw-rule>triple ( raw-rule -- quot )
167 [ from>> string>number ]
168 [ in>> month-abbreviation-index ]
172 ! "Europe/Helsinki" find-zone-rules
176 ! from - year or "min"
178 ! from "1938" or "min"
179 ! to "1945" or "max" or "only"
180 ! type "-" always "-"
181 ! in "Mar" -- 3-letter month name
182 ! on "26" or "Mon>=15" or lastSun lastFri
183 ! at "23:00s" "12:13:00s" "1:00s" "1:00u"
184 ! save "-0:00:05" "1:00" "0:14:15"
185 ! letters "S" or "-" or "AMT" "BDST"
188 ! name "Indian/Maldives"
189 ! gmt-offset "4:54:00" "9:55:56" "-9:55:56"
190 ! rules/save "-" "0:20" "0:30" "1:00" "AN" "W-Eur" "Winn" "Zion" "sol87" "sol88"
191 ! format "LMT" "%s" "%sT" "A%sT" "AC%sT" "ACT"
193 ! { "1847" "Dec" "1" "0:00s" }
194 ! { "1883" "Nov" "18" "12:12:57" }
195 ! { "1989" "Sep" "lastSun" "2:00s" }
198 ! T{ link { from "Asia/Riyadh88" } { to "Mideast/Riyadh88" } }