git.iko.soy/typeable/timezone-olson
on 384add4 help sign in
12 files changed+155−524
since splitsnapshots
unifiedsplit
.gitignore−16 .travis.yml−107 Data/Time/LocalTime/TimeZone/Olson.hs+3−3 Data/Time/LocalTime/TimeZone/Olson/Parse.hs+43−79 Data/Time/LocalTime/TimeZone/Olson/Render.hs+72−95 Data/Time/LocalTime/TimeZone/Olson/Types.hs+24−91 LICENSE+1−1 README.md−43 Setup.hs−2 catTZ.hs−14 hzdump.hs+12−28 timezone-olson.cabal−45
.gitignoredeleted−16
@@ -1,16 +0,0 @@
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.ymldeleted−107
@@ -1,107 +0,0 @@
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+3−3
@@ -1,7 +1,7 @@
11-----------------------------------------------------------------------------
22-- |
33-- Module : Data.Time.LocalTime.TimeZone.Olson
4−-- Copyright : Yitzchak Gale 2018
4+-- Copyright : Yitzchak Gale 2010
55--
66-- Maintainer : Yitzchak Gale <gale@sefer.org>
77-- Portability : portable
@@ -9,11 +9,11 @@
99-- A parser and renderer for binary Olson timezone files whose format
1010-- are specified by the tzfile(5) man page on Unix-like systems. For
1111-- more information about this format, see
12−-- <http://www.twinsun.com/tz/tz-link.htm>. Functions are provided for
12+-- http://www.twinsun.com/tz/tz-link.htm. Functions are provided for
1313-- converting the parsed data into @TimeZoneSeries@ and @TimeZone@
1414-- objects.
1515
16−{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
16+{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
1717For licensing information, see the BSD3-style license in the file
1818LICENSE that was originally distributed by the author together with
1919this file. -}
Data/Time/LocalTime/TimeZone/Olson/Parse.hs+43−79
@@ -3,18 +3,19 @@
33-----------------------------------------------------------------------------
44-- |
55-- Module : Data.Time.LocalTime.TimeZone.Olson.Parse
6−-- Copyright : Yitzchak Gale 2018
6+-- Copyright : Yitzchak Gale 2010
77--
88-- Maintainer : Yitzchak Gale <gale@sefer.org>
99-- Portability : portable
1010--
11−-- A parser for binary Olson timezone files whose format is specified
11+-- A parser for binary Olson timezone files whose format are specified
1212-- by the tzfile(5) man page on Unix-like systems. For more
1313-- information about this format, see
14−-- <http://www.twinsun.com/tz/tz-link.htm>. Functions are provided for
15−-- converting the parsed data into 'TimeZoneSeries' objects.
14+-- http://www.twinsun.com/tz/tz-link.htm. Functions are provided for
15+-- converting the parsed data into @TimeZoneSeries@ and @TimeZone@
16+-- objects.
1617
17−{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
18+{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
1819For licensing information, see the BSD3-style license in the file
1920LICENSE that was originally distributed by the author together with
2021this file. -}
@@ -39,14 +40,11 @@ import Data.Binary.Get (Get, runGet, getWord8, getWord32be, getWord64be,
3940import qualified Data.ByteString as B
4041import qualified Data.ByteString.Lazy as L
4142import Data.Monoid (mappend)
42−import Data.List (sortBy, groupBy)
4343import Data.Maybe (listToMaybe, fromMaybe)
4444import Data.Word (Word8)
4545import Data.Int (Int32, Int64)
46−import Data.Ord (comparing)
47−import Data.Function (on)
4846import Data.Typeable (Typeable)
49−import Control.Monad (guard, replicateM, replicateM_, when)
47+import Control.Monad (guard, replicateM, replicateM_)
5048import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
5149
5250-- | An exception indicating that the binary data being parsed was not
@@ -60,8 +58,8 @@ instance Exception OlsonError
6058-- | Convert parsed Olson timezone data into a @TimeZoneSeries@.
6159olsonToTimeZoneSeries :: OlsonData -> Maybe TimeZoneSeries
6260olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
63− fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) .
64− uniqTimes . sortBy futureToPast $ ttimes
61+ fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) $
62+ reverse ttimes
6563 where
6664 dflt = fromMaybe dflt0 . listToMaybe $ filter isStd ttinfos
6765 isStd (TtInfo _ isdst _ _) = not isdst
@@ -70,74 +68,55 @@ olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
7068 lookupTZ ttinfos ttime = fmap (((,) $ toUTC ttime) . mkTZ) . listToMaybe $
7169 drop (transIndex ttime) ttinfos
7270 toUTC = posixSecondsToUTCTime . fromIntegral . transTime
73− uniqTimes = map last . groupBy ((==) `on` transTime)
74− futureToPast = comparing $ negate . transTime
7571olsonToTimeZoneSeries _ = Nothing
7672
7773-- | Read timezone data from a binary Olson timezone file and convert
7874-- it into a @TimeZoneSeries@ for use together with the types and
7975-- fucntions of "Data.Time". This is the function from this module
8076-- for which you are most likely to have use.
81−--
82−-- If the values in the Olson timezone file exceed the standard size
83−-- limits (see 'defaultLimits'), this function throws an
84−-- exception. For other behavior, use 'getOlson' and
85−-- 'Data.Binary.Get.runGet' directly.
8677getTimeZoneSeriesFromOlsonFile :: FilePath -> IO TimeZoneSeries
8778getTimeZoneSeriesFromOlsonFile fp = getOlsonFromFile fp >>=
8879 maybe (throwOlson fp "no timezone found in OlsonData") return .
8980 olsonToTimeZoneSeries
9081
9182-- | Parse a binary Olson timezone file.
92−--
93−-- If the values in the Olson timezone file exceed the standard size
94−-- limits (see 'defaultLimits'), this function throws an
95−-- exception. For other behavior, use 'getOlson' and
96−-- 'Data.Binary.Get.runGet' directly.
9783getOlsonFromFile :: FilePath -> IO OlsonData
9884getOlsonFromFile fp = do
99− e <- try . fmap (runGet $ getOlson defaultLimits) $ L.readFile fp
85+ e <- try . fmap (runGet getOlson) $ L.readFile fp
10086 either (formatError fp) return e
10187
10288formatError :: FilePath -> ErrorCall -> IO a
10389formatError fp e = throwOlson fp $ show e
10490
10591-- | A binary parser for binary Olson timezone files
106−getOlson :: SizeLimits -> Get OlsonData
107−getOlson limits = do
108− (version, part1) <- getOlsonPart True limits get32bitInteger
109− -- There is one part for Version 1 format data, and two parts and a POSIX
110− -- TZ string for Version 2 or 3 format data
111− case () of
112− _ | version == 0 -> return part1
113− | version == 50 || version == 51 -> do
114− (_, part2) <- getOlsonPart False limits get64bitInteger
115− posixTZ <- getPosixTZ
116− return $ part1 `mappend` part2 `mappend` posixTZ
117− | otherwise -> do
118− let msg = "getOlson: invalid tzfile version " ++ toASCII [version]
119− verify (const False) msg undefined
120−
121−-- Parse the part of an Olson file that contains timezone data
122−getOlsonPart :: Integral a => Bool -> SizeLimits -> Get a ->
123− Get (Word8, OlsonData)
124−getOlsonPart verifyMagic limits getTime = do
125− magic <- fmap (toASCII . B.unpack) $ getByteString 4
126− when verifyMagic $ verify_ (== "TZif") "missing magic number" magic
92+getOlson :: Get OlsonData
93+getOlson = do
94+ getByteString 4
95+ >>= verify ((== "TZif") . toASCII . B.unpack) "missing magic number"
12796 version <- getWord8
97+ >>= verify (`elem` [0, 50]) "invalid version number"
12898 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
110+
111+-- Parse the part of an Olson file that contains timezone data
112+getOlsonPart :: Integral a => Get a -> Get OlsonData
113+getOlsonPart getTime = do
129114 tzh_ttisgmtcnt <- get32bitInt
130115 tzh_ttisstdcnt <- get32bitInt
131116 tzh_leapcnt <- get32bitInt
132− >>= verify (withinLimit maxLeaps) "too many leap second specifications"
133117 tzh_timecnt <- get32bitInt
134− >>= verify (withinLimit maxTimes) "too many timezone transitions"
135118 tzh_typecnt <- get32bitInt
136− >>= verify (withinLimit maxTypes) "too many timezone type specifications"
137− verify (withinLimit maxTypes) "too many isgmt specifiers" tzh_ttisgmtcnt
138− verify (withinLimit maxTypes) "too many isstd specifiers" tzh_ttisstdcnt
139119 tzh_charcnt <- get32bitInt
140− >>= verify (withinLimit maxAbbrChars) "too many tilezone specifiers"
141120 times <- fmap (map toInteger) $ replicateM tzh_timecnt getTime
142121 indexes <- replicateM tzh_timecnt get8bitInt
143122 ttinfos <- replicateM tzh_typecnt getTtInfo
@@ -145,32 +124,22 @@ getOlsonPart verifyMagic limits getTime = do
145124 leaps <- replicateM tzh_leapcnt $ getLeapInfo getTime
146125 isstds <- replicateM tzh_ttisstdcnt getBool
147126 isgmts <- replicateM tzh_ttisgmtcnt getBool
148− return
149− (version,
150− OlsonData
151− (zipWith Transition times indexes)
152− (map (flip lookupAbbr abbr_chars) . zipWith setTtype ttinfos $
153− zipWithExtend boolsToTType False False isstds isgmts)
154− leaps
155− Nothing
156− )
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
157134 where
158− withinLimit limit value = maybe True (value <=) $ limit limits
159135 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
160136 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
161137 setTtype ttinfo ttype = ttinfo {tt_ttype = ttype}
162− boolsToTType _ isgmt | isgmt = UTC
163− boolsToTType isstd _
164− | isstd = Std
165− | otherwise = Wall
166−
167−-- A variant of zipWith whose result is the length of the longer
168−-- rather than the shorter list, by extending the shorter list with
169−-- a default value
170−zipWithExtend :: (a -> b -> c) -> a -> b -> [a] -> [b] -> [c]
171−zipWithExtend f x0 y0 (x:xs) (y:ys) = f x y : zipWithExtend f x0 y0 xs ys
172−zipWithExtend f x0 _ [] ys = map (f x0) ys
173−zipWithExtend f _ y0 xs _ = map (flip f y0) xs
138+ boolsToTType _ isgmt | isgmt = UTC
139+ boolsToTType isstd _ = maybe UnknownType stdOrWall isstd
140+ stdOrWall isstd
141+ | isstd = Std
142+ | otherwise = Wall
174143
175144-- Parse a POSIX-style TZ string.
176145-- We don't try to understand the TZ string, we just pass it along whole.
@@ -191,7 +160,7 @@ getTtInfo = do
191160 gmtoff <- get32bitInt
192161 isdst <- getBool
193162 abbrind <- get8bitInt
194− return $ TtInfo gmtoff isdst Wall abbrind
163+ return $ TtInfo gmtoff isdst UnknownType abbrind
195164
196165-- Parse leap second info. (usually not used)
197166getLeapInfo :: Integral a => Get a -> Get LeapInfo
@@ -234,11 +203,6 @@ verify pred msg val
234203 | pred val = return val
235204 | otherwise = error msg
236205
237−verify_ :: Monad m => (a -> Bool) -> String -> a -> m ()
238−verify_ pred msg val
239− | pred val = return ()
240− | otherwise = error msg
241−
242206throwOlson :: FilePath -> String -> IO a
243207throwOlson fp msg = throw . OlsonError $
244208 fp ++ ": invalid timezone file: " ++ msg
Data/Time/LocalTime/TimeZone/Olson/Render.hs+72−95
@@ -2,17 +2,17 @@
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
99--
10−-- Render Olson timezone data in the standard binary format as used in
11−-- Olson timezone files, and as specified by the tzfile(5) man page on
12−-- Unix-like systems. For more information about this format, see
13−-- <http://www.twinsun.com/tz/tz-link.htm>.
10+-- Render Olson timezone data in the standard binary format as
11+-- used in Olson timezone files, and as specified by the
12+-- tzfile(5) man page on Unix-like systems. For more information
13+-- about this format, see 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. -}
@@ -25,12 +25,9 @@ module Data.Time.LocalTime.TimeZone.Olson.Render
2525 -- timestamp, or if a POSIX-style TZ string is specified, timezone
2626 -- data is rendered using Version 2 format. Otherwise, the timezone data
2727 -- is rendered using Version 1 format.
28− renderTimeZoneSeriesToOlsonFile,
29− timeZoneSeriesToOlson,
3028 renderOlsonToFile,
31− verifyOlsonLimits,
3229 putOlson,
33− extractOlsonV1
30+ splitOlson
3431)
3532where
3633
@@ -47,15 +44,9 @@ import Data.Ord (comparing)
4744import Data.Word (Word8)
4845import Data.Maybe (listToMaybe, maybeToList, isNothing, fromMaybe, catMaybes)
4946import Data.Monoid (mempty)
50−import Control.Monad (guard, replicateM_, unless)
47+import Control.Monad (guard, replicateM_)
5148
5249-- | Render a @TimeZoneSeries@ as a binary Olson timezone file.
53−--
54−-- If the values in the Olson timezone data exceed the standard size
55−-- limits (see 'defaultLimits'), this function throws an
56−-- exception. For other behavior, use 'timeZoneSeriesToOlson',
57−-- 'verifyOlsonLimits', 'putOlson' and 'Data.Binary.Put.runPut'
58−-- directly.
5950renderTimeZoneSeriesToOlsonFile :: FilePath -> TimeZoneSeries -> IO ()
6051renderTimeZoneSeriesToOlsonFile fp = renderOlsonToFile fp .
6152 fromMaybe (error "Cannot render TimeZoneSeries: default is summer time") .
@@ -69,93 +60,87 @@ timeZoneSeriesToOlson (TimeZoneSeries dflt pairs)
6960 | otherwise = Just $
7061 OlsonData
7162 [Transition secs ttinfo |
72− (t, tz) <- reverse pairs,
63+ (t, tzs) <- pairs,
7364 let secs = round $ utcTimeToPOSIXSeconds t,
74− ttinfo <- maybeToList $ lookup (mkTT tz) ttAssocs]
65+ ttinfo <- maybeToList $ lookup (mkTT tzs) ttAssocs]
7566 ttinfos
7667 []
7768 Nothing
7869 where
79− mkTT (TimeZone offset isdst abbr) =
80− TtInfo (offset*60) isdst Wall abbr
70+ mkTT (TimeZone offset issdst abbr) =
71+ TtInfo (offset*60) issdst Wall abbr
8172 dfltTT = mkTT dflt
8273 ttAssocs = (dfltTT, 0) :
83− zip (uniq . sort . filter (/= dfltTT) $ map (mkTT . snd) pairs) [1..]
74+ zip (filter (/= dfltTT) $ map (mkTT . snd) pairs) [1..]
8475 ttinfos = map fst ttAssocs
8576
86−-- | Check whether @OlsonData@ is within size limits.
87−verifyOlsonLimits :: SizeLimits -> OlsonData -> Bool
88−verifyOlsonLimits limits (OlsonData transs ttinfos leaps _) =
89− withinLimit maxTimes transs &&
90− withinLimit maxTypes ttinfos &&
91− withinLimit maxLeaps leaps &&
92− withinLimit maxAbbrChars abbrChars
93− where
94− withinLimit limit items = maybe True (null . flip drop items) $
95− limit limits
96− abbrChars = concat abbrStrs ++ map (const '\NUL') abbrStrs
97− abbrStrs = map tt_abbr ttinfos
98−
9977-- | Render Olson timezone data as a binary Olson timezone file
100−--
101−-- If the values in the Olson timezone data exceed the standard size
102−-- limits (see 'defaultLimits'), this function throws an
103−-- exception. For other behavior, use 'verifyOlsonLimits', 'putOlson'
104−-- and 'Data.Binary.Put.runPut' directly.
10578renderOlsonToFile :: FilePath -> OlsonData -> IO ()
106−renderOlsonToFile fp olson = do
107− unless (verifyOlsonLimits defaultLimits olson) $
108− error "Olson timezone data exceeds size limits"
109− L.writeFile fp . runPut . putOlson $ olson
79+renderOlsonToFile fp = L.writeFile fp . runPut . putOlson
11080
11181-- | Render Olson timezone data in binary Olson timezone file format
11282-- 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
83+putOlson :: 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
11890 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
91+ (olson1, olson2, posix) = splitOlson olson
92+ version
93+ | olson2 /= mempty = 50
94+ | otherwise = maybe 0 (const 50) $ posix >>= guard . not . null
95+
96+-- | Split Olson timezone data into three parts: timezone data that can
97+-- be rendered using Version 1 format, timezone data that can only be
98+-- rendered using Version 2 format, and the POSIX-style TZ string
99+-- if specified
100+splitOlson :: OlsonData -> (OlsonData, OlsonData, Maybe String)
101+splitOlson (OlsonData transs ttinfos leaps posix) =
102+ (OlsonData transs1 ttinfos1 leaps1 Nothing,
103+ OlsonData transs2 ttinfos2 leaps2 Nothing,
104+ posix)
128105 where
129106 cutoff = 0x80000000 -- 2^31
130− 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
107+ ( leaps1 , leaps2 ) = partition ((>= cutoff) . leapTime) leaps
108+ (transs1', transs2') = partition ((>= cutoff) . transTime) transs
109+ assoc1 = mkAssoc [0] transs1'
110+ assoc2 = mkAssoc [] transs2'
111+ transs1 = mkTranss transs1' assoc1
112+ transs2 = mkTranss transs2' assoc2
113+ ttinfos1 = mkTtinfos assoc1
114+ ttinfos2 = mkTtinfos assoc2
115+ mkAssoc prepend transs' = zip
135116 (sortBy (comparing $ fmap tt_ttype . listToMaybe . flip drop ttinfos) .
136− uniq . sort . (0 :) $ map transIndex transs1')
117+ uniq . sort . (prepend ++) $ map transIndex transs')
137118 [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
142−
143−putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
144−putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
145− putASCII "magic number" "TZif"
146− putWord8 version
147− putByteString . B.pack $ replicate 15 0 -- padding nulls
119+ mkTranss transs' assoc = [t {transIndex = i} |
120+ t <- transs', i <- maybeToList $ lookup (transIndex t) assoc]
121+ mkTtinfos assoc = map snd . dropWhile (isNothing . fst) .
122+ sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
123+
124+putOlsonParts :: 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
129+ putPosixTZ posix
130+
131+putOlsonPart :: (Integer -> Put) -> OlsonData -> Put
132+putOlsonPart putTime (OlsonData transs ttinfos leaps _) = do
148133 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
134+ -- tzh_ttisgmtcnt
135+ -- tzh_ttisstdcnt
136+ putCount leaps -- tzh_leapcnt
137+ putCount transs -- tzh_timecnt
138+ putCount ttinfos -- tzh_typecnt
139+ putCount abbrChars -- tzh_charcnt
155140 mapM_ (putTime . transTime ) transs
156141 mapM_ (put8bitIntegral . transIndex) transs
157142 mapM_ putTtInfo ttinfosIndexed
158− mapM_ putAbbr abbrStrings
143+ putASCII abbrChars
159144 mapM_ (putLeapInfo putTime) leaps
160145 mapM_ (putBool . (== Std) . tt_ttype) ttinfosWithTtype -- isstd
161146 mapM_ (putBool . (== UTC) . tt_ttype) ttinfosWithTtype -- isgmt
@@ -163,18 +148,16 @@ putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
163148 putCount = put32bitIntegral . length
164149 ttinfosWithTtype = takeWhile ((<= UTC) . tt_ttype) ttinfosIndexed
165150 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
168− abbrAssocs = zip abbrStrings . scanl (+) 0 $
169− map ((+ 1) . length) abbrStrings
151+ abbrChars = concatMap (++ "\NUL") abbrStrings
170152 ttinfosIndexed = [TtInfo gmtoff isdst ttype i |
171153 TtInfo gmtoff isdst ttype abbr <- ttinfos,
172− i <- maybeToList $ lookup abbr abbrAssocs]
154+ i <- maybeToList . lookup abbr . zip abbrStrings .
155+ scanl (+) 0 $ map ((+ 1) . length) abbrStrings]
173156
174157putPosixTZ :: Maybe String -> Put
175158putPosixTZ posix = do
176159 putWord8 10
177− putASCII "POSIX TZ string"$ fromMaybe "" posix
160+ putASCII $ fromMaybe "" posix
178161 putWord8 10
179162
180163putTtInfo :: TtInfo Int -> Put
@@ -207,11 +190,5 @@ putBool True = putWord8 1
207190uniq :: Eq a => [a] -> [a]
208191uniq = map head . group
209192
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"
193+putASCII :: String -> Put
194+putASCII = putByteString . B.pack . map (fromIntegral . fromEnum)
Data/Time/LocalTime/TimeZone/Olson/Types.hs+24−91
@@ -1,9 +1,8 @@
11{-# OPTIONS_HADDOCK hide #-}
2−{-# LANGUAGE CPP #-}
32-----------------------------------------------------------------------------
43-- |
54-- Module : Data.Time.LocalTime.TimeZone.Olson.Types
6−-- Copyright : Yitzchak Gale 2018
5+-- Copyright : Yitzchak Gale 2010
76--
87-- Maintainer : Yitzchak Gale <gale@sefer.org>
98-- Portability : portable
@@ -11,11 +10,11 @@
1110-- Data types to represent timezone data as used in Olson timezone
1211-- files, and as specified by the tzfile(5) man page on Unix-like
1312-- systems. For more information about this format, see
14−-- <http://www.twinsun.com/tz/tz-link.htm>.
13+-- http://www.twinsun.com/tz/tz-link.htm.
1514--
1615-- Both Version 1 and Version 2 timezone data can be represented.
1716
18−{- Copyright (c) 2018 Yitzchak Gale. All rights reserved.
17+{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.
1918For licensing information, see the BSD3-style license in the file
2019LICENSE that was originally distributed by the author together with
2120this file. -}
@@ -27,73 +26,38 @@ module Data.Time.LocalTime.TimeZone.Olson.Types
2726 Transition(..),
2827 TransitionType(..),
2928 TtInfo(..),
30− LeapInfo(..),
31−
32− -- ** Size limits for Olson timezone data
33− SizeLimits(..),
34− defaultLimits,
35− limitsNoSolar,
36− noLimits
29+ LeapInfo(..)
3730)
3831where
3932
40−#if MIN_VERSION_base(4,11,0)
41−import Data.Semigroup (Semigroup(..))
42−#endif
4333import Data.Monoid (Monoid(..))
4434import Control.Monad (mplus)
4535
4636-- | @OlsonData@ represents a full set of timezone data for a location.
4737--
48−-- @OlsonData@ can represent timezone data from files in Version 1, 2,
49−-- or 3 format.
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−--
55−-- In a Version 2 format file, the timezone data is split into two
56−-- parts. The first part contains timezone data for which all
57−-- timestamp values can be represented in less than 32 bits, and the
58−-- second part contains timezone data for which 32 bits or more are
59−-- required to represent timestamp values. The POSIX TZ string, if
60−-- present, can only be rendered in a Version 2 file, and appears
61−-- after both parts of the timezone data.
38+-- @OlsonData@ can represent timezone data from files in Version 1 format
39+-- or Version 2 format. Version 1 format files can only contain timestamp
40+-- values that can be represented in less than 32 bits, and cannot contain
41+-- a POSIX TZ string.
6242--
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.
43+-- In a Version 2 format file, the timezone data is split into two parts.
44+-- The first part contains timezone data for which all timestamp values
45+-- can be represented in less than 32 bits, and the second part contains
46+-- timezone data for which 32 bits or more are required to represent
47+-- timestamp values. The POSIX TZ string, if present, can only be rendered
48+-- in a Version 2 file, and appears after both parts of the timezone data.
6749data OlsonData =
68− OlsonData {
69− olsonTransitions :: [Transition],
70− olsonTypes :: [TtInfo String],
71− olsonLeaps :: [LeapInfo],
72− olsonPosixTZ :: (Maybe String)
73− -- ^ Optional POSIX TZ string for
74− -- times after the last @Transition@
75− }
50+ OlsonData
51+ [Transition]
52+ [TtInfo String]
53+ [LeapInfo]
54+ (Maybe String) -- ^ Optional POSIX TZ string for the last @Transition@
7655 deriving (Eq, Show)
7756
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
8957instance Monoid OlsonData where
9058 mempty = OlsonData [] [] [] Nothing
91− mappend (OlsonData a b c d ) (OlsonData a' b' c' 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
59+ mappend (OlsonData a b c d ) (OlsonData a' b' c' d')
60+ = OlsonData (a++a') (b++b') (c++c') (d `mplus` d')
9761
9862-- | A @Transition@ represents a moment when the clocks change in a
9963-- timezone. It consists of a Unix timestamp value that indicates the
@@ -116,7 +80,7 @@ data Transition =
11680-- zone. This historical trivia may seem rather boring, but
11781-- unfortunately it is needed to interpret a POSIX-style TZ string
11882-- timezone specification correctly.
119−data TransitionType = Std | Wall | UTC
83+data TransitionType = Std | Wall | UTC | UnknownType
12084 deriving (Eq, Ord, Show)
12185
12286-- | A @TtInfo@ is a specification of local time in a timezone for
@@ -133,10 +97,10 @@ data TtInfo abbr =
13397 tt_ttype :: TransitionType,
13498 tt_abbr :: abbr -- ^ The timezone abbreviation string.
13599 }
136− deriving (Eq, Ord, Show)
100+ deriving (Eq, Show)
137101
138102-- | Olson timezone files can contain leap second specifications, though
139−-- most do not.
103+-- the official files have not done so since the late 1980s.
140104data LeapInfo =
141105 LeapInfo
142106 {leapTime :: Integer, -- ^ A Unix timestamp indicating the time
@@ -145,34 +109,3 @@ data LeapInfo =
145109 -- after this leap second
146110 }
147111 deriving (Eq, Show)
148−
149−-- | The reference C implentation imposes size limits on the data
150−-- structures in a timezone file.
151−data SizeLimits = SizeLimits
152− {maxTimes :: Maybe Int, -- ^ The maximum number of transition times
153− maxTypes :: Maybe Int, -- ^ The maximum number of TtInfo
154− -- clock settings
155− maxAbbrChars :: Maybe Int, -- ^ The maximum total number of bytes in
156− -- all timezone abbreviations.
157− maxLeaps :: Maybe Int -- ^ The maximum number of leap second
158− -- specifications.
159− }
160−
161−-- | The size limits in @defaultLimits@ are taken from the file
162−-- tzfile.h from tzcode version 2010f. These are the limits for the C
163−-- implementation on many platforms.
164−defaultLimits :: SizeLimits
165−defaultLimits = SizeLimits { maxTimes = Just 1200, maxTypes = Just 256,
166− maxAbbrChars = Just 50, maxLeaps = Just 50 }
167−
168−-- | @limitsNoSolar@ contains the tighter size limits imposed on some
169−-- platforms that do not allow timezones that are based on solar time.
170−limitsNoSolar :: SizeLimits
171−limitsNoSolar = defaultLimits { maxTypes = Just 20 }
172−
173−-- | @noLimits@ imposes no size limits. If you use @noLimits@ when
174−-- parsing, you may exhaust all available memory when reading a faulty
175−-- or malicious timezone file. If you use @noLimits@ when rendering,
176−-- the rendered timezone file might not be readable on some systems.
177−noLimits :: SizeLimits
178−noLimits = SizeLimits Nothing Nothing Nothing Nothing
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2010-2018 Yitzchak Gale. All rights reserved.
1+Copyright (c) 2010 Yitzchak Gale. All rights reserved.
22
33Redistribution and use in source and binary forms, with or without
44modification, are permitted provided that the following conditions are
README.mddeleted−43
@@ -1,43 +0,0 @@
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.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
catTZ.hsdeleted−14
@@ -1,14 +0,0 @@
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)
hzdump.hs+12−28
@@ -1,6 +1,5 @@
11-- | A clone in Haskell of the zdump(8) command, including most of
2−-- the bugs, that takes paths to Olson files instead of timezone
3−-- names. This is useful for testing the Haskell implementation
2+-- the bugs. This is useful for testing the Haskell implementation
43-- of the Olson timezone parser and renderer against the reference
54-- implementation in C. But less useful than it might seem at first,
65-- because Haskell rounds historical solar mean timezones to the
@@ -15,18 +14,15 @@ import Data.Maybe (listToMaybe)
1514import System.Environment (getArgs)
1615import System.Exit (exitWith, exitSuccess, ExitCode(ExitFailure))
1716import System.Locale (defaultTimeLocale)
18−import Control.Monad (guard)
1917
2018version :: IO a
2119version = do
22− putStrLn "hzdump version 0.1, clone of zdump 1.7 -c option fixed,"
23− putStrLn "takes zone file paths instead of zone specifications"
20+ putStrLn "hzdump version 0.1, clone of zdump 1.7 with -c option fixed"
2421 exitSuccess
2522
2623usage :: IO a
2724usage = do
28− putStrLn "usage: hzdump [--version] [-v] [-c cutoff] zone-file-path ..."
29− putStrLn " where cutoff is lo-year,hi-year or hi-year"
25+ putStrLn "usage: hzdump [--version] [-v] [-c cutoff] zonename ..."
3026 exitWith $ ExitFailure 1
3127
3228illegalOpt :: Char -> IO a -- sic
@@ -34,8 +30,7 @@ illegalOpt opt = do
3430 putStrLn $ "hzdump: illegal option -- " ++ [opt]
3531 usage
3632
37−data Option = Version | Illegal Char |
38− Verbose (Maybe Integer, Maybe Integer) | Now
33+data Option = Version | Illegal Char | Verbose (Maybe Integer) | Now
3934 deriving (Eq, Ord, Show)
4035
4136main = do
@@ -43,7 +38,7 @@ main = do
4338 (getTimes, displayTime) <- case opts of
4439 Version -> version
4540 Illegal opt -> illegalOpt opt
46− Verbose rng -> return (transitionTimes rng, displayVerbose)
41+ Verbose yr -> return (transitionTimesUntil yr, displayVerbose)
4742 _ -> do now <- getCurrentTime
4843 return (const [now], displayConcise)
4944 tzss <- mapM getZone zones
@@ -58,26 +53,17 @@ main = do
5853
5954parseArgs :: [String] -> (Option, [FilePath])
6055parseArgs args | "--version" `elem` args = (Version, [])
61−parseArgs args = getOpts False (Nothing, Nothing) args
56+parseArgs args = getOpts False Nothing args
6257 where
6358 getOpts _ cutoff ("-v":args) = getOpts True cutoff args
64− getOpts v _ ("-c":c:args) = maybe (Illegal 'c', [])
65− (\y -> getOpts v y args) $
66− parseCutoff c
59+ getOpts v Nothing ("-c":c:args) = maybe (Illegal 'c', [])
60+ (\y -> getOpts v (Just y) args) $
61+ maybeRead c
6762 getOpts v cutoff ("--":zones ) = (opts v cutoff, zones)
6863 getOpts _ _ (('-':x:_):_) = (Illegal x, [])
6964 getOpts v cutoff zones = (opts v cutoff, zones)
70−
7165 opts v cutoff = if v then Verbose cutoff else Now
7266
73− parseCutoff c = let p0 = listToMaybe $ reads c
74− los = fmap fst p0
75− his = do str <- fmap snd p0
76− let (comma, str1) = splitAt 1 str
77− guard $ comma == ","
78− maybeRead str1
79− in los >> Just (maybe (Nothing, los) ((,) los . Just) his)
80−
8167displayConcise :: TimeZoneSeries -> UTCTime -> String
8268displayConcise tzs t = formatTime defaultTimeLocale format $
8369 ZoneSeriesTime t tzs
@@ -90,11 +76,9 @@ displayVerbose tzs t = concat [displayConcise utcTZ t, " = ",
9076 where
9177 isdst = timeZoneSummerOnly $ timeZoneFromSeries tzs t
9278
93−transitionTimes :: (Maybe Integer, Maybe Integer) -> TimeZoneSeries ->
94− [UTCTime]
95−transitionTimes (lo, hi) =
96− maybe id (takeWhile . (>)) (fmap happyNewYear hi) .
97− maybe id (dropWhile . (>)) (fmap happyNewYear lo) .
79+transitionTimesUntil :: Maybe Integer -> TimeZoneSeries -> [UTCTime]
80+transitionTimesUntil yr =
81+ maybe id (dropWhile . (>=)) (fmap happyNewYear yr) .
9882 addBugs . addPrevSecond . actualTransitions
9983 where
10084 happyNewYear y = UTCTime (fromGregorian y 1 1) 0
timezone-olson.cabaldeleted−45
@@ -1,45 +0,0 @@
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