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.
9fb3c85Yitzchak Gale
Data/Time/LocalTime/TimeZone/Olson/Parse.hs+31−26
@@ -44,7 +44,7 @@ import Data.Maybe (listToMaybe, fromMaybe)
44import Data.Word (Word8)
44import Data.Word (Word8)
45import Data.Int (Int32, Int64)
45import Data.Int (Int32, Int64)
46import Data.Typeable (Typeable)
46import Data.Typeable (Typeable)
47−import Control.Monad (guard, replicateM, replicateM_)
47+import Control.Monad (guard, replicateM, replicateM_, when)
48import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
48import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
49
49
50-- | An exception indicating that the binary data being parsed was not
50-- | An exception indicating that the binary data being parsed was not
@@ -91,26 +91,23 @@ formatError fp e = throwOlson fp $ show e
91-- | A binary parser for binary Olson timezone files
91-- | A binary parser for binary Olson timezone files
92getOlson :: Get OlsonData
92getOlson :: Get OlsonData
93getOlson = do
93getOlson = do
94− getByteString 4
94+ (version, part1) <- getOlsonPart True get32bitInteger
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
95+ -- There is one part for Version 1 format data, and two parts and a POSIX
102−-- TZ string for Version 2 format data
96+ -- TZ string for Version 2 format data
103−getOlsonParts :: Word8 -> Get OlsonData
97+ case version of
104−getOlsonParts 0 = getOlsonPart get32bitInteger
98+ 0 -> return part1
105−getOlsonParts 50 = do part1 <- getOlsonPart get32bitInteger
106− part2 <- getOlsonPart get64bitInteger
99+ 50 -> do (_, part2) <- getOlsonPart False get64bitInteger
107− posixTZ <- getPosixTZ
100+ posixTZ <- getPosixTZ
108− return $ part1 `mappend` part2 `mappend` posixTZ
101+ return $ part1 `mappend` part2 `mappend` posixTZ
109−getOlsonParts _ = error "Invalid version number" -- never reached
102+ _ -> verify (const False) "invalid version number" undefined
110
103
111-- Parse the part of an Olson file that contains timezone data
104-- Parse the part of an Olson file that contains timezone data
112−getOlsonPart :: Integral a => Get a -> Get OlsonData
105+getOlsonPart :: Integral a => Bool -> Get a -> Get (Word8, OlsonData)
113−getOlsonPart getTime = do
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
114 tzh_ttisgmtcnt <- get32bitInt
111 tzh_ttisgmtcnt <- get32bitInt
115 tzh_ttisstdcnt <- get32bitInt
112 tzh_ttisstdcnt <- get32bitInt
116 tzh_leapcnt <- get32bitInt
113 tzh_leapcnt <- get32bitInt
@@ -124,13 +121,16 @@ getOlsonPart getTime = do
124 leaps <- replicateM tzh_leapcnt $ getLeapInfo getTime
121 leaps <- replicateM tzh_leapcnt $ getLeapInfo getTime
125 isstds <- replicateM tzh_ttisstdcnt getBool
122 isstds <- replicateM tzh_ttisstdcnt getBool
126 isgmts <- replicateM tzh_ttisgmtcnt getBool
123 isgmts <- replicateM tzh_ttisgmtcnt getBool
124+ return
125+ (version,
127− return $ OlsonData
126+ OlsonData
128− (zipWith Transition times indexes)
127+ (zipWith Transition times indexes)
129− (map (flip lookupAbbr abbr_chars) . zipWith setTtype ttinfos .
128+ (map (flip lookupAbbr abbr_chars) . zipWith setTtype ttinfos .
130− (++ repeat UnknownType) $
129+ (++ repeat UnknownType) $
131− zipWith boolsToTType (map Just isstds ++ repeat Nothing) isgmts)
130+ zipWith boolsToTType (map Just isstds ++ repeat Nothing) isgmts)
132− leaps
131+ leaps
133− Nothing
132+ Nothing
133+ )
134 where
134 where
135 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
135 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
136 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
136 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
@@ -203,6 +203,11 @@ verify pred msg val
203 | pred val = return val
203 | pred val = return val
204 | otherwise = error msg
204 | otherwise = error msg
205
205
206+verify_ :: Monad m => (a -> Bool) -> String -> a -> m ()
207+verify_ pred msg val
208+ | pred val = return ()
209+ | otherwise = error msg
210+
206throwOlson :: FilePath -> String -> IO a
211throwOlson :: FilePath -> String -> IO a
207throwOlson fp msg = throw . OlsonError $
212throwOlson fp msg = throw . OlsonError $
208 fp ++ ": invalid timezone file: " ++ msg
213 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
81-- | Render Olson timezone data in binary Olson timezone file format
81-- | Render Olson timezone data in binary Olson timezone file format
82-- as a lazy @ByteString@
82-- as a lazy @ByteString@
83putOlson :: OlsonData -> Put
83putOlson :: 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
84+putOlson olson = putOlsonParts version olson1 olson2 posix >> flush
89− flush
90 where
85 where
91 (olson1, olson2, posix) = splitOlson olson
86 (olson1, olson2, posix) = splitOlson olson
92 version
87 version
@@ -122,14 +117,17 @@ splitOlson (OlsonData transs ttinfos leaps posix) =
122 sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
117 sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
123
118
124putOlsonParts :: Word8 -> OlsonData -> OlsonData -> Maybe String -> Put
119putOlsonParts :: Word8 -> OlsonData -> OlsonData -> Maybe String -> Put
125−putOlsonParts 0 olson1 _ _ = putOlsonPart put32bitIntegral olson1
120+putOlsonParts 0 olson1 _ _ = putOlsonPart 0 put32bitIntegral olson1
126−putOlsonParts _ olson1 olson2 posix = do
121+putOlsonParts v2 olson1 olson2 posix = do
127− putOlsonPart put32bitIntegral olson1
122+ putOlsonPart v2 put32bitIntegral olson1
128− putOlsonPart put64bitIntegral olson2
123+ putOlsonPart v2 put64bitIntegral olson2
129 putPosixTZ posix
124 putPosixTZ posix
130
125
131−putOlsonPart :: (Integer -> Put) -> OlsonData -> Put
126+putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
132−putOlsonPart putTime (OlsonData transs ttinfos leaps _) = do
127+putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
128+ putASCII "TZif"
129+ putWord8 version
130+ putByteString . B.pack $ replicate 15 0 -- padding nulls
133 replicateM_ 2 $ putCount ttinfosWithTtype
131 replicateM_ 2 $ putCount ttinfosWithTtype
134 -- tzh_ttisgmtcnt
132 -- tzh_ttisgmtcnt
135 -- tzh_ttisstdcnt
133 -- tzh_ttisstdcnt