tz 0.1.1.1 → 0.1.2.0
raw patch · 9 files changed
+159/−249 lines, 9 filesdep −bindings-posixdep −unixdep ~data-default
Dependencies removed: bindings-posix, unix
Dependency ranges changed: data-default
Files
- Data/Time/Zones/Read.hs +3/−0
- Data/Time/Zones/TH.hs +3/−0
- benchmarks/benchTZ.hs +5/−4
- benchmarks/bench_c_localtime.hs +0/−99
- stack.yaml +12/−3
- tests/testTH.hs +2/−2
- tests/testTZ.hs +1/−118
- tests/testTZSys.hs +112/−0
- tz.cabal +21/−23
Data/Time/Zones/Read.hs view
@@ -48,6 +48,9 @@ -- | Looks for the time zone file in the system timezone directory, which is -- @\/usr\/share\/zoneinfo@, or if the @TZDIR@ environment variable is -- set, then there.+--+-- Note, this is unlikely to work on non-posix systems (e.g.,+-- Windows), use `loadTZFromDB` or `loadTZFromFile` instead. loadSystemTZ :: String -> IO TZ loadSystemTZ tzName = pathForSystemTZ tzName >>= loadTZFromFile
Data/Time/Zones/TH.hs view
@@ -52,6 +52,9 @@ -- -- See also: `loadSystemTZ` for details on how system time zone files -- are located.+--+-- Note, this is unlikely to work on non-posix systems (e.g.,+-- Windows), use `includeTZFromDB` or `includeTZFromFile` instead. includeSystemTZ :: String -> Q Exp includeSystemTZ tzName = do desc <- runIO $ pathForSystemTZ tzName >>= BL.readFile
benchmarks/benchTZ.hs view
@@ -1,6 +1,5 @@ module Main (main) where -import Bindings.Posix.Time import Criterion.Main import Data.Fixed import Data.Int@@ -8,12 +7,14 @@ import Data.Time.LocalTime.TimeZone.Olson import Data.Time.LocalTime.TimeZone.Series import Data.Time.Zones-import System.Posix.Env+import System.Environment +foreign import ccall safe "time.h tzset" c_tzset :: IO ()+ setupTZ :: String -> IO TZ setupTZ zoneName = do- setEnv "TZ" zoneName True- c'tzset+ setEnv "TZ" zoneName+ c_tzset loadSystemTZ zoneName mkLocal :: Integer -> Int -> Int -> Int -> Int -> Pico -> LocalTime
− benchmarks/bench_c_localtime.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE ViewPatterns #-}--module Main (main) where--import Bindings.Posix.Time-import Bindings.Posix.Sys.Types-import Criterion.Main-import Foreign.Safe-import Foreign.C-import System.Posix.Env--setupTZ :: String -> IO ()-setupTZ zoneName = do- setEnv "TZ" zoneName True- c'tzset--data C_tm = C_tm {- _tm_sec :: {-# UNPACK #-} !CInt,- _tm_min :: {-# UNPACK #-} !CInt,- _tm_hour :: {-# UNPACK #-} !CInt,- _tm_mday :: {-# UNPACK #-} !CInt,- _tm_mon :: {-# UNPACK #-} !CInt,- _tm_year :: {-# UNPACK #-} !CInt,- _tm_wday :: {-# UNPACK #-} !CInt,- _tm_yday :: {-# UNPACK #-} !CInt,- _tm_isdst :: {-# UNPACK #-} !CInt- -- _tm_gmtoff :: {-# UNPACK #-} !CInt- -- _tm_zone :: {-# UNPACK #-} !Ptr CString (??)- } deriving (Show)--instance Storable C_tm where- -- Overestimate; it's 10 or 11 based on architecture.- sizeOf _ = 16 * sizeOf (undefined :: CInt)- alignment _ = alignment (undefined :: CLong)- peek (castPtr -> p) = do- s <- peekElemOff p 0- mi <- peekElemOff p 1- h <- peekElemOff p 2- d <- peekElemOff p 3- m <- peekElemOff p 4- y <- peekElemOff p 5- wd <- peekElemOff p 6- yd <- peekElemOff p 7- idst <- peekElemOff p 8- return $ C_tm s mi h d m y wd yd idst- poke (castPtr -> p) (C_tm s mi h d m y wd yd idst) = do- pokeElemOff p 0 s- pokeElemOff p 1 mi- pokeElemOff p 2 h- pokeElemOff p 3 d- pokeElemOff p 4 m- pokeElemOff p 5 y- pokeElemOff p 6 wd- pokeElemOff p 7 yd- pokeElemOff p 8 idst--foreign import ccall unsafe "time.h localtime_r" c_localtime_r- :: Ptr C'time_t -> Ptr C_tm -> IO (Ptr C_tm)--localtime :: Int -> IO C_tm-localtime t = with (fromIntegral t) $ \ptime ->- alloca $ \ptm -> do- res <- c_localtime_r ptime ptm- if res /= nullPtr- then peek res- else fail "c_localtime_r failed"---- This just wraps the binding from Bindings.Posix.Time.------ Because it's foreign import ccall _safe_, it's more than two times--- slower!-localtime' :: Int -> IO C'tm-localtime' t = with (fromIntegral t) $ \ptime ->- alloca $ \ptm -> do- res <- c'localtime_r ptime ptm- if res /= nullPtr- then peek res- else fail "c'localtime_r failed"--benchmarks :: [Benchmark]-benchmarks = [- bgroup "c'localtime_r" [- bench "past" $ whnfIO $ localtime' (-2100000000),- bench "epoch" $ whnfIO $ localtime' 0,- bench "now" $ whnfIO $ localtime' 1395572400,- bench "future" $ whnfIO $ localtime' 2100000000- ],- bgroup "our_localtime_r" [- bench "past" $ whnfIO $ localtime (-2100000000),- bench "epoch" $ whnfIO $ localtime 0,- bench "now" $ whnfIO $ localtime 1395572400,- bench "future" $ whnfIO $ localtime 2100000000- ]- ]--main :: IO ()-main = do- setupTZ "Europe/Budapest"- defaultMain benchmarks
stack.yaml view
@@ -1,15 +1,24 @@ # For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)-resolver: nightly-2016-05-29+resolver: nightly-2016-08-06 # Local packages, usually specified by relative directory name packages: - '.'+# timezone-series with time-1.6 support+- location:+ git: https://github.com/ryantrinkle/timezone-series.git+ commit: f8dece8c016db6476e2bb0d4f972769a76f6ff40+ extra-dep: true+# timezone-olson with time-1.6 support+- location:+ git: https://github.com/klao/timezone-olson.git+ commit: 04085c5adfa3a87155d560f7cbbf189a28e513e3+ extra-dep: true # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)-extra-deps:-- bindings-posix-1.2.6+extra-deps: [] # Override default flag values for local packages and extra-deps flags: {}
tests/testTH.hs view
@@ -7,11 +7,11 @@ import Test.HUnit hiding (Test, assert) tzBudapest :: TZ-tzBudapest = $(includeSystemTZ "Europe/Budapest")+tzBudapest = $(includeTZFromDB "Europe/Budapest") case_Budapest_is_Budapest :: IO () case_Budapest_is_Budapest = do- readBp <- loadSystemTZ "Europe/Budapest"+ readBp <- loadTZFromDB "Europe/Budapest" tzBudapest @?= readBp main :: IO ()
tests/testTZ.hs view
@@ -3,128 +3,16 @@ module Main (main) where -import Bindings.Posix.Time-import Data.Bits-import Data.Int-import Data.IORef import Data.Time-import Data.Time.Clock.POSIX import Data.Time.Zones-import Data.Time.Zones.Types-import qualified Data.Vector.Generic as V import Test.Framework.Providers.HUnit-import Test.Framework.Providers.QuickCheck2 import Test.Framework.TH import Test.HUnit hiding (Test, assert)-import Test.QuickCheck-import Test.QuickCheck.Monadic-import System.Posix.Env-import System.IO.Unsafe -setupTZ :: String -> IO TZ-setupTZ zoneName = do- setEnv "TZ" zoneName True- c'tzset- loadSystemTZ zoneName--onceIO :: IO a -> IO a-{-# NOINLINE onceIO #-}-onceIO op = opWrap- where- {-# NOINLINE var #-}- var = unsafePerformIO $ newIORef Nothing- opWrap = do- v <- readIORef var- case v of- Just x -> return x- Nothing -> do- x <- op- atomicWriteIORef var $ Just x- return x---- On the Int32 range of POSIX times we should replicate the behavior--- perfectly.------ * After year 2038 we normally run into a range where the--- envvar-like "rule" part of the TZif should be interpreted, which we--- don't do yet.------ * And below around -2^55 the localtime_r C function starts failing--- with "value too large".-checkTimeZone :: String -> Int32 -> Property-checkTimeZone zoneName = prop- where- setup = onceIO $ setupTZ zoneName- prop ut = monadicIO $ do- tz <- run $ setup- run $ print ut- timeZone <- run $ getTimeZone $ posixSecondsToUTCTime $ fromIntegral ut- run $ timeZoneForPOSIX tz (fromIntegral ut) @?= timeZone---- See comment for the checkTimeZone.-checkTimeZone64 :: String -> Property-checkTimeZone64 zoneName = prop- where- setup = onceIO $ setupTZ zoneName- two31 = 2147483647- prop = monadicIO $ do- tz <- run $ setup- ut <- pick $ oneof [arbitrary, choose (-two31, two31)]- pre $ ut < two31 && ut > -(1 `shiftL` 55)- -- This is important. On 32 bit machines we want to limit- -- testing to the Int range.- pre $ ut > fromIntegral (minBound :: Int)- timeZone <- run $ getTimeZone $ posixSecondsToUTCTime $ fromIntegral ut- run $ timeZoneForPOSIX tz ut @?= timeZone---- On the Int32 range of POSIX times we should mostly replicate the--- behavior.------ * After year 2038 we normally run into a range where the--- envvar-like "rule" part of the TZif should be interpreted, which we--- don't do yet.------ * And the very first time diff in most of the TZif files is usually--- the "Local Mean Time", which is generally a fractional number of--- minutes, so we would get difference with getTimeZone too. Most of--- the locations switch to some more standard time zone before or--- around 1900, which happens to be less than -2^31 POSIX time. But--- in some locations this transition falls within the Int32 range--- (eg. China), so we can supply another lower bound.-checkLocalTime :: String -> Maybe Int32 -> Int32 -> Property-checkLocalTime zoneName mLower = prop- where- setup = onceIO $ setupTZ zoneName- prop ut = monadicIO $ do- case mLower of- Nothing -> return ()- Just lower -> pre $ ut > lower- tz <- run $ setup- let utcTime = posixSecondsToUTCTime $ fromIntegral ut- timeZone <- run $ getTimeZone utcTime- run $ utcToLocalTimeTZ tz utcTime @?= utcToLocalTime timeZone utcTime-- case_utcTZ_is_utc = timeZoneForPOSIX utcTZ 0 @?= utc case_utcTZ_zero_diff = diffForPOSIX utcTZ 0 @?= 0 -prop_Budapest_correct_TimeZone = checkTimeZone64 "Europe/Budapest"-prop_New_York_correct_TimeZone = checkTimeZone64 "America/New_York"-prop_Los_Angeles_correct_TimeZone = checkTimeZone64 "America/Los_Angeles"-prop_Shanghai_correct_TimeZone = checkTimeZone64 "Asia/Shanghai"-prop_Jerusalem_correct_TimeZone = checkTimeZone64 "Asia/Jerusalem"-prop_Antarctica_Palmer_correct_TimeZone = checkTimeZone64 "Antarctica/Palmer"-prop_Melbourne_correct_TimeZone = checkTimeZone64 "Australia/Melbourne"--prop_Budapest_correct_LocalTime = checkLocalTime "Europe/Budapest" Nothing-prop_New_York_correct_LocalTime = checkLocalTime "America/New_York" Nothing-prop_Los_Angeles_correct_LocalTime = checkLocalTime "America/Los_Angeles" Nothing-prop_Shanghai_correct_LocalTime = checkLocalTime "Asia/Shanghai" $ Just (-1325491558)-prop_Jerusalem_correct_LocalTime = checkLocalTime "Asia/Jerusalem" $ Just (-1641003641)-prop_Antarctica_Palmer_correct_LocalTime = checkLocalTime "Antarctica/Palmer" Nothing-prop_Melbourne_correct_LocalTime = checkLocalTime "Australia/Melbourne" Nothing- case_DB_utc_is_utc = do tz <- loadTZFromDB "UTC" tz @?= utcTZ@@ -183,9 +71,4 @@ diffForAbbr tz "XYZ" @?= Nothing main :: IO ()-main = do- -- When we are running 'cabal test' the package is not yet- -- installed, so we want to use the data directory from within the- -- sources.- setEnv "tz_datadir" "./tzdata" True- $defaultMainGenerator+main = $defaultMainGenerator
+ tests/testTZSys.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++module Main (main) where++import Data.Bits+import Data.IORef+import Data.Int+import Data.Time+import Data.Time.Clock.POSIX+import Data.Time.Zones+import System.Environment+import System.IO.Unsafe+import Test.Framework.Providers.QuickCheck2+import Test.Framework.TH+import Test.HUnit hiding (Test, assert)+import Test.QuickCheck+import Test.QuickCheck.Monadic++foreign import ccall safe "time.h tzset" c_tzset :: IO ()++setupTZ :: String -> IO TZ+setupTZ zoneName = do+ setEnv "TZ" zoneName+ c_tzset+ loadSystemTZ zoneName++onceIO :: IO a -> IO a+{-# NOINLINE onceIO #-}+onceIO op = opWrap+ where+ {-# NOINLINE var #-}+ var = unsafePerformIO $ newIORef $ Left op+ opWrap = do+ v <- readIORef var+ case v of+ Right x -> return x+ Left _ -> do+ x <- op+ atomicWriteIORef var $ Right x+ return x++-- On the Int32 range of POSIX times we should replicate the behavior+-- perfectly.+--+-- * After year 2038 we normally run into a range where the+-- envvar-like "rule" part of the TZif should be interpreted, which we+-- don't do yet.+--+-- * And below around -2^55 the localtime_r C function starts failing+-- with "value too large".+checkTimeZone64 :: String -> Property+checkTimeZone64 zoneName = prop+ where+ setup = onceIO $ setupTZ zoneName+ two31 = 2147483647+ prop = monadicIO $ do+ tz <- run $ setup+ ut <- pick $ oneof [arbitrary, choose (-two31, two31)]+ pre $ ut < two31 && ut > -(1 `shiftL` 55)+ -- This is important. On 32 bit machines we want to limit+ -- testing to the Int range.+ pre $ ut > fromIntegral (minBound :: Int)+ timeZone <- run $ getTimeZone $ posixSecondsToUTCTime $ fromIntegral ut+ run $ timeZoneForPOSIX tz ut @?= timeZone++-- On the Int32 range of POSIX times we should mostly replicate the+-- behavior.+--+-- * After year 2038 we normally run into a range where the+-- envvar-like "rule" part of the TZif should be interpreted, which we+-- don't do yet.+--+-- * And the very first time diff in most of the TZif files is usually+-- the "Local Mean Time", which is generally a fractional number of+-- minutes, so we would get difference with getTimeZone too. Most of+-- the locations switch to some more standard time zone before or+-- around 1900, which happens to be less than -2^31 POSIX time. But+-- in some locations this transition falls within the Int32 range+-- (eg. China), so we can supply another lower bound.+checkLocalTime :: String -> Maybe Int32 -> Int32 -> Property+checkLocalTime zoneName mLower = prop+ where+ setup = onceIO $ setupTZ zoneName+ prop ut = monadicIO $ do+ case mLower of+ Nothing -> return ()+ Just lower -> pre $ ut > lower+ tz <- run $ setup+ let utcTime = posixSecondsToUTCTime $ fromIntegral ut+ timeZone <- run $ getTimeZone utcTime+ run $ utcToLocalTimeTZ tz utcTime @?= utcToLocalTime timeZone utcTime+++prop_Budapest_correct_TimeZone = checkTimeZone64 "Europe/Budapest"+prop_New_York_correct_TimeZone = checkTimeZone64 "America/New_York"+prop_Los_Angeles_correct_TimeZone = checkTimeZone64 "America/Los_Angeles"+prop_Shanghai_correct_TimeZone = checkTimeZone64 "Asia/Shanghai"+prop_Jerusalem_correct_TimeZone = checkTimeZone64 "Asia/Jerusalem"+prop_Antarctica_Palmer_correct_TimeZone = checkTimeZone64 "Antarctica/Palmer"+prop_Melbourne_correct_TimeZone = checkTimeZone64 "Australia/Melbourne"++prop_Budapest_correct_LocalTime = checkLocalTime "Europe/Budapest" Nothing+prop_New_York_correct_LocalTime = checkLocalTime "America/New_York" Nothing+prop_Los_Angeles_correct_LocalTime = checkLocalTime "America/Los_Angeles" Nothing+prop_Shanghai_correct_LocalTime = checkLocalTime "Asia/Shanghai" $ Just (-1325491558)+prop_Jerusalem_correct_LocalTime = checkLocalTime "Asia/Jerusalem" $ Just (-1641003641)+prop_Antarctica_Palmer_correct_LocalTime = checkLocalTime "Antarctica/Palmer" Nothing+prop_Melbourne_correct_LocalTime = checkLocalTime "Australia/Melbourne" Nothing++main :: IO ()+main = $defaultMainGenerator
tz.cabal view
@@ -1,5 +1,5 @@ Name: tz-Version: 0.1.1.1+Version: 0.1.2.0 License: Apache-2.0 License-File: LICENSE Author: Mihaly Barasz, Gergely Risko@@ -53,7 +53,7 @@ binary >= 0.5 && < 0.9, bytestring >= 0.9 && < 0.11, containers >= 0.5 && < 0.6,- data-default >= 0.5 && < 0.7,+ data-default >= 0.5 && < 0.8, deepseq >= 1.1 && < 2, time >= 1.2 && < 1.7, tzdata >= 0.1 && < 0.2,@@ -76,16 +76,29 @@ Build-Depends: tz, base >= 4 && < 5,- bindings-posix >= 1.2 && < 2, HUnit >= 1.2 && < 1.4,- QuickCheck >= 2.4 && < 3, test-framework >= 0.4 && < 1, test-framework-hunit >= 0.2 && < 0.4,+ test-framework-th >= 0.2 && < 0.4,+ time >= 1.2 && < 1.7++Test-Suite testsSys+ Default-Language: Haskell2010+ Type: exitcode-stdio-1.0+ HS-Source-Dirs: tests+ Main-Is: testTZSys.hs+ GHC-Options: -Wall+ If os(mingw32) || os(mingw64)+ Buildable: False+ Build-Depends:+ tz,+ base >= 4 && < 5,+ HUnit >= 1.2 && < 1.4,+ QuickCheck >= 2.4 && < 3,+ test-framework >= 0.4 && < 1, test-framework-quickcheck2 >= 0.2 && < 0.4, test-framework-th >= 0.2 && < 0.4,- time >= 1.2 && < 1.7,- unix >= 2.6 && < 3,- vector >= 0.9 && < 0.12+ time >= 1.2 && < 1.7 Test-Suite th-test Default-Language: Haskell2010@@ -127,25 +140,10 @@ Build-Depends: tz, base >= 4 && < 5,- bindings-posix >= 1.2 && < 2, criterion >= 0.8 && < 1.2, time >= 1.2 && < 1.7, timezone-olson,- timezone-series,- unix >= 2.6 && < 3--Benchmark bench_c- Default-Language: Haskell2010- Type: exitcode-stdio-1.0- HS-Source-Dirs: benchmarks- Main-Is: bench_c_localtime.hs- GHC-Options: -Wall -O2- Build-Depends:- tz,- base >= 4 && < 5,- bindings-posix >= 1.2 && < 2,- criterion >= 0.8 && < 1.2,- unix >= 2.6 && < 3+ timezone-series Benchmark bench_greg Default-Language: Haskell2010