git.iko.soy/typeable/timezone-olson
on master help sign in
.gitignorenew file+16
since splitsnapshots
unifiedsplit
@@ -0,0 +1,16 @@
1+dist
2+cabal-dev
3+*.o
4+*.hi
5+*.chi
6+*.chs.h
7+*.dyn_o
8+*.dyn_hi
9+.virtualenv
10+.hpc
11+.hsenv
12+.cabal-sandbox/
13+cabal.sandbox.config
14+*.prof
15+*.aux
16+*.hp
.travis.ymlnew file+107
@@ -0,0 +1,107 @@
1+# This Travis job script has been generated by a script via
2+#
3+# runghc make_travis_yml_2.hs 'timezone-olson.cabal'
4+#
5+# For more information, see https://github.com/hvr/multi-ghc-travis
6+#
7+language: c
8+sudo: false
9+
10+git:
11+ submodules: false # whether to recursively clone submodules
12+
13+cache:
14+ directories:
15+ - $HOME/.cabal/packages
16+ - $HOME/.cabal/store
17+
18+before_cache:
19+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
20+ # remove files that are regenerated by 'cabal update'
21+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
22+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
23+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
24+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
25+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
26+
27+ - rm -rfv $HOME/.cabal/packages/head.hackage
28+
29+matrix:
30+ include:
31+ - compiler: "ghc-8.0.1"
32+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
33+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.1], sources: [hvr-ghc]}}
34+ - compiler: "ghc-8.0.2"
35+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
36+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
37+ - compiler: "ghc-8.2.1"
38+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
39+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.1], sources: [hvr-ghc]}}
40+ - compiler: "ghc-8.2.2"
41+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
42+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
43+ - compiler: "ghc-8.4.1"
44+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
45+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}
46+
47+before_install:
48+ - HC=${CC}
49+ - HCPKG=${HC/ghc/ghc-pkg}
50+ - unset CC
51+ - ROOTDIR=$(pwd)
52+ - mkdir -p $HOME/.local/bin
53+ - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
54+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
55+ - echo $HCNUMVER
56+
57+install:
58+ - cabal --version
59+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
60+ - BENCH=${BENCH---enable-benchmarks}
61+ - TEST=${TEST---enable-tests}
62+ - HADDOCK=${HADDOCK-true}
63+ - INSTALLED=${INSTALLED-true}
64+ - GHCHEAD=${GHCHEAD-false}
65+ - travis_retry cabal update -v
66+ - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
67+ - rm -fv cabal.project cabal.project.local
68+ - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
69+ - "printf 'packages: \".\"\\n' > cabal.project"
70+ - cat cabal.project
71+ - if [ -f "./configure.ac" ]; then
72+ (cd "." && autoreconf -i);
73+ fi
74+ - rm -f cabal.project.freeze
75+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
76+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
77+ - rm -rf .ghc.environment.* "."/dist
78+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
79+
80+# Here starts the actual work to be performed for the package under test;
81+# any command which exits with a non-zero exit code causes the build to fail.
82+script:
83+ # test that source-distributions can be generated
84+ - (cd "." && cabal sdist)
85+ - mv "."/dist/timezone-olson-*.tar.gz ${DISTDIR}/
86+ - cd ${DISTDIR} || false
87+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
88+ - "printf 'packages: timezone-olson-*/*.cabal\\n' > cabal.project"
89+ - cat cabal.project
90+ # this builds all libraries and executables (without tests/benchmarks)
91+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
92+
93+ # Build with installed constraints for packages in global-db
94+ - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi
95+
96+ # build & run tests, build benchmarks
97+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all
98+
99+ # cabal check
100+ - (cd timezone-olson-* && cabal check)
101+
102+ # haddock
103+ - rm -rf ./dist-newstyle
104+ - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
105+
106+# REGENDATA ["timezone-olson.cabal"]
107+# EOF
Data/Time/LocalTime/TimeZone/Olson.hs+2−2
@@ -1,7 +1,7 @@
1-----------------------------------------------------------------------------
1-----------------------------------------------------------------------------
2-- |
2-- |
3-- Module : Data.Time.LocalTime.TimeZone.Olson
3-- Module : Data.Time.LocalTime.TimeZone.Olson
4−-- Copyright : Yitzchak Gale 2010
4+-- Copyright : Yitzchak Gale 2018
5--
5--
6-- Maintainer : Yitzchak Gale <gale@sefer.org>
6-- Maintainer : Yitzchak Gale <gale@sefer.org>
7-- Portability : portable
7-- Portability : portable
@@ -13,7 +13,7 @@
13-- converting the parsed data into @TimeZoneSeries@ and @TimeZone@
13-- converting the parsed data into @TimeZoneSeries@ and @TimeZone@
14-- objects.
14-- objects.
15
15
16−{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
16+{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
17For licensing information, see the BSD3-style license in the file
17For licensing information, see the BSD3-style license in the file
18LICENSE that was originally distributed by the author together with
18LICENSE that was originally distributed by the author together with
19this file. -}
19this file. -}
Data/Time/LocalTime/TimeZone/Olson/Parse.hs+21−14
@@ -3,19 +3,18 @@
3-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
4-- |
4-- |
5-- Module : Data.Time.LocalTime.TimeZone.Olson.Parse
5-- Module : Data.Time.LocalTime.TimeZone.Olson.Parse
6−-- Copyright : Yitzchak Gale 2010
6+-- Copyright : Yitzchak Gale 2018
7--
7--
8-- Maintainer : Yitzchak Gale <gale@sefer.org>
8-- Maintainer : Yitzchak Gale <gale@sefer.org>
9-- Portability : portable
9-- Portability : portable
10--
10--
11−-- A parser for binary Olson timezone files whose format are specified
11+-- A parser for binary Olson timezone files whose format is specified
12-- by the tzfile(5) man page on Unix-like systems. For more
12-- by the tzfile(5) man page on Unix-like systems. For more
13-- information about this format, see
13-- information about this format, see
14-- <http://www.twinsun.com/tz/tz-link.htm>. Functions are provided for
14-- <http://www.twinsun.com/tz/tz-link.htm>. Functions are provided for
15−-- converting the parsed data into @TimeZoneSeries@ and @TimeZone@
15+-- converting the parsed data into 'TimeZoneSeries' objects.
16−-- objects.
17
16
18−{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
17+{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
19For licensing information, see the BSD3-style license in the file
18For licensing information, see the BSD3-style license in the file
20LICENSE that was originally distributed by the author together with
19LICENSE that was originally distributed by the author together with
21this file. -}
20this file. -}
@@ -40,9 +39,12 @@ import Data.Binary.Get (Get, runGet, getWord8, getWord32be, getWord64be,
40import qualified Data.ByteString as B
39import qualified Data.ByteString as B
41import qualified Data.ByteString.Lazy as L
40import qualified Data.ByteString.Lazy as L
42import Data.Monoid (mappend)
41import Data.Monoid (mappend)
42+import Data.List (sortBy, groupBy)
43import Data.Maybe (listToMaybe, fromMaybe)
43import Data.Maybe (listToMaybe, fromMaybe)
44import Data.Word (Word8)
44import Data.Word (Word8)
45import Data.Int (Int32, Int64)
45import Data.Int (Int32, Int64)
46+import Data.Ord (comparing)
47+import Data.Function (on)
46import Data.Typeable (Typeable)
48import Data.Typeable (Typeable)
47import Control.Monad (guard, replicateM, replicateM_, when)
49import Control.Monad (guard, replicateM, replicateM_, when)
48import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
50import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
@@ -58,8 +60,8 @@ instance Exception OlsonError
58-- | Convert parsed Olson timezone data into a @TimeZoneSeries@.
60-- | Convert parsed Olson timezone data into a @TimeZoneSeries@.
59olsonToTimeZoneSeries :: OlsonData -> Maybe TimeZoneSeries
61olsonToTimeZoneSeries :: OlsonData -> Maybe TimeZoneSeries
60olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
62olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
61− fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) $
63+ fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) .
62− reverse ttimes
64+ uniqTimes . sortBy futureToPast $ ttimes
63 where
65 where
64 dflt = fromMaybe dflt0 . listToMaybe $ filter isStd ttinfos
66 dflt = fromMaybe dflt0 . listToMaybe $ filter isStd ttinfos
65 isStd (TtInfo _ isdst _ _) = not isdst
67 isStd (TtInfo _ isdst _ _) = not isdst
@@ -68,6 +70,8 @@ olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
68 lookupTZ ttinfos ttime = fmap (((,) $ toUTC ttime) . mkTZ) . listToMaybe $
70 lookupTZ ttinfos ttime = fmap (((,) $ toUTC ttime) . mkTZ) . listToMaybe $
69 drop (transIndex ttime) ttinfos
71 drop (transIndex ttime) ttinfos
70 toUTC = posixSecondsToUTCTime . fromIntegral . transTime
72 toUTC = posixSecondsToUTCTime . fromIntegral . transTime
73+ uniqTimes = map last . groupBy ((==) `on` transTime)
74+ futureToPast = comparing $ negate . transTime
71olsonToTimeZoneSeries _ = Nothing
75olsonToTimeZoneSeries _ = Nothing
72
76
73-- | Read timezone data from a binary Olson timezone file and convert
77-- | Read timezone data from a binary Olson timezone file and convert
@@ -103,13 +107,16 @@ getOlson :: SizeLimits -> Get OlsonData
103getOlson limits = do
107getOlson limits = do
104 (version, part1) <- getOlsonPart True limits get32bitInteger
108 (version, part1) <- getOlsonPart True limits get32bitInteger
105 -- There is one part for Version 1 format data, and two parts and a POSIX
109 -- There is one part for Version 1 format data, and two parts and a POSIX
106− -- TZ string for Version 2 format data
110+ -- TZ string for Version 2 or 3 format data
107− case version of
111+ case () of
108− 0 -> return part1
112+ _ | version == 0 -> return part1
113+ | version == 50 || version == 51 -> do
109− 50 -> do (_, part2) <- getOlsonPart False limits get64bitInteger
114+ (_, part2) <- getOlsonPart False limits get64bitInteger
110− posixTZ <- getPosixTZ
115+ posixTZ <- getPosixTZ
111− return $ part1 `mappend` part2 `mappend` posixTZ
116+ return $ part1 `mappend` part2 `mappend` posixTZ
117+ | otherwise -> do
118+ let msg = "getOlson: invalid tzfile version " ++ toASCII [version]
112− _ -> verify (const False) "invalid version number" undefined
119+ verify (const False) msg undefined
113
120
114-- Parse the part of an Olson file that contains timezone data
121-- Parse the part of an Olson file that contains timezone data
115getOlsonPart :: Integral a => Bool -> SizeLimits -> Get a ->
122getOlsonPart :: Integral a => Bool -> SizeLimits -> Get a ->
Data/Time/LocalTime/TimeZone/Olson/Render.hs+47−54
@@ -2,7 +2,7 @@
2-----------------------------------------------------------------------------
2-----------------------------------------------------------------------------
3-- |
3-- |
4-- Module : Data.Time.LocalTime.TimeZone.Olson.Render
4-- Module : Data.Time.LocalTime.TimeZone.Olson.Render
5−-- Copyright : Yitzchak Gale 2010
5+-- Copyright : Yitzchak Gale 2018
6--
6--
7-- Maintainer : Yitzchak Gale <gale@sefer.org>
7-- Maintainer : Yitzchak Gale <gale@sefer.org>
8-- Portability : portable
8-- Portability : portable
@@ -12,7 +12,7 @@
12-- Unix-like systems. For more information about this format, see
12-- Unix-like systems. For more information about this format, see
13-- <http://www.twinsun.com/tz/tz-link.htm>.
13-- <http://www.twinsun.com/tz/tz-link.htm>.
14
14
15−{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
15+{- Copyright (c) 2018ZZ Yitzchak Gale. All rights reserved.
16For licensing information, see the BSD3-style license in the file
16For licensing information, see the BSD3-style license in the file
17LICENSE that was originally distributed by the author together with
17LICENSE that was originally distributed by the author together with
18this file. -}
18this file. -}
@@ -30,7 +30,7 @@ module Data.Time.LocalTime.TimeZone.Olson.Render
30 renderOlsonToFile,
30 renderOlsonToFile,
31 verifyOlsonLimits,
31 verifyOlsonLimits,
32 putOlson,
32 putOlson,
33− splitOlson
33+ extractOlsonV1
34)
34)
35where
35where
36
36
@@ -69,9 +69,9 @@ timeZoneSeriesToOlson (TimeZoneSeries dflt pairs)
69 | otherwise = Just $
69 | otherwise = Just $
70 OlsonData
70 OlsonData
71 [Transition secs ttinfo |
71 [Transition secs ttinfo |
72− (t, tzs) <- reverse pairs,
72+ (t, tz) <- reverse pairs,
73 let secs = round $ utcTimeToPOSIXSeconds t,
73 let secs = round $ utcTimeToPOSIXSeconds t,
74− ttinfo <- maybeToList $ lookup (mkTT tzs) ttAssocs]
74+ ttinfo <- maybeToList $ lookup (mkTT tz) ttAssocs]
75 ttinfos
75 ttinfos
76 []
76 []
77 Nothing
77 Nothing
@@ -110,66 +110,52 @@ renderOlsonToFile fp olson = do
110
110
111-- | Render Olson timezone data in binary Olson timezone file format
111-- | Render Olson timezone data in binary Olson timezone file format
112-- as a lazy @ByteString@
112-- as a lazy @ByteString@
113−putOlson :: OlsonData -> Put
113+putOlson olson@(OlsonData _ _ _ posix)
114−putOlson olson = putOlsonParts version olson1 olson2 posix >> flush
114+ | fitsInVersion1 = putOlsonPart 0 put32bitIntegral olson
115+ | otherwise = do putOlsonPart 50 put32bitIntegral olson1
116+ putOlsonPart 50 put64bitIntegral olson
117+ putPosixTZ posix
115 where
118 where
116− (olson1, olson2, posix) = splitOlson olson
119+ olson1 = extractOlsonV1 olson
117− version
120+ fitsInVersion1 = fmap null posix /= Just False &&
118− | olson2 /= mempty = 50
121+ olson1 == olson {olsonPosixTZ = Nothing}
119− | otherwise = maybe 0 (const 50) $ posix >>= guard . not . null
122+
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+-- | Extract Olson timezone data that can be rendered using Version 1 format
123−-- rendered using Version 2 format, and the POSIX-style TZ string
124−-- if specified
125−splitOlson :: OlsonData -> (OlsonData, OlsonData, Maybe String)
124+extractOlsonV1 :: OlsonData -> OlsonData
125+extractOlsonV1 (OlsonData transs ttinfos leaps _)
126−splitOlson (OlsonData transs ttinfos leaps posix) =
126+ | allV1 = OlsonData transs ttinfos leaps Nothing
127− (OlsonData transs1 ttinfos1 leaps1 Nothing,
127+ | otherwise = OlsonData transs1 ttinfos1 leaps1 Nothing
128− OlsonData transs2 ttinfos2 leaps2 Nothing,
129− posix)
130 where
128 where
131 cutoff = 0x80000000 -- 2^31
129 cutoff = 0x80000000 -- 2^31
132 fitsIn32bits x = x < cutoff && x >= negate cutoff
130 fitsIn32bits x = x < cutoff && x >= negate cutoff
133− ( leaps1 , leaps2 ) = partition (fitsIn32bits . leapTime) leaps
131+ leaps1 = filter (fitsIn32bits . leapTime) leaps
134− (transs1', transs2') = partition (fitsIn32bits . transTime) transs
132+ transs1' = filter (fitsIn32bits . transTime) transs
135− assoc1 = mkAssoc [0] transs1'
133+ allV1 = length leaps1 == length leaps && length transs1' == length transs
136− assoc2 = mkAssoc [] transs2'
134+ assoc1 = zip
137− transs1 = mkTranss transs1' assoc1
138− transs2 = mkTranss transs2' assoc2
139− ttinfos1 = mkTtinfos assoc1
140− ttinfos2 = mkTtinfos assoc2
141− mkAssoc prepend transs' = zip
142 (sortBy (comparing $ fmap tt_ttype . listToMaybe . flip drop ttinfos) .
135 (sortBy (comparing $ fmap tt_ttype . listToMaybe . flip drop ttinfos) .
143− uniq . sort . (prepend ++) $ map transIndex transs')
136+ uniq . sort . (0 :) $ map transIndex transs1')
144 [0..]
137 [0..]
145− mkTranss transs' assoc = [t {transIndex = i} |
138+ transs1 = [t {transIndex = i} |
146− t <- transs', i <- maybeToList $ lookup (transIndex t) assoc]
139+ t <- transs1', i <- maybeToList $ lookup (transIndex t) assoc1]
147− mkTtinfos assoc = map snd . dropWhile (isNothing . fst) .
140+ ttinfos1 = map snd . dropWhile (isNothing . fst) .
148− sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
141+ sortBy (comparing fst) $ zip (map (flip lookup assoc1) [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
156
142
157putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
143putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
158putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
144putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
159− putASCII "TZif"
145+ putASCII "magic number" "TZif"
160 putWord8 version
146 putWord8 version
161 putByteString . B.pack $ replicate 15 0 -- padding nulls
147 putByteString . B.pack $ replicate 15 0 -- padding nulls
162 replicateM_ 2 $ putCount ttinfosWithTtype
148 replicateM_ 2 $ putCount ttinfosWithTtype
163− -- tzh_ttisgmtcnt
149+ -- tzh_ttisgmtcnt
164− -- tzh_ttisstdcnt
150+ -- tzh_ttisstdcnt
165− putCount leaps -- tzh_leapcnt
151+ putCount leaps -- tzh_leapcnt
166− putCount transs -- tzh_timecnt
152+ putCount transs -- tzh_timecnt
167− putCount ttinfos -- tzh_typecnt
153+ putCount ttinfos -- tzh_typecnt
168− putCount abbrChars -- tzh_charcnt
154+ put32bitIntegral abbrChars -- tzh_charcnt
169 mapM_ (putTime . transTime ) transs
155 mapM_ (putTime . transTime ) transs
170 mapM_ (put8bitIntegral . transIndex) transs
156 mapM_ (put8bitIntegral . transIndex) transs
171 mapM_ putTtInfo ttinfosIndexed
157 mapM_ putTtInfo ttinfosIndexed
172− putASCII abbrChars
158+ mapM_ putAbbr abbrStrings
173 mapM_ (putLeapInfo putTime) leaps
159 mapM_ (putLeapInfo putTime) leaps
174 mapM_ (putBool . (== Std) . tt_ttype) ttinfosWithTtype -- isstd
160 mapM_ (putBool . (== Std) . tt_ttype) ttinfosWithTtype -- isstd
175 mapM_ (putBool . (== UTC) . tt_ttype) ttinfosWithTtype -- isgmt
161 mapM_ (putBool . (== UTC) . tt_ttype) ttinfosWithTtype -- isgmt
@@ -177,7 +163,8 @@ putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
177 putCount = put32bitIntegral . length
163 putCount = put32bitIntegral . length
178 ttinfosWithTtype = takeWhile ((<= UTC) . tt_ttype) ttinfosIndexed
164 ttinfosWithTtype = takeWhile ((<= UTC) . tt_ttype) ttinfosIndexed
179 abbrStrings = uniq . sort $ map tt_abbr ttinfos
165 abbrStrings = uniq . sort $ map tt_abbr ttinfos
180− abbrChars = concatMap (++ "\NUL") abbrStrings
166+ abbrChars = sum (map length abbrStrings) + length abbrStrings
167+ putAbbr abbr = putASCII "time zone abbreviation" abbr >> putWord8 0
181 abbrAssocs = zip abbrStrings . scanl (+) 0 $
168 abbrAssocs = zip abbrStrings . scanl (+) 0 $
182 map ((+ 1) . length) abbrStrings
169 map ((+ 1) . length) abbrStrings
183 ttinfosIndexed = [TtInfo gmtoff isdst ttype i |
170 ttinfosIndexed = [TtInfo gmtoff isdst ttype i |
@@ -187,7 +174,7 @@ putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
187putPosixTZ :: Maybe String -> Put
174putPosixTZ :: Maybe String -> Put
188putPosixTZ posix = do
175putPosixTZ posix = do
189 putWord8 10
176 putWord8 10
190− putASCII $ fromMaybe "" posix
177+ putASCII "POSIX TZ string"$ fromMaybe "" posix
191 putWord8 10
178 putWord8 10
192
179
193putTtInfo :: TtInfo Int -> Put
180putTtInfo :: TtInfo Int -> Put
@@ -220,5 +207,11 @@ putBool True = putWord8 1
220uniq :: Eq a => [a] -> [a]
207uniq :: Eq a => [a] -> [a]
221uniq = map head . group
208uniq = map head . group
222
209
223−putASCII :: String -> Put
210+putASCII :: String -> String -> Put
211+putASCII what =
224−putASCII = putByteString . B.pack . map (fromIntegral . fromEnum)
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"
Data/Time/LocalTime/TimeZone/Olson/Types.hs+45−18
@@ -1,8 +1,9 @@
1{-# OPTIONS_HADDOCK hide #-}
1{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE CPP #-}
2-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
3-- |
4-- |
4-- Module : Data.Time.LocalTime.TimeZone.Olson.Types
5-- Module : Data.Time.LocalTime.TimeZone.Olson.Types
5−-- Copyright : Yitzchak Gale 2010
6+-- Copyright : Yitzchak Gale 2018
6--
7--
7-- Maintainer : Yitzchak Gale <gale@sefer.org>
8-- Maintainer : Yitzchak Gale <gale@sefer.org>
8-- Portability : portable
9-- Portability : portable
@@ -14,7 +15,7 @@
14--
15--
15-- Both Version 1 and Version 2 timezone data can be represented.
16-- Both Version 1 and Version 2 timezone data can be represented.
16
17
17−{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
18+{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
18For licensing information, see the BSD3-style license in the file
19For licensing information, see the BSD3-style license in the file
19LICENSE that was originally distributed by the author together with
20LICENSE that was originally distributed by the author together with
20this file. -}
21this file. -}
@@ -36,22 +37,33 @@ module Data.Time.LocalTime.TimeZone.Olson.Types
36)
37)
37where
38where
38
39
40+#if MIN_VERSION_base(4,11,0)
41+import Data.Semigroup (Semigroup(..))
42+#endif
39import Data.Monoid (Monoid(..))
43import Data.Monoid (Monoid(..))
40import Control.Monad (mplus)
44import Control.Monad (mplus)
41
45
42-- | @OlsonData@ represents a full set of timezone data for a location.
46-- | @OlsonData@ represents a full set of timezone data for a location.
43--
47--
44−-- @OlsonData@ can represent timezone data from files in Version 1 format
48+-- @OlsonData@ can represent timezone data from files in Version 1, 2,
45−-- or Version 2 format. Version 1 format files can only contain timestamp
49+-- or 3 format.
46−-- values that can be represented in less than 32 bits, and cannot contain
47−-- a POSIX TZ string.
48--
50--
51+-- Version 1 format files can only contain timestamp values that can
52+-- be represented in less than 32 bits, and cannot contain a POSIX TZ
53+-- string.
54+--
49−-- In a Version 2 format file, the timezone data is split into two parts.
55+-- In a Version 2 format file, the timezone data is split into two
50−-- The first part contains timezone data for which all timestamp values
56+-- parts. The first part contains timezone data for which all
51−-- can be represented in less than 32 bits, and the second part contains
57+-- timestamp values can be represented in less than 32 bits, and the
52−-- timezone data for which 32 bits or more are required to represent
58+-- second part contains timezone data for which 32 bits or more are
53−-- timestamp values. The POSIX TZ string, if present, can only be rendered
59+-- required to represent timestamp values. The POSIX TZ string, if
60+-- present, can only be rendered in a Version 2 file, and appears
54−-- in a Version 2 file, and appears after both parts of the timezone data.
61+-- after both parts of the timezone data.
62+--
63+-- Version 3 format files relax certain syntax requirements for the
64+-- POSIX TZ string. Since we represent the POSIX TZ string as an
65+-- unparsed 'String', Version 3 is identical to Version 2 for our
66+-- purposes.
55data OlsonData =
67data OlsonData =
56 OlsonData {
68 OlsonData {
57 olsonTransitions :: [Transition],
69 olsonTransitions :: [Transition],
@@ -63,10 +75,25 @@ data OlsonData =
63 }
75 }
64 deriving (Eq, Show)
76 deriving (Eq, Show)
65
77
78+#if MIN_VERSION_base(4,11,0)
79+instance Semigroup OlsonData where
80+ OlsonData a b c d <> OlsonData a' b' c' d' =
81+ OlsonData (a ++ map (shiftBy $ length b) a')
82+ (b ++ b') (c ++ c') (d `mplus` d')
83+ where
84+ shiftBy n trans = trans {transIndex = n + transIndex trans}
85+
86+instance Monoid OlsonData where
87+ mempty = OlsonData [] [] [] Nothing
88+#else
66instance Monoid OlsonData where
89instance Monoid OlsonData where
67 mempty = OlsonData [] [] [] Nothing
90 mempty = OlsonData [] [] [] Nothing
68− mappend (OlsonData a b c d ) (OlsonData a' b' c' d')
91+ mappend (OlsonData a b c d ) (OlsonData a' b' c' d') =
69− = OlsonData (a++a') (b++b') (c++c') (d `mplus` d')
92+ OlsonData (a ++ map (shiftBy $ length b) a')
93+ (b ++ b') (c ++ c') (d `mplus` d')
94+ where
95+ shiftBy n trans = trans {transIndex = n + transIndex trans}
96+#endif
70
97
71-- | A @Transition@ represents a moment when the clocks change in a
98-- | A @Transition@ represents a moment when the clocks change in a
72-- timezone. It consists of a Unix timestamp value that indicates the
99-- timezone. It consists of a Unix timestamp value that indicates the
@@ -143,9 +170,9 @@ defaultLimits = SizeLimits { maxTimes = Just 1200, maxTypes = Just 256,
143limitsNoSolar :: SizeLimits
170limitsNoSolar :: SizeLimits
144limitsNoSolar = defaultLimits { maxTypes = Just 20 }
171limitsNoSolar = defaultLimits { maxTypes = Just 20 }
145
172
146−-- | @noLimits@ imoses no size limits. If you use @noLimits@ when parsing,
173+-- | @noLimits@ imposes no size limits. If you use @noLimits@ when
147−-- you may exhaust all available memory when reading a faulty or
174+-- parsing, you may exhaust all available memory when reading a faulty
148−-- malicious timezone file. If you use @noLimits@ when rendering, the
175+-- or malicious timezone file. If you use @noLimits@ when rendering,
149−-- rendered timezone file might not be readable on some sytems.
176+-- the rendered timezone file might not be readable on some systems.
150noLimits :: SizeLimits
177noLimits :: SizeLimits
151noLimits = SizeLimits Nothing Nothing Nothing Nothing
178noLimits = SizeLimits Nothing Nothing Nothing Nothing
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2010 Yitzchak Gale. All rights reserved.
1+Copyright (c) 2010-2018 Yitzchak Gale. All rights reserved.
2
2
3Redistribution and use in source and binary forms, with or without
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are
4modification, are permitted provided that the following conditions are
README.mdnew file+43
@@ -0,0 +1,43 @@
1+# timezone-olson
2+
3+On Hackage: [timezone-olson](http://hackage.haskell.org/package/timezone-olson)
4+
5+This package provides a parser and renderer for binary Olson timezone
6+files whose format is specified by the tzfile(5) man page on Unix-like
7+systems. For more information about this format, see
8+[the IANA timezone database site](https://www.iana.org/time-zones).
9+
10+Functions are provided for
11+converting the parsed data into `TimeZoneSeries` objects from the
12+[timezone-series](http://hackage.haskell.org/package/timezone-series)
13+package. On many platforms, binary Olson timezone
14+files suitable for use with this package are available in the
15+directory `/usr/share/zoneinfo` and its subdirectories on your computer.
16+
17+See also the
18+[timezone-olson-th](http://hackage.haskell.org/package/timezone-olson-th)
19+package for a way to include timezone informaton from a binary Olson
20+timezone file at compile time.
21+
22+Copyright (c) 2010-2018 Yitzchak Gale. All rights reserved.
23+
24+For licensing information, see the BSD3-style license in the file
25+LICENSE that was originally distributed by the author together with
26+this file.
27+
28+This package is part of the [time-ng project](http://projects.haskell.org/time-ng/).
29+
30+## Testing utilities
31+
32+This package also provides two Haskell files, each of which can be
33+compiled into a command-line utility that might be helpful for testing
34+purposes.
35+
36+zhdump.hs: A clone of zdump(8), including most of its bugs, that
37+ is usually present on systems that have an Olson timezone
38+ database, except hzdump takes paths to timezone files
39+ instead of timezone identifiers on the command line.
40+
41+catTZ: Read and parse a timezone file, then render it. With the
42+ -i flag, interprets the timezone data as a TimeZoneSeries
43+ object before rendering.
Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
catTZ.hsnew file+14
@@ -0,0 +1,14 @@
1+-- | Parse an Olson timezone file, then render it.
2+-- If -i is specified, interpret it as a TimeZoneSeries before rendering.
3+module Main where
4+
5+import Data.Time.LocalTime.TimeZone.Olson
6+import Data.Time.LocalTime.TimeZone.Series
7+import System.Environment
8+
9+main = do
10+ args <- getArgs
11+ if head args == "-i"
12+ then getTimeZoneSeriesFromOlsonFile (args !! 1) >>=
13+ renderTimeZoneSeriesToOlsonFile (args !! 2)
14+ else getOlsonFromFile (args !! 0) >>= renderOlsonToFile (args !! 1)
timezone-olson.cabalnew file+45
@@ -0,0 +1,45 @@
1+Name: timezone-olson
2+Version: 0.1.9
3+Synopsis: A pure Haskell parser and renderer for binary Olson timezone files
4+Description: A parser and renderer for binary Olson timezone
5+ files whose format is specified by the tzfile(5)
6+ man page on Unix-like systems. For more
7+ information about this format, see
8+ <http://www.iana.org/time-zones/repository/tz-link.html>.
9+ Functions are provided for converting the parsed data into
10+ 'TimeZoneSeries' objects from the timezone-series
11+ package. On many platforms, binary Olson timezone
12+ files suitable for use with this package are
13+ available in the directory /usr/share/zoneinfo
14+ and its subdirectories on your computer.
15+ For a way to read binary Olson timezone files at compile
16+ time, see the timezone-olson-th package
17+ (<http://hackage.haskell.org/package/timezone-olson-th>).
18+Homepage: http://projects.haskell.org/time-ng/
19+License: BSD3
20+License-file: LICENSE
21+Author: Yitzchak Gale
22+Maintainer: yitz@sefer.org
23+Copyright: Copyright (c) 2010-2018 Yitzchak Gale. All rights reserved.
24+Category: Data
25+Build-type: Simple
26+Extra-source-files: README.md, catTZ.hs, hzdump.hs
27+Cabal-version: >=1.10
28+Tested-with: GHC > 8.0 && < 8.5
29+
30+Source-repository HEAD
31+ type: git
32+ location: https://github.com/ygale/timezone-olson.git
33+
34+Library
35+ Default-language: Haskell2010
36+ Exposed-modules: Data.Time.LocalTime.TimeZone.Olson
37+ , Data.Time.LocalTime.TimeZone.Olson.Parse
38+ , Data.Time.LocalTime.TimeZone.Olson.Render
39+ , Data.Time.LocalTime.TimeZone.Olson.Types
40+ Build-depends: base >= 3.0 && < 5.0,
41+ timezone-series >= 0.1.0 && < 0.2,
42+ time >= 1.6 && < 1.10,
43+ binary >= 0.4.1 && < 0.9,
44+ bytestring >= 0.9 && < 1.0,
45+ extensible-exceptions >= 0.1.0 && < 0.2