git.iko.soy/typeable/timezone-olson
on master help sign in
Header of Part 2 in Olson file also contains magic number, version, and padding.
commit9fb3c85037bfa237b24b73d3081cf242e7aac608 authorYitzchak Gale <gale@sefer.org> dateMon 17 May 2010, 12:02 (16 years ago) parent384add4
2 files changed+41−38
Data/Time/LocalTime/TimeZone/Olson/Parse.hs+31−26 Data/Time/LocalTime/TimeZone/Olson/Render.hs+10−12
Data/Time/LocalTime/TimeZone/Olson/Parse.hs+31−26
@@ -44,7 +44,7 @@ import Data.Maybe (listToMaybe, fromMaybe)
4444import Data.Word (Word8)
4545import Data.Int (Int32, Int64)
4646import Data.Typeable (Typeable)
47−import Control.Monad (guard, replicateM, replicateM_)
47+import Control.Monad (guard, replicateM, replicateM_, when)
4848import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
4949
5050-- | An exception indicating that the binary data being parsed was not
@@ -91,26 +91,23 @@ formatError fp e = throwOlson fp $ show e
9191-- | A binary parser for binary Olson timezone files
9292getOlson :: Get OlsonData
9393getOlson = do
94− getByteString 4
95− >>= verify ((== "TZif") . toASCII . B.unpack) "missing magic number"
96− version <- getWord8
97− >>= verify (`elem` [0, 50]) "invalid version number"
98− replicateM_ 15 getWord8 -- padding nulls
99− getOlsonParts version
100−
101−-- There is one part for Version 1 format data, and two parts and a POSIX
102−-- TZ string for Version 2 format data
103−getOlsonParts :: Word8 -> Get OlsonData
104−getOlsonParts 0 = getOlsonPart get32bitInteger
105−getOlsonParts 50 = do part1 <- getOlsonPart get32bitInteger
106− part2 <- getOlsonPart get64bitInteger
107− posixTZ <- getPosixTZ
108− return $ part1 `mappend` part2 `mappend` posixTZ
109−getOlsonParts _ = error "Invalid version number" -- never reached
94+ (version, part1) <- getOlsonPart True get32bitInteger
95+ -- There is one part for Version 1 format data, and two parts and a POSIX
96+ -- TZ string for Version 2 format data
97+ case version of
98+ 0 -> return part1
99+ 50 -> do (_, part2) <- getOlsonPart False get64bitInteger
100+ posixTZ <- getPosixTZ
101+ return $ part1 `mappend` part2 `mappend` posixTZ
102+ _ -> verify (const False) "invalid version number" undefined
110103
111104-- Parse the part of an Olson file that contains timezone data
112−getOlsonPart :: Integral a => Get a -> Get OlsonData
113−getOlsonPart getTime = do
105+getOlsonPart :: Integral a => Bool -> Get a -> Get (Word8, OlsonData)
106+getOlsonPart verifyMagic getTime = do
107+ magic <- fmap (toASCII . B.unpack) $ getByteString 4
108+ when verifyMagic $ verify_ (== "TZif") "missing magic number" magic
109+ version <- getWord8
110+ replicateM_ 15 getWord8 -- padding nulls
114111 tzh_ttisgmtcnt <- get32bitInt
115112 tzh_ttisstdcnt <- get32bitInt
116113 tzh_leapcnt <- get32bitInt
@@ -124,13 +121,16 @@ getOlsonPart getTime = do
124121 leaps <- replicateM tzh_leapcnt $ getLeapInfo getTime
125122 isstds <- replicateM tzh_ttisstdcnt getBool
126123 isgmts <- replicateM tzh_ttisgmtcnt getBool
127− return $ OlsonData
128− (zipWith Transition times indexes)
129− (map (flip lookupAbbr abbr_chars) . zipWith setTtype ttinfos .
130− (++ repeat UnknownType) $
131− zipWith boolsToTType (map Just isstds ++ repeat Nothing) isgmts)
132− leaps
133− Nothing
124+ return
125+ (version,
126+ OlsonData
127+ (zipWith Transition times indexes)
128+ (map (flip lookupAbbr abbr_chars) . zipWith setTtype ttinfos .
129+ (++ repeat UnknownType) $
130+ zipWith boolsToTType (map Just isstds ++ repeat Nothing) isgmts)
131+ leaps
132+ Nothing
133+ )
134134 where
135135 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
136136 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
@@ -203,6 +203,11 @@ verify pred msg val
203203 | pred val = return val
204204 | otherwise = error msg
205205
206+verify_ :: Monad m => (a -> Bool) -> String -> a -> m ()
207+verify_ pred msg val
208+ | pred val = return ()
209+ | otherwise = error msg
210+
206211throwOlson :: FilePath -> String -> IO a
207212throwOlson fp msg = throw . OlsonError $
208213 fp ++ ": invalid timezone file: " ++ msg
Data/Time/LocalTime/TimeZone/Olson/Render.hs+10−12
@@ -81,12 +81,7 @@ renderOlsonToFile fp = L.writeFile fp . runPut . putOlson
8181-- | Render Olson timezone data in binary Olson timezone file format
8282-- as a lazy @ByteString@
8383putOlson :: OlsonData -> Put
84−putOlson olson = do
85− putASCII "TZif"
86− putWord8 version
87− putByteString . B.pack $ replicate 15 0 -- padding nulls
88− putOlsonParts version olson1 olson2 posix
89− flush
84+putOlson olson = putOlsonParts version olson1 olson2 posix >> flush
9085 where
9186 (olson1, olson2, posix) = splitOlson olson
9287 version
@@ -122,14 +117,17 @@ splitOlson (OlsonData transs ttinfos leaps posix) =
122117 sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
123118
124119putOlsonParts :: Word8 -> OlsonData -> OlsonData -> Maybe String -> Put
125−putOlsonParts 0 olson1 _ _ = putOlsonPart put32bitIntegral olson1
126−putOlsonParts _ olson1 olson2 posix = do
127− putOlsonPart put32bitIntegral olson1
128− putOlsonPart put64bitIntegral olson2
120+putOlsonParts 0 olson1 _ _ = putOlsonPart 0 put32bitIntegral olson1
121+putOlsonParts v2 olson1 olson2 posix = do
122+ putOlsonPart v2 put32bitIntegral olson1
123+ putOlsonPart v2 put64bitIntegral olson2
129124 putPosixTZ posix
130125
131−putOlsonPart :: (Integer -> Put) -> OlsonData -> Put
132−putOlsonPart putTime (OlsonData transs ttinfos leaps _) = do
126+putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
127+putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
128+ putASCII "TZif"
129+ putWord8 version
130+ putByteString . B.pack $ replicate 15 0 -- padding nulls
133131 replicateM_ 2 $ putCount ttinfosWithTtype
134132 -- tzh_ttisgmtcnt
135133 -- tzh_ttisstdcnt