@@ -2,7 +2,7 @@
22-----------------------------------------------------------------------------
33-- |
44-- Module : Data.Time.LocalTime.TimeZone.Olson.Render
5−-- Copyright : Yitzchak Gale 2018
5+-- Copyright : Yitzchak Gale 2010
66--
77-- Maintainer : Yitzchak Gale <gale@sefer.org>
88-- Portability : portable
@@ -12,7 +12,7 @@
1212-- Unix-like systems. For more information about this format, see
1313-- <http://www.twinsun.com/tz/tz-link.htm>.
1414
15−{- Copyright (c) 2018ZZ Yitzchak Gale. All rights reserved.
15+{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
1616For licensing information, see the BSD3-style license in the file
1717LICENSE that was originally distributed by the author together with
1818this file. -}
@@ -30,7 +30,7 @@ module Data.Time.LocalTime.TimeZone.Olson.Render
3030 renderOlsonToFile,
3131 verifyOlsonLimits,
3232 putOlson,
33− extractOlsonV1
33+ splitOlson
3434)
3535where
3636
@@ -69,9 +69,9 @@ timeZoneSeriesToOlson (TimeZoneSeries dflt pairs)
6969 | otherwise = Just $
7070 OlsonData
7171 [Transition secs ttinfo |
72− (t, tz) <- reverse pairs,
72+ (t, tzs) <- reverse pairs,
7373 let secs = round $ utcTimeToPOSIXSeconds t,
74− ttinfo <- maybeToList $ lookup (mkTT tz) ttAssocs]
74+ ttinfo <- maybeToList $ lookup (mkTT tzs) ttAssocs]
7575 ttinfos
7676 []
7777 Nothing
@@ -110,52 +110,66 @@ renderOlsonToFile fp olson = do
110110
111111-- | Render Olson timezone data in binary Olson timezone file format
112112-- as a lazy @ByteString@
113−putOlson olson@(OlsonData _ _ _ posix)
114− | fitsInVersion1 = putOlsonPart 0 put32bitIntegral olson
115− | otherwise = do putOlsonPart 50 put32bitIntegral olson1
116− putOlsonPart 50 put64bitIntegral olson
117− putPosixTZ posix
113+putOlson :: OlsonData -> Put
114+putOlson olson = putOlsonParts version olson1 olson2 posix >> flush
118115 where
119− olson1 = extractOlsonV1 olson
120− fitsInVersion1 = fmap null posix /= Just False &&
121− olson1 == olson {olsonPosixTZ = Nothing}
122−
123−-- | Extract Olson timezone data that can be rendered using Version 1 format
124−extractOlsonV1 :: OlsonData -> OlsonData
125−extractOlsonV1 (OlsonData transs ttinfos leaps _)
126− | allV1 = OlsonData transs ttinfos leaps Nothing
127− | otherwise = OlsonData transs1 ttinfos1 leaps1 Nothing
116+ (olson1, olson2, posix) = splitOlson olson
117+ version
118+ | olson2 /= mempty = 50
119+ | otherwise = maybe 0 (const 50) $ posix >>= guard . not . null
120+
121+-- | Split Olson timezone data into three parts: timezone data that can
122+-- be rendered using Version 1 format, timezone data that can only be
123+-- rendered using Version 2 format, and the POSIX-style TZ string
124+-- if specified
125+splitOlson :: OlsonData -> (OlsonData, OlsonData, Maybe String)
126+splitOlson (OlsonData transs ttinfos leaps posix) =
127+ (OlsonData transs1 ttinfos1 leaps1 Nothing,
128+ OlsonData transs2 ttinfos2 leaps2 Nothing,
129+ posix)
128130 where
129131 cutoff = 0x80000000 -- 2^31
130132 fitsIn32bits x = x < cutoff && x >= negate cutoff
131− leaps1 = filter (fitsIn32bits . leapTime) leaps
132− transs1' = filter (fitsIn32bits . transTime) transs
133− allV1 = length leaps1 == length leaps && length transs1' == length transs
134− assoc1 = zip
133+ ( leaps1 , leaps2 ) = partition (fitsIn32bits . leapTime) leaps
134+ (transs1', transs2') = partition (fitsIn32bits . transTime) transs
135+ assoc1 = mkAssoc [0] transs1'
136+ assoc2 = mkAssoc [] transs2'
137+ transs1 = mkTranss transs1' assoc1
138+ transs2 = mkTranss transs2' assoc2
139+ ttinfos1 = mkTtinfos assoc1
140+ ttinfos2 = mkTtinfos assoc2
141+ mkAssoc prepend transs' = zip
135142 (sortBy (comparing $ fmap tt_ttype . listToMaybe . flip drop ttinfos) .
136− uniq . sort . (0 :) $ map transIndex transs1')
143+ uniq . sort . (prepend ++) $ map transIndex transs')
137144 [0..]
138− transs1 = [t {transIndex = i} |
139− t <- transs1', i <- maybeToList $ lookup (transIndex t) assoc1]
140− ttinfos1 = map snd . dropWhile (isNothing . fst) .
141− sortBy (comparing fst) $ zip (map (flip lookup assoc1) [0..]) ttinfos
145+ mkTranss transs' assoc = [t {transIndex = i} |
146+ t <- transs', i <- maybeToList $ lookup (transIndex t) assoc]
147+ mkTtinfos assoc = map snd . dropWhile (isNothing . fst) .
148+ sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
149+
150+putOlsonParts :: Word8 -> OlsonData -> OlsonData -> Maybe String -> Put
151+putOlsonParts 0 olson1 _ _ = putOlsonPart 0 put32bitIntegral olson1
152+putOlsonParts v2 olson1 olson2 posix = do
153+ putOlsonPart v2 put32bitIntegral olson1
154+ putOlsonPart v2 put64bitIntegral olson2
155+ putPosixTZ posix
142156
143157putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
144158putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
145− putASCII "magic number" "TZif"
159+ putASCII "TZif"
146160 putWord8 version
147161 putByteString . B.pack $ replicate 15 0 -- padding nulls
148162 replicateM_ 2 $ putCount ttinfosWithTtype
149− -- tzh_ttisgmtcnt
150− -- tzh_ttisstdcnt
151− putCount leaps -- tzh_leapcnt
152− putCount transs -- tzh_timecnt
153− putCount ttinfos -- tzh_typecnt
154− put32bitIntegral abbrChars -- tzh_charcnt
163+ -- tzh_ttisgmtcnt
164+ -- tzh_ttisstdcnt
165+ putCount leaps -- tzh_leapcnt
166+ putCount transs -- tzh_timecnt
167+ putCount ttinfos -- tzh_typecnt
168+ putCount abbrChars -- tzh_charcnt
155169 mapM_ (putTime . transTime ) transs
156170 mapM_ (put8bitIntegral . transIndex) transs
157171 mapM_ putTtInfo ttinfosIndexed
158− mapM_ putAbbr abbrStrings
172+ putASCII abbrChars
159173 mapM_ (putLeapInfo putTime) leaps
160174 mapM_ (putBool . (== Std) . tt_ttype) ttinfosWithTtype -- isstd
161175 mapM_ (putBool . (== UTC) . tt_ttype) ttinfosWithTtype -- isgmt
@@ -163,8 +177,7 @@ putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
163177 putCount = put32bitIntegral . length
164178 ttinfosWithTtype = takeWhile ((<= UTC) . tt_ttype) ttinfosIndexed
165179 abbrStrings = uniq . sort $ map tt_abbr ttinfos
166− abbrChars = sum (map length abbrStrings) + length abbrStrings
167− putAbbr abbr = putASCII "time zone abbreviation" abbr >> putWord8 0
180+ abbrChars = concatMap (++ "\NUL") abbrStrings
168181 abbrAssocs = zip abbrStrings . scanl (+) 0 $
169182 map ((+ 1) . length) abbrStrings
170183 ttinfosIndexed = [TtInfo gmtoff isdst ttype i |
@@ -174,7 +187,7 @@ putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
174187putPosixTZ :: Maybe String -> Put
175188putPosixTZ posix = do
176189 putWord8 10
177− putASCII "POSIX TZ string"$ fromMaybe "" posix
190+ putASCII $ fromMaybe "" posix
178191 putWord8 10
179192
180193putTtInfo :: TtInfo Int -> Put
@@ -207,11 +220,5 @@ putBool True = putWord8 1
207220uniq :: Eq a => [a] -> [a]
208221uniq = map head . group
209222
210−putASCII :: String -> String -> Put
211−putASCII what =
212− putByteString . B.pack . map (fromIntegral . verify . fromEnum)
213− where
214− verify c
215− | c >= 32 && c <= 126 = c
216− | otherwise = error $ "Cannot render TimeZoneSeries: " ++
217− what ++ " contains non-ASCII characters"
223+putASCII :: String -> Put
224+putASCII = putByteString . B.pack . map (fromIntegral . fromEnum)