chronos 1.1.1 → 1.1.2
raw patch · 5 files changed
+127/−39 lines, 5 filesdep ~aesondep ~deepseqPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, deepseq
API changes (from Hackage documentation)
+ Chronos: builderIso8601 :: Datetime -> Builder
+ Chronos: encodeIso8601 :: Datetime -> Text
+ Chronos: instance Control.DeepSeq.NFData (Chronos.UnboxedMonthMatch a)
+ Chronos: instance Control.DeepSeq.NFData Chronos.Date
+ Chronos: instance Control.DeepSeq.NFData Chronos.Datetime
+ Chronos: instance Control.DeepSeq.NFData Chronos.DatetimeFormat
+ Chronos: instance Control.DeepSeq.NFData Chronos.Day
+ Chronos: instance Control.DeepSeq.NFData Chronos.DayOfMonth
+ Chronos: instance Control.DeepSeq.NFData Chronos.DayOfWeek
+ Chronos: instance Control.DeepSeq.NFData Chronos.DayOfYear
+ Chronos: instance Control.DeepSeq.NFData Chronos.Month
+ Chronos: instance Control.DeepSeq.NFData Chronos.MonthDate
+ Chronos: instance Control.DeepSeq.NFData Chronos.Offset
+ Chronos: instance Control.DeepSeq.NFData Chronos.OffsetDatetime
+ Chronos: instance Control.DeepSeq.NFData Chronos.OffsetFormat
+ Chronos: instance Control.DeepSeq.NFData Chronos.OrdinalDate
+ Chronos: instance Control.DeepSeq.NFData Chronos.SubsecondPrecision
+ Chronos: instance Control.DeepSeq.NFData Chronos.Time
+ Chronos: instance Control.DeepSeq.NFData Chronos.TimeInterval
+ Chronos: instance Control.DeepSeq.NFData Chronos.TimeOfDay
+ Chronos: instance Control.DeepSeq.NFData Chronos.TimeParts
+ Chronos: instance Control.DeepSeq.NFData Chronos.Timespan
+ Chronos: instance Control.DeepSeq.NFData Chronos.Year
+ Chronos: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Chronos.DatetimeLocale a)
+ Chronos: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Chronos.DayOfWeekMatch a)
+ Chronos: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Chronos.MeridiemLocale a)
+ Chronos: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Chronos.MonthMatch a)
+ Chronos: instance Data.Aeson.Types.FromJSON.FromJSON Chronos.Datetime
Files
- bench/Bench.hs +0/−15
- chronos.cabal +9/−3
- src/Chronos.hs +108/−21
- src/Chronos/Internal/CTimespec.hs +2/−0
- test/Spec.hs +8/−0
bench/Bench.hs view
@@ -110,18 +110,3 @@ ] ] ]--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-deriving instance NFData Chronos.Time
chronos.cabal view
@@ -1,10 +1,10 @@-cabal-version: 2.2+cabal-version: 3.0 name: chronos version:- 1.1.1+ 1.1.2 synopsis:- A performant time library+ A high-performance time library description: Chronos is a performance-oriented time library for Haskell, with a straightforward API. The main differences between this@@ -61,6 +61,7 @@ , attoparsec >= 0.13 && < 0.14 , base >= 4.9 && < 5 , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4.4.0 , hashable >= 1.2 && < 1.4 , primitive >= 0.6.4 && < 0.8 , semigroups >= 0.16 && < 0.20@@ -89,10 +90,12 @@ build-depends: , HUnit , QuickCheck+ , aeson >= 1.1 && < 1.5 , attoparsec , base , bytestring , chronos+ , deepseq >= 1.4.4.0 , test-framework , test-framework-hunit , test-framework-quickcheck2@@ -115,7 +118,9 @@ , QuickCheck , base , chronos+ , deepseq >= 1.4.4.0 , doctest >= 0.10+ , torsor >= 0.1 && < 0.2 default-language: Haskell2010 @@ -134,6 +139,7 @@ , chronos , criterion , deepseq+ , deepseq >= 1.4.4.0 , old-locale , text , thyme
src/Chronos.hs view
@@ -155,6 +155,8 @@ , builder_YmdIMSp , builder_YmdIMS_p , builderW3C+ , builderIso8601+ , encodeIso8601 , encode_DmyHMS , encode_DmyIMS_p , encode_YmdHMS@@ -270,6 +272,7 @@ ) where import Control.Applicative+import Control.DeepSeq (NFData(..), deepseq) import Control.Exception (evaluate) import Control.Monad import Data.Aeson (FromJSON,ToJSON,FromJSONKey,ToJSONKey)@@ -473,7 +476,7 @@ where mx = if m >= 1 && m <= 12 then fromIntegral (m - 1)- else 1+ else 0 -- | Construct a 'Time' from year, month, day, hour, minute, second: --@@ -509,7 +512,7 @@ SYSTEMTIME{..} <- W32.getSystemTime let date = Date { dateYear = Year (fromIntegral wYear)- , dateMonth = Month (fromIntegral wMonth)+ , dateMonth = Month (fromIntegral wMonth - 1) , dateDay = DayOfMonth (fromIntegral wDay) } let secNano = (fromIntegral wSecond :: Int64) * 1000000000@@ -953,13 +956,13 @@ -- | Given a 'Date' and a separator, construct a 'Text' 'TB.Builder' -- corresponding to Year\/Month\/Day encoding. builder_Ymd :: Maybe Char -> Date -> TB.Builder-builder_Ymd msep (Date (Year y) m d) = case msep of+builder_Ymd msep (Date y m d) = case msep of Nothing ->- TB.decimal y+ yearToZeroPaddedDigit y <> monthToZeroPaddedDigit m <> zeroPadDayOfMonth d Just sep -> let sepBuilder = TB.singleton sep in- TB.decimal y+ yearToZeroPaddedDigit y <> sepBuilder <> monthToZeroPaddedDigit m <> sepBuilder@@ -968,17 +971,17 @@ -- | Given a 'Date' and a separator, construct a 'Text' 'TB.Builder' -- corresponding to a Day\/Month\/Year encoding. builder_Dmy :: Maybe Char -> Date -> TB.Builder-builder_Dmy msep (Date (Year y) m d) = case msep of+builder_Dmy msep (Date y m d) = case msep of Nothing -> zeroPadDayOfMonth d <> monthToZeroPaddedDigit m- <> TB.decimal y+ <> yearToZeroPaddedDigit y Just sep -> let sepBuilder = TB.singleton sep in zeroPadDayOfMonth d <> sepBuilder <> monthToZeroPaddedDigit m <> sepBuilder- <> TB.decimal y+ <> yearToZeroPaddedDigit y -- | Parse a Year\/Month\/Day-encoded 'Date' that uses the -- given separator.@@ -1070,13 +1073,13 @@ -- | Given a 'Date' and a separator, construct a 'ByteString' 'BB.Builder' -- corresponding to a Day\/Month\/Year encoding. builderUtf8_Ymd :: Maybe Char -> Date -> BB.Builder-builderUtf8_Ymd msep (Date (Year y) m d) = case msep of+builderUtf8_Ymd msep (Date y m d) = case msep of Nothing ->- BB.intDec y+ yearToZeroPaddedDigitBS y <> monthToZeroPaddedDigitBS m <> zeroPadDayOfMonthBS d Just sep -> let sepBuilder = BB.char7 sep in- BB.intDec y+ yearToZeroPaddedDigitBS y <> sepBuilder <> monthToZeroPaddedDigitBS m <> sepBuilder@@ -1427,9 +1430,21 @@ -- | Construct a 'Text' 'TB.Builder' corresponding to the W3C -- encoding of the given 'Datetime'.+--+-- Deprecated. This is just a poorly named alias for 'builderIso8601'. builderW3C :: Datetime -> TB.Builder-builderW3C = builder_YmdHMS SubsecondPrecisionAuto w3c+builderW3C = builderIso8601 +-- | Construct a 'Text' 'TB.Builder' corresponding to the ISO-8601+-- encoding of the given 'Datetime'.+builderIso8601 :: Datetime -> TB.Builder+builderIso8601 = builder_YmdHMS SubsecondPrecisionAuto w3c++-- | Construct 'Text' corresponding to the ISO-8601+-- encoding of the given 'Datetime'.+encodeIso8601 :: Datetime -> Text+encodeIso8601 = LT.toStrict . TB.toLazyText . builderIso8601+ -- | Decode a Year\/Month\/Day,Hour\/Minute\/Second-encoded 'Datetime' -- from 'Text' that was encoded with the given 'DatetimeFormat'. decode_YmdHMS :: DatetimeFormat -> Text -> Maybe Datetime@@ -2649,6 +2664,13 @@ tenRaisedToSmallPowers :: UVector.Vector Int64 tenRaisedToSmallPowers = UVector.fromList $ map (10 ^) [0 :: Int ..15] +yearToZeroPaddedDigit :: Year -> TB.Builder+yearToZeroPaddedDigit (Year x)+ | x < 10 = "000" <> TB.decimal x+ | x < 100 = "00" <> TB.decimal x+ | x < 1000 = "0" <> TB.decimal x+ | otherwise = TB.decimal x+ monthToZeroPaddedDigit :: Month -> TB.Builder monthToZeroPaddedDigit (Month x) = indexTwoDigitTextBuilder (x + 1)@@ -2656,6 +2678,13 @@ zeroPadDayOfMonth :: DayOfMonth -> TB.Builder zeroPadDayOfMonth (DayOfMonth d) = indexTwoDigitTextBuilder d +yearToZeroPaddedDigitBS :: Year -> BB.Builder+yearToZeroPaddedDigitBS (Year x)+ | x < 10 = "000" <> BB.intDec x+ | x < 100 = "00" <> BB.intDec x+ | x < 1000 = "0" <> BB.intDec x+ | otherwise = BB.intDec x+ monthToZeroPaddedDigitBS :: Month -> BB.Builder monthToZeroPaddedDigitBS (Month x) = indexTwoDigitByteStringBuilder (x + 1)@@ -2710,26 +2739,27 @@ -- | A day represented as the modified Julian date, the number of days -- since midnight on November 17, 1858. newtype Day = Day { getDay :: Int }- deriving (Show,Read,Eq,Ord,Hashable,Enum,ToJSON,FromJSON,Storable,Prim)+ deriving (Show,Read,Eq,Ord,Hashable,Enum,ToJSON,FromJSON,Storable,Prim,NFData)+ instance Torsor Day Int where add i (Day d) = Day (d + i) difference (Day a) (Day b) = a - b -- | The day of the week. newtype DayOfWeek = DayOfWeek { getDayOfWeek :: Int }- deriving (Show,Read,Eq,Ord,Hashable)+ deriving (Show,Read,Eq,Ord,Hashable,NFData) -- | The day of the month. newtype DayOfMonth = DayOfMonth { getDayOfMonth :: Int }- deriving (Show,Read,Eq,Ord,Prim,Enum)+ deriving (Show,Read,Eq,Ord,Prim,Enum,NFData) -- | The day of the year. newtype DayOfYear = DayOfYear { getDayOfYear :: Int }- deriving (Show,Read,Eq,Ord,Prim)+ deriving (Show,Read,Eq,Ord,Prim,NFData) -- | The month of the year. newtype Month = Month { getMonth :: Int }- deriving (Show,Read,Eq,Ord,Prim)+ deriving (Show,Read,Eq,Ord,Prim,NFData) instance Enum Month where fromEnum = getMonth@@ -2750,36 +2780,39 @@ -- | The number of years elapsed since the beginning -- of the Common Era. newtype Year = Year { getYear :: Int }- deriving (Show,Read,Eq,Ord)+ deriving (Show,Read,Eq,Ord, NFData) -- | A <https://en.wikipedia.org/wiki/UTC_offset UTC offset>. newtype Offset = Offset { getOffset :: Int }- deriving (Show,Read,Eq,Ord,Enum)+ deriving (Show,Read,Eq,Ord,Enum,NFData) -- | POSIX time with nanosecond resolution. newtype Time = Time { getTime :: Int64 }- deriving (FromJSON,ToJSON,Hashable,Eq,Ord,Show,Read,Storable,Prim,Bounded)+ deriving (FromJSON,ToJSON,Hashable,Eq,Ord,Show,Read,Storable,Prim,Bounded, NFData) -- | Match a 'DayOfWeek'. By `match`, we mean that a 'DayOfWeekMatch' -- is a mapping from the integer value of a 'DayOfWeek' to some value -- of type @a@. You should construct a 'DayOfWeekMatch' with -- 'buildDayOfWeekMatch', and match it using 'caseDayOfWeek'. newtype DayOfWeekMatch a = DayOfWeekMatch { getDayOfWeekMatch :: Vector a }+ deriving (NFData) -- | Match a 'Month'. By `match`, we mean that a 'MonthMatch' is -- a mapping from the integer value of a 'Month' to some value of -- type @a@. You should construct a 'MonthMatch' with -- 'buildMonthMatch', and match it using 'caseMonth'. newtype MonthMatch a = MonthMatch { getMonthMatch :: Vector a }+ deriving (NFData) -- | Like 'MonthMatch', but the matched value can have an instance of -- 'UVector.Unbox'. newtype UnboxedMonthMatch a = UnboxedMonthMatch { getUnboxedMonthMatch :: UVector.Vector a }+ deriving (NFData) -- | A timespan. This is represented internally as a number -- of nanoseconds. newtype Timespan = Timespan { getTimespan :: Int64 }- deriving (Show,Read,Eq,Ord,ToJSON,FromJSON,Additive)+ deriving (Show,Read,Eq,Ord,ToJSON,FromJSON,Additive,NFData) instance Semigroup Timespan where (Timespan a) <> (Timespan b) = Timespan (a + b)@@ -2805,6 +2838,11 @@ | SubsecondPrecisionFixed {-# UNPACK #-} !Int -- ^ Specify number of places after decimal deriving (Eq, Ord, Show, Read) +instance NFData SubsecondPrecision where+ rnf (SubsecondPrecisionAuto) = ()+ rnf (SubsecondPrecisionFixed a) = a `deepseq` ()++ -- | A date as represented by the Gregorian calendar. data Date = Date { dateYear :: {-# UNPACK #-} !Year@@ -2812,6 +2850,9 @@ , dateDay :: {-# UNPACK #-} !DayOfMonth } deriving (Show,Read,Eq,Ord) +instance NFData Date where+ rnf (Date y m d) = y `deepseq` m `deepseq` d `deepseq` ()+ -- | An 'OrdinalDate' is a 'Year' and the number of days elapsed -- since the 'Year' began. data OrdinalDate = OrdinalDate@@ -2819,6 +2860,9 @@ , ordinalDateDayOfYear :: {-# UNPACK #-} !DayOfYear } deriving (Show,Read,Eq,Ord) +instance NFData OrdinalDate where+ rnf (OrdinalDate y d) = y `deepseq` d `deepseq` ()+ -- | A month and the day of the month. This does not actually represent -- a specific date, since this recurs every year. data MonthDate = MonthDate@@ -2826,19 +2870,31 @@ , monthDateDay :: {-# UNPACK #-} !DayOfMonth } deriving (Show,Read,Eq,Ord) +instance NFData MonthDate where+ rnf (MonthDate m d) = m `deepseq` d `deepseq` ()+ -- | A 'Date' as represented by the Gregorian calendar -- and a 'TimeOfDay'.+-- While the 'ToJSON' instance encodes with a hyphen separator, the+-- 'FromJSON' instance allows any non-digit character to act as+-- separator, using the lenient parser. data Datetime = Datetime { datetimeDate :: {-# UNPACK #-} !Date , datetimeTime :: {-# UNPACK #-} !TimeOfDay } deriving (Show,Read,Eq,Ord) +instance NFData Datetime where+ rnf (Datetime d t) = d `deepseq` t `deepseq` ()+ -- | A 'Datetime' with a time zone 'Offset'. data OffsetDatetime = OffsetDatetime { offsetDatetimeDatetime :: {-# UNPACK #-} !Datetime , offsetDatetimeOffset :: {-# UNPACK #-} !Offset } deriving (Show,Read,Eq,Ord) +instance NFData OffsetDatetime where+ rnf (OffsetDatetime dt o) = dt `deepseq` o `deepseq` ()+ -- | A time of day with nanosecond resolution. data TimeOfDay = TimeOfDay { timeOfDayHour :: {-# UNPACK #-} !Int@@ -2846,6 +2902,9 @@ , timeOfDayNanoseconds :: {-# UNPACK #-} !Int64 } deriving (Show,Read,Eq,Ord) +instance NFData TimeOfDay where+ rnf (TimeOfDay h m s) = h `deepseq` m `deepseq` s `deepseq` ()+ -- | The format of a 'Datetime'. In particular -- this provides separators for parts of the 'Datetime' -- and nothing else.@@ -2858,6 +2917,9 @@ -- ^ Separator in the time } deriving (Show,Read,Eq,Ord) +instance NFData DatetimeFormat where+ rnf (DatetimeFormat s1 s2 s3) = s1 `deepseq` s2 `deepseq` s3 `deepseq` ()+ -- | Formatting settings for a timezone offset. data OffsetFormat = OffsetFormatColonOff -- ^ @%z@ (e.g., -0400)@@ -2866,6 +2928,9 @@ | OffsetFormatColonAuto -- ^ @%:::z@ (e.g., -04, +05:30) deriving (Show,Read,Eq,Ord,Enum,Bounded,Generic) +instance NFData OffsetFormat where+ rnf !_ = ()+ -- | Locale-specific formatting for weekdays and months. The -- type variable will likely be instantiated to @Text@ -- or @ByteString@.@@ -2880,6 +2945,10 @@ -- ^ abbreviated months starting with January, 12 elements } +instance NFData a => NFData (DatetimeLocale a) where+ rnf (DatetimeLocale d1 d2 m1 m2) =+ d1 `deepseq` d2 `deepseq` m1 `deepseq` m2 `deepseq` ()+ -- | A TimeInterval represents a start and end time. -- It can sometimes be more ergonomic than the 'Torsor' API when -- you only care about whether or not a 'Time' is within a certain range.@@ -2890,12 +2959,18 @@ data TimeInterval = TimeInterval {-# UNPACK #-} !Time {-# UNPACK #-} !Time deriving (Read,Show,Eq,Ord,Bounded) +instance NFData TimeInterval where+ rnf (TimeInterval t1 t2) = t1 `deepseq` t2 `deepseq` ()+ -- | Locale-specific formatting for AM and PM. data MeridiemLocale a = MeridiemLocale { meridiemLocaleAm :: !a , meridiemLocalePm :: !a } deriving (Read,Show,Eq,Ord) +instance NFData a => NFData (MeridiemLocale a) where+ rnf (MeridiemLocale am pm) = am `deepseq` pm `deepseq` ()+ newtype instance UVector.MVector s Month = MV_Month (PVector.MVector s Month) newtype instance UVector.Vector Month = V_Month (PVector.Vector Month) @@ -3045,6 +3120,14 @@ toJSON = AE.String . encode_YmdHMS SubsecondPrecisionAuto hyphen toEncoding x = AEE.unsafeToEncoding (BB.char7 '"' SG.<> builderUtf8_YmdHMS SubsecondPrecisionAuto hyphen x SG.<> BB.char7 '"') +instance FromJSON Datetime where+ parseJSON =+ AE.withText "Datetime" aesonParserDatetime++aesonParserDatetime :: Text -> AET.Parser Datetime+aesonParserDatetime =+ either (const (fail "could not parse Datetime")) pure . AT.parseOnly (parser_lenient <* AT.endOfInput)+ instance ToJSON Offset where toJSON = AE.String . encodeOffset OffsetFormatColonOn toEncoding x = AEE.unsafeToEncoding (BB.char7 '"' SG.<> builderOffsetUtf8 OffsetFormatColonOn x SG.<> BB.char7 '"')@@ -3079,6 +3162,10 @@ , timePartsOffset :: !Int } deriving (Eq, Read, Show)++instance NFData TimeParts where+ rnf (TimeParts d mo y h m s ss o) =+ d `deepseq` mo `deepseq` y `deepseq` h `deepseq` m `deepseq` s `deepseq` ss `deepseq` o `deepseq` () -- | Deconstruct a 'Time' into its 'TimeParts'. timeParts :: Offset -> Time -> TimeParts
src/Chronos/Internal/CTimespec.hs view
@@ -7,7 +7,9 @@ ( #ifndef mingw32_HOST_OS getPosixNanoseconds+#ifndef ghcjs_HOST_OS , CTimespec(..)+#endif #endif ) where
test/Spec.hs view
@@ -6,6 +6,7 @@ module Main (main) where import Chronos.Types+import qualified Data.Aeson as AE import Data.ByteString (ByteString) import Data.Int (Int64) import Data.List (intercalate)@@ -169,6 +170,8 @@ (dateBuilder (Just '-') "2016-01-01" (Date (Year 2016) (Month 0) (DayOfMonth 1))) , PH.testCase "Separator 2" (dateBuilder (Just '-') "1876-09-27" (Date (Year 1876) (Month 8) (DayOfMonth 27)))+ , PH.testCase "zero-pad year"+ (dateBuilder (Just '-') "0001-01-01" (Date (Year 1) (Month 0) (DayOfMonth 1))) ] , testProperty "Builder Parser Isomorphism (Y-m-d)" $ propEncodeDecodeIso (LText.toStrict . Builder.toLazyText . C.builder_Ymd (Just '-'))@@ -479,6 +482,11 @@ (C.timeToDayOfWeek (Time (-17308800000000000)) @?= DayOfWeek 6) , PH.testCase "Tuesday, June 6, 1944 4:00:00 PM" (C.timeToDayOfWeek (Time (-806918400000000000)) @?= DayOfWeek 2)+ ]+ , testGroup "json"+ [ PH.testCase "Datetime" $+ let dt = Datetime (Date (Year 3000) (Month 11) (DayOfMonth 31)) (TimeOfDay 0 0 0)+ in AE.eitherDecode (AE.encode dt) @?= Right dt ] ]