chronos 1.0.3 → 1.0.4
raw patch · 5 files changed
+203/−17 lines, 5 filesdep +criteriondep +deepseqdep +old-localedep ~aesondep ~attoparsecdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: criterion, deepseq, old-locale, thyme, time
Dependency ranges changed: aeson, attoparsec, base, bytestring, vector
API changes (from Hackage documentation)
- Chronos: instance Data.Semigroup.Semigroup Chronos.Timespan
+ Chronos: instance GHC.Base.Semigroup Chronos.Timespan
+ Chronos: zeptoUtf8_HMS :: Maybe Char -> Parser TimeOfDay
+ Chronos: zeptoUtf8_YmdHMS :: DatetimeFormat -> Parser Datetime
Files
- bench/Bench.hs +83/−0
- chronos.cabal +19/−1
- src/Chronos.hs +91/−13
- src/Chronos/Internal/CTimespec.hs +0/−3
- src/cbits/hs-time.c +10/−0
+ bench/Bench.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE BangPatterns #-}++module Main where++import qualified Data.Vector.Unboxed as UVector+import Criterion.Main+import Data.Int+import Data.Maybe (fromMaybe)+import Control.DeepSeq (($!!), NFData(..), deepseq)+import qualified Data.ByteString.Char8 as BS8+import Test.QuickCheck (arbitrary, generate)+import Data.Attoparsec.ByteString.Char8 (Parser, parseOnly, char)+import qualified Data.Attoparsec.Zepto as Z+import Data.Foldable (traverse_)+import Data.Word+import Control.Monad (when)+import Control.Applicative++import qualified Data.Time as Time+import qualified Data.Thyme as Thyme+import qualified System.Locale as Thyme+import qualified Chronos++main :: IO ()+main = do+ utcthyme <- generate arbitrary :: IO Thyme.UTCTime+ let+ isoFormatString = "%Y-%m-%dT%H:%M:%S"+ renderIsoTime = Thyme.formatTime Thyme.defaultTimeLocale isoFormatString+ timeParser :: String -> Time.UTCTime+ timeParser =+ fromMaybe (error "Failed to parse in timeParser")+ . Time.parseTimeM True Time.defaultTimeLocale isoFormatString+ thymeParser :: String -> Thyme.UTCTime+ thymeParser =+ fromMaybe (error "Failed to parse in thymeParser")+ . Thyme.parseTime Thyme.defaultTimeLocale isoFormatString+ thymeAttoparsec :: BS8.ByteString -> Thyme.UTCTime+ thymeAttoparsec =+ Thyme.buildTime @Thyme.UTCTime+ . either error id+ . parseOnly (Thyme.timeParser Thyme.defaultTimeLocale isoFormatString)+ chronosAttoparsec :: BS8.ByteString -> Chronos.Datetime+ chronosAttoparsec =+ either error id+ . parseOnly (Chronos.parserUtf8_YmdHMS Chronos.w3c)+ chronosZepto :: BS8.ByteString -> Chronos.Datetime+ chronosZepto =+ either error id+ . Z.parse (Chronos.zeptoUtf8_YmdHMS Chronos.w3c)+++ string <- return $!! renderIsoTime utcthyme+ bytestring <- return $!! BS8.pack (renderIsoTime utcthyme)++ defaultMain+ [ bgroup "parsing"+ [ bench "Time.parseTimeM" $ nf timeParser string+ , bench "Thyme.parseTime" $ nf thymeParser string+ , bench "Thyme.timeParser" $ nf thymeAttoparsec bytestring+ , bench "Chronos.parserUtf8_YmdHMS" $ nf chronosAttoparsec bytestring+ , bench "Chronos.zeptoUtf8_YmdHMS" $ nf chronosZepto bytestring+ ]+ ]++instance NFData Chronos.Datetime where+ rnf (Chronos.Datetime a b) = a `deepseq` b `deepseq` ()++instance NFData Chronos.Date where+ rnf (Chronos.Date y m d) = y `deepseq` m `deepseq` d `deepseq` ()++instance NFData Chronos.TimeOfDay where+ rnf (Chronos.TimeOfDay h m s) = h `deepseq` m `deepseq` s `deepseq` ()++deriving instance NFData Chronos.DayOfMonth+deriving instance NFData Chronos.Month+deriving instance NFData Chronos.Year+deriving instance NFData Chronos.Day
chronos.cabal view
@@ -1,5 +1,5 @@ name: chronos-version: 1.0.3+version: 1.0.4 synopsis: A performant time library description: Performance-oriented time library for haskell. The main differences@@ -73,6 +73,24 @@ , HUnit , test-framework-hunit ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++benchmark bench+ type: exitcode-stdio-1.0+ hs-source-dirs: bench+ main-is: Bench.hs+ build-depends:+ base+ , attoparsec+ , bytestring+ , chronos+ , criterion+ , deepseq+ , old-locale+ , QuickCheck+ , thyme+ , time+ , vector default-language: Haskell2010 source-repository head
src/Chronos.hs view
@@ -106,6 +106,7 @@ , builderUtf8_IMSp , parserUtf8_HMS , parserUtf8_HMS_opt_S+ , zeptoUtf8_HMS -- ** Datetime -- *** Text , builder_DmyHMS@@ -138,6 +139,7 @@ , decodeUtf8_YmdHMS_opt_S , parserUtf8_YmdHMS , parserUtf8_YmdHMS_opt_S+ , zeptoUtf8_YmdHMS -- ** Offset Datetime -- *** Text , encode_YmdHMSz@@ -167,8 +169,8 @@ , parserOffsetUtf8 -- ** Timespan -- *** Text- , encodeTimespan - , builderTimespan + , encodeTimespan+ , builderTimespan -- *** UTF-8 ByteString , encodeTimespanUtf8 , builderTimespanUtf8@@ -209,7 +211,7 @@ import Data.ByteString (ByteString) import Torsor (add,difference,scale,plus) import Chronos.Internal.CTimespec (getPosixNanoseconds)-import Data.Word (Word64)+import Data.Word (Word64, Word8) import Torsor import GHC.Generics (Generic) import Data.Aeson (FromJSON,ToJSON,FromJSONKey,ToJSONKey)@@ -222,6 +224,7 @@ import qualified Data.Aeson.Types as AET import qualified Data.Attoparsec.ByteString.Char8 as AB import qualified Data.Attoparsec.Text as AT+import qualified Data.Attoparsec.Zepto as Z import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as LB@@ -273,7 +276,7 @@ offsetDatetimeToTime :: OffsetDatetime -> Time offsetDatetimeToTime = fromUtc . offsetDatetimeToUtcTime --- | Convert 'Time' to 'Day'. This function is lossy; consequently, it +-- | Convert 'Time' to 'Day'. This function is lossy; consequently, it -- does not roundtrip with 'dayToTimeMidnight'. timeToDayTruncate :: Time -> Day timeToDayTruncate (Time i) = Day (fromIntegral (div i 86400000000000) + 40587)@@ -544,7 +547,7 @@ year = quadcent * 400 + cent * 100 + quad * 4 + y + 1 {- $format- + The formats provided is this module are language-agnostic. To find meridiem formats and month formats, look in a language-specific module.@@ -747,7 +750,6 @@ when (d < 1 || d > 31) (fail "day must be between 1 and 31") return (Date (Year y) (Month $ m - 1) (DayOfMonth d)) - builder_HMS :: SubsecondPrecision -> Maybe Char -> TimeOfDay -> TB.Builder builder_HMS sp msep (TimeOfDay h m ns) = indexTwoDigitTextBuilder h@@ -907,7 +909,7 @@ LT.toStrict . TB.toLazyText . builderTimespan sp builderTimespan :: SubsecondPrecision -> Timespan -> TB.Builder-builderTimespan sp (Timespan ns) = +builderTimespan sp (Timespan ns) = TB.decimal sInt64 <> prettyNanosecondsBuilder sp nsRemainder where (!sInt64,!nsRemainder) = quotRem ns 1000000000@@ -931,7 +933,7 @@ builder_DmyHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> TB.Builder-builder_DmyHMS sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) = +builder_DmyHMS sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) = case msep of Nothing -> builder_Dmy mdateSep date <> builder_HMS sp mtimeSep time@@ -953,7 +955,7 @@ encode_DmyHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Text-encode_DmyHMS sp format = +encode_DmyHMS sp format = LT.toStrict . TB.toLazyText . builder_DmyHMS sp format encode_DmyIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Text@@ -1200,7 +1202,7 @@ LB.toStrict . BB.toLazyByteString . builderTimespanUtf8 sp builderTimespanUtf8 :: SubsecondPrecision -> Timespan -> BB.Builder-builderTimespanUtf8 sp (Timespan ns) = +builderTimespanUtf8 sp (Timespan ns) = int64Builder sInt64 <> prettyNanosecondsBuilderUtf8 sp nsRemainder where (!sInt64,!nsRemainder) = quotRem ns 1000000000@@ -1299,7 +1301,7 @@ LT.toStrict . TB.toLazyText . builder_YmdHMSz offsetFormat sp datetimeFormat builder_DmyHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> TB.Builder-builder_DmyHMSz offsetFormat sp datetimeFormat (OffsetDatetime datetime offset) = +builder_DmyHMSz offsetFormat sp datetimeFormat (OffsetDatetime datetime offset) = builder_DmyHMS sp datetimeFormat datetime <> builderOffset offsetFormat offset @@ -1309,7 +1311,7 @@ <*> parserOffset offsetFormat builder_DmyIMS_p_z :: OffsetFormat -> MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> TB.Builder-builder_DmyIMS_p_z offsetFormat meridiemLocale sp datetimeFormat (OffsetDatetime datetime offset) = +builder_DmyIMS_p_z offsetFormat meridiemLocale sp datetimeFormat (OffsetDatetime datetime offset) = builder_DmyIMS_p meridiemLocale sp datetimeFormat datetime <> " " <> builderOffset offsetFormat offset@@ -1592,6 +1594,83 @@ <> ":" <> indexTwoDigitByteStringBuilder b +-- Zepto parsers++zeptoUtf8_YmdHMS :: DatetimeFormat -> Z.Parser Datetime+zeptoUtf8_YmdHMS (DatetimeFormat mdateSep msep' mtimeSep) = do+ date <- zeptoUtf8_Ymd mdateSep+ let msep = BC.singleton <$> msep'+ traverse_ Z.string msep+ time <- zeptoUtf8_HMS mtimeSep+ return (Datetime date time)++zeptoCountZeroes :: Z.Parser Int+zeptoCountZeroes = do+ bs <- Z.takeWhile (0x30 ==)+ pure $! BC.length bs++zeptoUtf8_Ymd :: Maybe Char -> Z.Parser Chronos.Date+zeptoUtf8_Ymd msep' = do+ y <- zeptoFixedDigitsIntBS 4+ let msep = BC.singleton <$> msep'+ traverse_ Z.string msep+ m <- zeptoFixedDigitsIntBS 2+ when (m < 1 || m > 12) (fail "month must be between 1 and 12")+ traverse_ Z.string msep+ d <- zeptoFixedDigitsIntBS 2+ when (d < 1 || d > 31) (fail "day must be between 1 and 31")+ return (Date (Year y) (Month $ m - 1) (DayOfMonth d))++zeptoUtf8_HMS :: Maybe Char -> Z.Parser Chronos.TimeOfDay+zeptoUtf8_HMS msep' = do+ h <- zeptoFixedDigitsIntBS 2+ when (h > 23) (fail "hour must be between 0 and 23")+ let msep = BC.singleton <$> msep'+ traverse_ Z.string msep+ m <- zeptoFixedDigitsIntBS 2+ when (m > 59) (fail "minute must be between 0 and 59")+ traverse_ Z.string msep+ ns <- zeptoSecondsAndNanosecondsUtf8+ return (TimeOfDay h m ns)++zeptoFixedDigitsIntBS :: Int -> Z.Parser Int+zeptoFixedDigitsIntBS n = do+ t <- Z.take n+ case BC.readInt t of+ Nothing -> fail "datetime decoding could not parse integral bytestring (a)"+ Just (i,r) -> if BC.null r+ then return i+ else fail "datetime decoding could not parse integral bytestring (b)"++zeptoSecondsAndNanosecondsUtf8 :: Z.Parser Int64+zeptoSecondsAndNanosecondsUtf8 = do+ s' <- zeptoFixedDigitsIntBS 2+ let s = fromIntegral s' :: Int64+ when (s > 60) (fail "seconds must be between 0 and 60")+ nanoseconds <-+ ( do _ <- Z.string "."+ numberOfZeroes <- zeptoCountZeroes+ x <- zdecimal+ let totalDigits = countDigits x + numberOfZeroes+ result = if totalDigits == 9+ then x+ else if totalDigits < 9+ then x * raiseTenTo (9 - totalDigits)+ else quot x (raiseTenTo (totalDigits - 9))+ return (fromIntegral result)+ ) <|> return 0+ return (s * 1000000000 + nanoseconds)++zdecimal :: Z.Parser Int64+zdecimal = do+ digits <- Z.takeWhile wordIsDigit+ case BC.readInt digits of+ Nothing -> fail "somehow this didn't work"+ Just (i,_) -> pure $! fromIntegral i++wordIsDigit :: Word8 -> Bool+wordIsDigit a = 0x30 <= a && a <= 0x39+ january :: Month january = Month 0 @@ -2087,4 +2166,3 @@ aesonParserOffset t = case decodeOffset OffsetFormatColonOn t of Nothing -> fail "could not parse Offset" Just x -> return x-
src/Chronos/Internal/CTimespec.hs view
@@ -51,6 +51,3 @@ return ((s * 1000000000) + ns) #endif---
src/cbits/hs-time.c view
@@ -1,5 +1,10 @@ #ifdef __MACH__+#include <Availability.h> #include <sys/time.h>++#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED++#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1012 //clock_gettime is not implemented on OSX int clock_gettime(int clk_id, struct timespec* t) { struct timeval now;@@ -9,4 +14,9 @@ t->tv_nsec = now.tv_usec * 1000; return 0; }+#else+#endif++#endif+ #endif