         [ 5 tail harvest ]
     } cleave raw-zone boa ;
 
-: parse-partial-zone ( seq -- zone )
-    [ last-zone get name>> ] dip
-    {
+: parse-rest-of-zone ( prev seq -- zone )
+    [ name>> ] dip {
         [ first ]
         [ second ]
         [ 2 swap nth ]
         [ 3 tail harvest ]
     } cleave raw-zone boa ;
 
-: parse-zoneinfo-line ( seq -- tuple )
+: parse-zoneinfo-line ( prev/f seq -- tuple )
     dup first >lower
     {
-        { "rule" [ parse-rule ] }
-        { "link" [ parse-link ] }
-        { "leap" [ parse-leap ] }
-        { "zone" [ parse-zone dup last-zone set ] }
-        [ drop harvest parse-partial-zone ]
+        { "rule" [ nip parse-rule ] }
+        { "link" [ nip parse-link ] }
+        { "leap" [ nip parse-leap ] }
+        { "zone" [ nip parse-zone ] }
+        [ drop harvest parse-rest-of-zone ]
     } case ;
 
 : parse-zoneinfo-file ( path -- seq )
     zoneinfo-lines
     [ "\t " split harvest ] map harvest
-    [ parse-zoneinfo-line ] map ;
+    [ parse-zoneinfo-line ] map-with-previous ;
 
 MEMO: zoneinfo-files ( -- seq )
     zoneinfo-paths [ parse-zoneinfo-file ] map ;


: zoneinfo-lines ( path -- seq )
    utf8 file-lines
    [
        { [ length 0 = ] [ "#" head? ] } 1||
    ] reject ;

TUPLE: zonetab codes lat lng tz comments ;
C: <zonetab> zonetab
MEMO: zoneinfo-country-zones ( -- seq )
    "vocab:zoneinfo/zone1970.tab" zoneinfo-lines
    [

M: raw-leap zone-matches? 2drop f ;
M: raw-zone zone-matches? name>> = ;

: find-rules ( country -- rules )
    raw-rule-map
    [ [ to>> "max" = ] filter ] assoc-map at ;

ERROR: zone-not-found name ;

: find-zone ( timezone -- zone )
    raw-zone-map
    [ last ] assoc-map ?at [ zone-not-found ] unless ;

: timezone>rules ( timezone -- rules )
    raw-zone-map at ;

: find-zone-rules ( timezone -- zone rules )
    find-zone dup rules/save>> find-rules ;

: zone-abbrevs ( -- assoc )
