chronos 0.2.0 → 0.3
raw patch · 18 files changed
+765/−137 lines, 18 filesdep ~attoparsecdep ~primitivePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: attoparsec, primitive
API changes (from Hackage documentation)
- Chronos.Internal.FastText: MArrayAppend :: (Int -> MArray s -> ST s Int) -> !Int -> MArrayAppend s
- Chronos.Internal.FastText: combine :: MArrayAppend s -> MArrayAppend s -> MArrayAppend s
- Chronos.Internal.FastText: data MArrayAppend s
- Chronos.Internal.FastText: fromText :: Text -> MArrayAppend s
- Chronos.Internal.FastText: runMArrayAppend :: (forall s. MArrayAppend s) -> Text
- Chronos.Internal.FastText: singleton :: Char -> MArrayAppend s
- Chronos.Types: Days :: Int -> Days
- Chronos.Types: Months :: Int -> Months
- Chronos.Types: [datetimeLocaleAm] :: DatetimeLocale a -> !a
- Chronos.Types: [datetimeLocalePm] :: DatetimeLocale a -> !a
- Chronos.Types: [getDays] :: Days -> Int
- Chronos.Types: [getMonths] :: Months -> Int
- Chronos.Types: [ordinalDateMonth] :: OrdinalDate -> {-# UNPACK #-} !DayOfYear
- Chronos.Types: instance Data.Aeson.Types.FromJSON.FromJSON Chronos.Types.PosixTime
- Chronos.Types: instance Data.Aeson.Types.FromJSON.FromJSON Chronos.Types.TaiTime
- Chronos.Types: instance Data.Aeson.Types.ToJSON.ToJSON Chronos.Types.PosixTime
- Chronos.Types: instance Data.Aeson.Types.ToJSON.ToJSON Chronos.Types.TaiTime
- Chronos.Types: instance Data.Hashable.Class.Hashable Chronos.Types.Days
- Chronos.Types: instance GHC.Classes.Eq Chronos.Types.Days
- Chronos.Types: instance GHC.Classes.Eq Chronos.Types.Months
- Chronos.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Chronos.Types.DatetimeFormat a)
- Chronos.Types: instance GHC.Classes.Ord Chronos.Types.Days
- Chronos.Types: instance GHC.Classes.Ord Chronos.Types.Months
- Chronos.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Chronos.Types.DatetimeFormat a)
- Chronos.Types: instance GHC.Read.Read Chronos.Types.Days
- Chronos.Types: instance GHC.Read.Read Chronos.Types.Months
- Chronos.Types: instance GHC.Read.Read a => GHC.Read.Read (Chronos.Types.DatetimeFormat a)
- Chronos.Types: instance GHC.Show.Show Chronos.Types.Days
- Chronos.Types: instance GHC.Show.Show Chronos.Types.Months
- Chronos.Types: instance GHC.Show.Show a => GHC.Show.Show (Chronos.Types.DatetimeFormat a)
- Chronos.Types: newtype Days
- Chronos.Types: newtype Months
+ Chronos.Date.ByteString.Char7: builder_Ymd :: Maybe Char -> Date -> Builder
+ Chronos.Date.ByteString.Char7: parser_Ymd :: Maybe Char -> Parser Date
+ Chronos.Datetime.ByteString.Char7: builderW3 :: Datetime -> Builder
+ Chronos.Datetime.ByteString.Char7: builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
+ Chronos.Datetime.ByteString.Char7: builder_YmdIMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
+ Chronos.Datetime.ByteString.Char7: builder_YmdIMSp :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
+ Chronos.Datetime.ByteString.Char7: decode_YmdHMS :: DatetimeFormat -> ByteString -> Maybe Datetime
+ Chronos.Datetime.ByteString.Char7: decode_YmdHMS_opt_S :: DatetimeFormat -> ByteString -> Maybe Datetime
+ Chronos.Datetime.ByteString.Char7: encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> ByteString
+ Chronos.Datetime.ByteString.Char7: encode_YmdIMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> ByteString
+ Chronos.Datetime.ByteString.Char7: parser_YmdHMS :: DatetimeFormat -> Parser Datetime
+ Chronos.Datetime.ByteString.Char7: parser_YmdHMS_opt_S :: DatetimeFormat -> Parser Datetime
+ Chronos.Day: today :: IO Day
+ Chronos.Day: tomorrow :: IO Day
+ Chronos.Day: yesterday :: IO Day
+ Chronos.Internal: indexTwoDigitByteStringBuilder :: Int -> Builder
+ Chronos.Internal: parseFixedDigitsIntBS :: Int -> Parser Int
+ Chronos.Internal: twoDigitByteStringBuilder :: Vector Builder
+ Chronos.Internal: twoDigitStrings :: [String]
+ Chronos.Internal.Conversion: addDay :: Int -> Day -> Day
+ Chronos.Internal.Conversion: diffDay :: Day -> Day -> Int
+ Chronos.Internal.Format: monthToZeroPaddedDigitBS :: Month -> Builder
+ Chronos.Internal.Format: zeroPadDayOfMonthBS :: DayOfMonth -> Builder
+ Chronos.Nanoseconds: negate :: Nanoseconds -> Nanoseconds
+ Chronos.OffsetDatetime.ByteString.Char7: buildOffset_z :: Offset -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: buildOffset_z1 :: Offset -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: buildOffset_z2 :: Offset -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: buildOffset_z3 :: Offset -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: builderW3 :: OffsetDatetime -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: offsetBuilder :: OffsetFormat -> Offset -> Builder
+ Chronos.OffsetDatetime.ByteString.Char7: offsetParser :: OffsetFormat -> Parser Offset
+ Chronos.OffsetDatetime.ByteString.Char7: parseOffset_z :: Parser Offset
+ Chronos.OffsetDatetime.ByteString.Char7: parseOffset_z1 :: Parser Offset
+ Chronos.OffsetDatetime.ByteString.Char7: parseOffset_z2 :: Parser Offset
+ Chronos.OffsetDatetime.ByteString.Char7: parseOffset_z3 :: Parser Offset
+ Chronos.OffsetDatetime.ByteString.Char7: parseSignedness :: Parser Bool
+ Chronos.OffsetDatetime.ByteString.Char7: parser_YmdHMSz :: OffsetFormat -> DatetimeFormat -> Parser OffsetDatetime
+ Chronos.TimeOfDay.ByteString.Char7: builder_HMS :: SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: builder_IMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: builder_IMSp :: MeridiemLocale ByteString -> SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: countZeroes :: Parser Int
+ Chronos.TimeOfDay.ByteString.Char7: int64Builder :: Int64 -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: internalBuilder_I :: Int -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: internalBuilder_NS :: SubsecondPrecision -> Maybe Char -> Int -> Int64 -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: internalBuilder_p :: MeridiemLocale ByteString -> Int -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: microsecondsBuilder :: Int64 -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: millisecondsBuilder :: Int64 -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: nanosecondsBuilder :: Int64 -> Builder
+ Chronos.TimeOfDay.ByteString.Char7: parseSecondsAndNanoseconds :: Parser Int64
+ Chronos.TimeOfDay.ByteString.Char7: parser_HMS :: Maybe Char -> Parser TimeOfDay
+ Chronos.TimeOfDay.ByteString.Char7: parser_HMS_opt_S :: Maybe Char -> Parser TimeOfDay
+ Chronos.TimeOfDay.ByteString.Char7: prettyNanosecondsBuilder :: SubsecondPrecision -> Int64 -> Builder
+ Chronos.Types: [ordinalDateDayOfYear] :: OrdinalDate -> {-# UNPACK #-} !DayOfYear
+ Chronos.Types: instance Data.Aeson.Types.Class.FromJSON Chronos.Types.Nanoseconds
+ Chronos.Types: instance Data.Aeson.Types.Class.FromJSON Chronos.Types.PosixTime
+ Chronos.Types: instance Data.Aeson.Types.Class.FromJSON Chronos.Types.TaiTime
+ Chronos.Types: instance Data.Aeson.Types.Class.ToJSON Chronos.Types.Nanoseconds
+ Chronos.Types: instance Data.Aeson.Types.Class.ToJSON Chronos.Types.PosixTime
+ Chronos.Types: instance Data.Aeson.Types.Class.ToJSON Chronos.Types.TaiTime
+ Chronos.Types: instance GHC.Classes.Eq Chronos.Types.DatetimeFormat
+ Chronos.Types: instance GHC.Classes.Ord Chronos.Types.DatetimeFormat
+ Chronos.Types: instance GHC.Enum.Enum Chronos.Types.Day
+ Chronos.Types: instance GHC.Generics.Constructor Chronos.Types.C1_0OffsetFormat
+ Chronos.Types: instance GHC.Generics.Constructor Chronos.Types.C1_1OffsetFormat
+ Chronos.Types: instance GHC.Generics.Constructor Chronos.Types.C1_2OffsetFormat
+ Chronos.Types: instance GHC.Generics.Constructor Chronos.Types.C1_3OffsetFormat
+ Chronos.Types: instance GHC.Generics.Datatype Chronos.Types.D1OffsetFormat
+ Chronos.Types: instance GHC.Read.Read Chronos.Types.DatetimeFormat
+ Chronos.Types: instance GHC.Show.Show Chronos.Types.DatetimeFormat
- Chronos.Datetime.Text: builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder
+ Chronos.Datetime.Text: builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
- Chronos.Datetime.Text: builder_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder
+ Chronos.Datetime.Text: builder_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
- Chronos.Datetime.Text: builder_YmdIMSp :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder
+ Chronos.Datetime.Text: builder_YmdIMSp :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
- Chronos.Datetime.Text: decode_YmdHMS :: DatetimeFormat Char -> Text -> Maybe Datetime
+ Chronos.Datetime.Text: decode_YmdHMS :: DatetimeFormat -> Text -> Maybe Datetime
- Chronos.Datetime.Text: decode_YmdHMS_opt_S :: DatetimeFormat Char -> Text -> Maybe Datetime
+ Chronos.Datetime.Text: decode_YmdHMS_opt_S :: DatetimeFormat -> Text -> Maybe Datetime
- Chronos.Datetime.Text: encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Text
+ Chronos.Datetime.Text: encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Text
- Chronos.Datetime.Text: encode_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Text
+ Chronos.Datetime.Text: encode_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Text
- Chronos.Datetime.Text: parser_YmdHMS :: DatetimeFormat Char -> Parser Datetime
+ Chronos.Datetime.Text: parser_YmdHMS :: DatetimeFormat -> Parser Datetime
- Chronos.Datetime.Text: parser_YmdHMS_opt_S :: DatetimeFormat Char -> Parser Datetime
+ Chronos.Datetime.Text: parser_YmdHMS_opt_S :: DatetimeFormat -> Parser Datetime
- Chronos.Internal: indexTwoDigitTextBuilder :: Integral i => i -> Builder
+ Chronos.Internal: indexTwoDigitTextBuilder :: Int -> Builder
- Chronos.Internal: parseFixedDigits :: Integral i => Int -> Parser i
+ Chronos.Internal: parseFixedDigits :: Int -> Parser Int
- Chronos.Internal.Conversion: offsetTimeOfDay :: Offset -> TimeOfDay -> (Days, TimeOfDay)
+ Chronos.Internal.Conversion: offsetTimeOfDay :: Offset -> TimeOfDay -> (Int, TimeOfDay)
- Chronos.Locale.English.Text: datetimeCompact :: DatetimeFormat Char
+ Chronos.Locale.English.Text: datetimeCompact :: DatetimeFormat
- Chronos.Locale.English.Text: datetimeHyphen :: DatetimeFormat Char
+ Chronos.Locale.English.Text: datetimeHyphen :: DatetimeFormat
- Chronos.Locale.English.Text: datetimeSlash :: DatetimeFormat Char
+ Chronos.Locale.English.Text: datetimeSlash :: DatetimeFormat
- Chronos.Locale.English.Text: datetimeW3 :: DatetimeFormat Char
+ Chronos.Locale.English.Text: datetimeW3 :: DatetimeFormat
- Chronos.OffsetDatetime.Text: builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat Char -> OffsetDatetime -> Builder
+ Chronos.OffsetDatetime.Text: builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder
- Chronos.OffsetDatetime.Text: builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> OffsetDatetime -> Builder
+ Chronos.OffsetDatetime.Text: builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder
- Chronos.OffsetDatetime.Text: parser_YmdHMSz :: OffsetFormat -> DatetimeFormat Char -> Parser OffsetDatetime
+ Chronos.OffsetDatetime.Text: parser_YmdHMSz :: OffsetFormat -> DatetimeFormat -> Parser OffsetDatetime
- Chronos.Types: DatetimeFormat :: !(Maybe a) -> !(Maybe a) -> !(Maybe a) -> DatetimeFormat a
+ Chronos.Types: DatetimeFormat :: !(Maybe Char) -> !(Maybe Char) -> !(Maybe Char) -> DatetimeFormat
- Chronos.Types: DatetimeLocale :: !(DayOfWeekMatch a) -> !(DayOfWeekMatch a) -> !(MonthMatch a) -> !(MonthMatch a) -> !a -> !a -> DatetimeLocale a
+ Chronos.Types: DatetimeLocale :: !(DayOfWeekMatch a) -> !(DayOfWeekMatch a) -> !(MonthMatch a) -> !(MonthMatch a) -> DatetimeLocale a
- Chronos.Types: [datetimeFormatDateSeparator] :: DatetimeFormat a -> !(Maybe a)
+ Chronos.Types: [datetimeFormatDateSeparator] :: DatetimeFormat -> !(Maybe Char)
- Chronos.Types: [datetimeFormatSeparator] :: DatetimeFormat a -> !(Maybe a)
+ Chronos.Types: [datetimeFormatSeparator] :: DatetimeFormat -> !(Maybe Char)
- Chronos.Types: [datetimeFormatTimeSeparator] :: DatetimeFormat a -> !(Maybe a)
+ Chronos.Types: [datetimeFormatTimeSeparator] :: DatetimeFormat -> !(Maybe Char)
- Chronos.Types: data DatetimeFormat a
+ Chronos.Types: data DatetimeFormat
Files
- chronos.cabal +5/−2
- src/Chronos/Date/ByteString/Char7.hs +47/−0
- src/Chronos/Datetime/ByteString/Char7.hs +80/−0
- src/Chronos/Datetime/Text.hs +9/−9
- src/Chronos/Day.hs +25/−3
- src/Chronos/Internal.hs +35/−5
- src/Chronos/Internal/Conversion.hs +13/−5
- src/Chronos/Internal/FastText.hs +0/−40
- src/Chronos/Internal/Format.hs +8/−0
- src/Chronos/Locale/English/Text.hs +4/−4
- src/Chronos/Nanoseconds.hs +6/−0
- src/Chronos/OffsetDatetime/ByteString/Char7.hs +169/−0
- src/Chronos/OffsetDatetime/Text.hs +3/−3
- src/Chronos/Posix.hs +2/−3
- src/Chronos/TimeOfDay/ByteString/Char7.hs +206/−0
- src/Chronos/TimeOfDay/Text.hs +2/−1
- src/Chronos/Types.hs +57/−28
- test/Spec.hs +94/−34
chronos.cabal view
@@ -1,5 +1,5 @@ name: chronos-version: 0.2.0+version: 0.3 synopsis: A time library, encoding, decoding, and instances description: Please see README.md homepage: https://github.com/andrewthad/chronos#readme@@ -22,15 +22,18 @@ , Chronos.Nanoseconds , Chronos.Day , Chronos.Date.Text+ , Chronos.Date.ByteString.Char7 , Chronos.Datetime , Chronos.Datetime.Text+ , Chronos.Datetime.ByteString.Char7 , Chronos.OffsetDatetime.Text+ , Chronos.OffsetDatetime.ByteString.Char7 , Chronos.TimeOfDay.Text+ , Chronos.TimeOfDay.ByteString.Char7 , Chronos.Internal , Chronos.Internal.Format , Chronos.Internal.Conversion , Chronos.Internal.CTimespec- , Chronos.Internal.FastText , Chronos.Tai , Chronos.Posix , Chronos.Month
+ src/Chronos/Date/ByteString/Char7.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE OverloadedStrings #-}++module Chronos.Date.ByteString.Char7 where++import Chronos.Types+import Data.ByteString (ByteString)+import Data.ByteString.Builder (Builder)+import Data.Vector (Vector)+import Data.Monoid+import Data.Attoparsec.ByteString.Char8 (Parser)+import Control.Monad+import Data.Foldable+import qualified Chronos.Internal.Format as Format+import qualified Chronos.Internal as I+import qualified Data.ByteString.Char8 as ByteString+import qualified Data.Attoparsec.ByteString.Char8 as Atto+import qualified Data.Vector as Vector+import qualified Data.ByteString.Builder as Builder++-- | This could be written much more efficiently since we know the+-- exact size the resulting 'ByteString' will be.+builder_Ymd :: Maybe Char -> Date -> Builder+builder_Ymd msep (Date (Year y) m d) = case msep of+ Nothing ->+ Builder.intDec y+ <> Format.monthToZeroPaddedDigitBS m+ <> Format.zeroPadDayOfMonthBS d+ Just sep -> let sepBuilder = Builder.char7 sep in+ Builder.intDec y+ <> sepBuilder+ <> Format.monthToZeroPaddedDigitBS m+ <> sepBuilder+ <> Format.zeroPadDayOfMonthBS d++parser_Ymd :: Maybe Char -> Parser Date+parser_Ymd msep = do+ y <- I.parseFixedDigitsIntBS 4+ traverse_ Atto.char msep+ m <- I.parseFixedDigitsIntBS 2+ when (m < 1 || m > 12) (fail "month must be between 1 and 12")+ traverse_ Atto.char msep+ d <- I.parseFixedDigitsIntBS 2+ when (d < 1 || d > 31) (fail "day must be between 1 and 31")+ return (Date (Year y) (Month $ m - 1) (DayOfMonth d))+++
+ src/Chronos/Datetime/ByteString/Char7.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE OverloadedStrings #-}++module Chronos.Datetime.ByteString.Char7 where++import Chronos.Types+import Data.ByteString (ByteString)+import Data.ByteString.Builder (Builder)+import Data.Vector (Vector)+import Data.Monoid+import Data.Attoparsec.ByteString (Parser)+import Control.Monad+import Data.Foldable+import qualified Chronos.Date.ByteString.Char7 as Date+import qualified Chronos.TimeOfDay.ByteString.Char7 as TimeOfDay+import qualified Data.ByteString.Char8 as ByteString+import qualified Data.ByteString.Lazy.Char8 as LByteString+import qualified Data.Attoparsec.ByteString.Char8 as Atto+import qualified Data.Vector as Vector+import qualified Data.ByteString.Lazy.Builder as Builder++encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> ByteString+encode_YmdHMS sp format =+ LByteString.toStrict . Builder.toLazyByteString . builder_YmdHMS sp format++encode_YmdIMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> ByteString+encode_YmdIMS_p a sp b = LByteString.toStrict . Builder.toLazyByteString . builder_YmdIMS_p a sp b++-- | This could be written much more efficiently since we know the+-- exact size the resulting 'ByteString' will be.+builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder+builder_YmdHMS sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) =+ case msep of+ Nothing -> Date.builder_Ymd mdateSep date+ <> TimeOfDay.builder_HMS sp mtimeSep time+ Just sep -> Date.builder_Ymd mdateSep date+ <> Builder.char7 sep+ <> TimeOfDay.builder_HMS sp mtimeSep time++builder_YmdIMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder+builder_YmdIMS_p locale sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) =+ Date.builder_Ymd mdateSep date+ <> maybe mempty Builder.char7 msep+ <> TimeOfDay.builder_IMS_p locale sp mtimeSep time++builder_YmdIMSp :: MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder+builder_YmdIMSp locale sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) =+ Date.builder_Ymd mdateSep date+ <> maybe mempty Builder.char7 msep+ <> TimeOfDay.builder_IMS_p locale sp mtimeSep time+++builderW3 :: Datetime -> Builder+builderW3 = builder_YmdHMS SubsecondPrecisionAuto (DatetimeFormat (Just '-') (Just 'T') (Just ':'))++decode_YmdHMS :: DatetimeFormat -> ByteString -> Maybe Datetime+decode_YmdHMS format =+ either (const Nothing) Just . Atto.parseOnly (parser_YmdHMS format)++parser_YmdHMS :: DatetimeFormat -> Parser Datetime+parser_YmdHMS (DatetimeFormat mdateSep msep mtimeSep) = do+ date <- Date.parser_Ymd mdateSep+ traverse_ Atto.char msep+ time <- TimeOfDay.parser_HMS mtimeSep+ return (Datetime date time)++parser_YmdHMS_opt_S :: DatetimeFormat -> Parser Datetime+parser_YmdHMS_opt_S (DatetimeFormat mdateSep msep mtimeSep) = do+ date <- Date.parser_Ymd mdateSep+ traverse_ Atto.char msep+ time <- TimeOfDay.parser_HMS_opt_S mtimeSep+ return (Datetime date time)++decode_YmdHMS_opt_S :: DatetimeFormat -> ByteString -> Maybe Datetime+decode_YmdHMS_opt_S format =+ either (const Nothing) Just . Atto.parseOnly (parser_YmdHMS_opt_S format)+++++
src/Chronos/Datetime/Text.hs view
@@ -20,16 +20,16 @@ import qualified Data.Text.Lazy.Builder as Builder import qualified Data.Text.Lazy.Builder.Int as Builder -encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Text+encode_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Text encode_YmdHMS sp format = LText.toStrict . Builder.toLazyText . builder_YmdHMS sp format -encode_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Text+encode_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Text encode_YmdIMS_p a sp b = LText.toStrict . Builder.toLazyText . builder_YmdIMS_p a sp b -- | This could be written much more efficiently since we know the -- exact size the resulting 'Text' will be.-builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder+builder_YmdHMS :: SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder builder_YmdHMS sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) = case msep of Nothing -> Date.builder_Ymd mdateSep date@@ -38,13 +38,13 @@ <> Builder.singleton sep <> TimeOfDay.builder_HMS sp mtimeSep time -builder_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder+builder_YmdIMS_p :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder builder_YmdIMS_p locale sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) = Date.builder_Ymd mdateSep date <> maybe mempty Builder.singleton msep <> TimeOfDay.builder_IMS_p locale sp mtimeSep time -builder_YmdIMSp :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> Datetime -> Builder+builder_YmdIMSp :: MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder builder_YmdIMSp locale sp (DatetimeFormat mdateSep msep mtimeSep) (Datetime date time) = Date.builder_Ymd mdateSep date <> maybe mempty Builder.singleton msep@@ -54,25 +54,25 @@ builderW3 :: Datetime -> Builder builderW3 = builder_YmdHMS SubsecondPrecisionAuto (DatetimeFormat (Just '-') (Just 'T') (Just ':')) -decode_YmdHMS :: DatetimeFormat Char -> Text -> Maybe Datetime+decode_YmdHMS :: DatetimeFormat -> Text -> Maybe Datetime decode_YmdHMS format = either (const Nothing) Just . Atto.parseOnly (parser_YmdHMS format) -parser_YmdHMS :: DatetimeFormat Char -> Parser Datetime+parser_YmdHMS :: DatetimeFormat -> Parser Datetime parser_YmdHMS (DatetimeFormat mdateSep msep mtimeSep) = do date <- Date.parser_Ymd mdateSep traverse_ Atto.char msep time <- TimeOfDay.parser_HMS mtimeSep return (Datetime date time) -parser_YmdHMS_opt_S :: DatetimeFormat Char -> Parser Datetime+parser_YmdHMS_opt_S :: DatetimeFormat -> Parser Datetime parser_YmdHMS_opt_S (DatetimeFormat mdateSep msep mtimeSep) = do date <- Date.parser_Ymd mdateSep traverse_ Atto.char msep time <- TimeOfDay.parser_HMS_opt_S mtimeSep return (Datetime date time) -decode_YmdHMS_opt_S :: DatetimeFormat Char -> Text -> Maybe Datetime+decode_YmdHMS_opt_S :: DatetimeFormat -> Text -> Maybe Datetime decode_YmdHMS_opt_S format = either (const Nothing) Just . Atto.parseOnly (parser_YmdHMS_opt_S format)
src/Chronos/Day.hs view
@@ -1,11 +1,33 @@-module Chronos.Day where+module Chronos.Day+ ( add+ , diff+ , today+ , tomorrow+ , yesterday+ ) where import Chronos.Types import Data.Int+import Chronos.Internal.Conversion as Conv+import qualified Chronos.Posix as Posix +-- | Offset by the given number of days. To accomplish+-- subtraction, provide a negative number. add :: Int -> Day -> Day-add a (Day b) = Day (a + b)+add = Conv.addDay +-- | Take the difference between two days. diff :: Day -> Day -> Int-diff (Day a) (Day b) = a - b+diff = Conv.diffDay++-- | Gets the current POSIX day. This does not take the user\'s+-- time zone into account.+today :: IO Day+today = fmap Posix.truncateToDay Posix.now++tomorrow :: IO Day+tomorrow = fmap (add 1 . Posix.truncateToDay) Posix.now++yesterday :: IO Day+yesterday = fmap (add (-1) . Posix.truncateToDay) Posix.now
src/Chronos/Internal.hs view
@@ -9,6 +9,9 @@ import Data.Text.Lazy.Builder (Builder) import Data.Word import Data.Int+import qualified Data.ByteString.Builder as BBuilder+import qualified Data.ByteString.Char8 as BC8+import qualified Data.Attoparsec.ByteString.Char8 as AB import qualified Data.Vector.Unboxed as UVector import qualified Data.Attoparsec.Text as Atto import qualified Data.Text.Lazy.Builder as Builder@@ -17,15 +20,26 @@ import qualified Data.Text as Text import qualified Data.Vector as Vector -parseFixedDigits :: Integral i => Int -> Parser i+parseFixedDigits :: Int -> Parser Int parseFixedDigits n = do t <- Atto.take n case Text.decimal t of Left err -> fail err Right (i,r) -> if Text.null r then return i- else fail "datetime decoding could not parse integral"+ else fail "datetime decoding could not parse integral text"+{-# INLINE parseFixedDigits #-} +parseFixedDigitsIntBS :: Int -> AB.Parser Int+parseFixedDigitsIntBS n = do+ t <- AB.take n+ case BC8.readInt t of+ Nothing -> fail "datetime decoding could not parse integral bytestring (a)"+ Just (i,r) -> if BC8.null r+ then return i+ else fail "datetime decoding could not parse integral bytestring (b)"+{-# INLINE parseFixedDigitsIntBS #-}+ raiseTenTo :: Int -> Int64 raiseTenTo i = if i > 15 then 10 ^ i@@ -35,14 +49,31 @@ tenRaisedToSmallPowers = UVector.fromList $ map (10 ^) [0 :: Int ..15] -- | Only provide positive numbers to this function.-indexTwoDigitTextBuilder :: Integral i => i -> Builder+indexTwoDigitTextBuilder :: Int -> Builder indexTwoDigitTextBuilder i = if i < 100 then Vector.unsafeIndex twoDigitTextBuilder (fromIntegral i) else Builder.decimal i {-# INLINE indexTwoDigitTextBuilder #-} +-- | Only provide positive numbers to this function.+indexTwoDigitByteStringBuilder :: Int -> BBuilder.Builder+indexTwoDigitByteStringBuilder i = if i < 100+ then Vector.unsafeIndex twoDigitByteStringBuilder (fromIntegral i)+ else BBuilder.intDec i+{-# INLINE indexTwoDigitByteStringBuilder #-}++twoDigitByteStringBuilder :: Vector BBuilder.Builder+twoDigitByteStringBuilder = Vector.fromList+ $ map (BBuilder.byteString . BC8.pack) twoDigitStrings+{-# NOINLINE twoDigitByteStringBuilder #-}+ twoDigitTextBuilder :: Vector Builder-twoDigitTextBuilder = Vector.fromList $ map Builder.fromText+twoDigitTextBuilder = Vector.fromList+ $ map (Builder.fromText . Text.pack) twoDigitStrings+{-# NOINLINE twoDigitTextBuilder #-}++twoDigitStrings :: [String]+twoDigitStrings = [ "00","01","02","03","04","05","06","07","08","09" , "10","11","12","13","14","15","16","17","18","19" , "20","21","22","23","24","25","26","27","28","29"@@ -54,7 +85,6 @@ , "80","81","82","83","84","85","86","87","88","89" , "90","91","92","93","94","95","96","97","98","99" ]-{-# NOINLINE twoDigitTextBuilder #-} countDigits :: (Integral a) => a -> Int {-# INLINE countDigits #-}
src/Chronos/Internal/Conversion.hs view
@@ -18,9 +18,9 @@ -- | The first argument in the resulting tuple in a day -- adjustment. It should be either -1, 0, or 1, as no -- offset should ever exceed 24 hours.-offsetTimeOfDay :: Offset -> TimeOfDay -> (Days, TimeOfDay)+offsetTimeOfDay :: Offset -> TimeOfDay -> (Int, TimeOfDay) offsetTimeOfDay (Offset offset) (TimeOfDay h m s) =- (Days dayAdjustment,TimeOfDay h'' m'' s)+ (dayAdjustment,TimeOfDay h'' m'' s) where (!dayAdjustment, !h'') = divMod h' 24 (!hourAdjustment, !m'') = divMod m' 60@@ -52,7 +52,7 @@ utcTimeToOffsetDatetime :: Offset -> UtcTime -> OffsetDatetime utcTimeToOffsetDatetime offset (UtcTime (Day d) nanoseconds) =- let (!(Days dayAdjustment),!tod) = offsetTimeOfDay offset (nanosecondsSinceMidnightToTimeOfDay nanoseconds)+ let (!dayAdjustment,!tod) = offsetTimeOfDay offset (nanosecondsSinceMidnightToTimeOfDay nanoseconds) !date = dayToDate (Day (d + dayAdjustment)) in OffsetDatetime (Datetime date tod) offset @@ -68,8 +68,7 @@ offsetDatetimeToUtcTime :: OffsetDatetime -> UtcTime offsetDatetimeToUtcTime (OffsetDatetime (Datetime date timeOfDay) (Offset off)) =- let (!(Days !dayAdjustment),!tod) =- offsetTimeOfDay (Offset $ negate off) timeOfDay+ let (!dayAdjustment,!tod) = offsetTimeOfDay (Offset $ negate off) timeOfDay !(Day !day) = dateToDay date in UtcTime (Day (day + dayAdjustment))@@ -213,3 +212,12 @@ , replicate 31 (Month 11) ] {-# NOINLINE normalYearDayOfYearMonthTable #-}++addDay :: Int -> Day -> Day+addDay a (Day b) = Day (a + b)+{-# INLINE addDay #-}++diffDay :: Day -> Day -> Int+diffDay (Day a) (Day b) = a - b+{-# INLINE diffDay #-}+
− src/Chronos/Internal/FastText.hs
@@ -1,40 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE RankNTypes #-}--module Chronos.Internal.FastText where--import Data.Text (Text)-import Control.Monad.ST (ST)-import qualified Data.Text.Internal as I-import qualified Data.Text as Text-import qualified Data.Text.Array as A-import qualified Data.Text.Internal.Unsafe.Char as C--data MArrayAppend s = MArrayAppend (Int -> A.MArray s -> ST s Int) !Int--combine :: MArrayAppend s -> MArrayAppend s -> MArrayAppend s-combine (MArrayAppend f maxSizeA) (MArrayAppend g maxSizeB) = do- MArrayAppend (\i marr -> f i marr >>= (\j -> g j marr)) (maxSizeA + maxSizeB)--runMArrayAppend :: (forall s. MArrayAppend s) -> Text-runMArrayAppend x =- let (!arr, !word16Used) = A.run2 $ case x of- MArrayAppend f maxSize -> do- marr <- A.new maxSize- finalSize <- f 0 marr- return (marr,finalSize)- in I.Text arr 0 word16Used--singleton :: Char -> MArrayAppend s-singleton c = MArrayAppend (\i marr -> do- C.unsafeWrite marr i c- ) 2--fromText :: Text -> MArrayAppend s-fromText (I.Text arr start len) = MArrayAppend (\i marr -> do- let j = len + i- A.copyI marr i arr start j- return j ) len---
src/Chronos/Internal/Format.hs view
@@ -8,6 +8,7 @@ import Data.Attoparsec.Text (Parser) import Control.Monad import Data.Foldable+import qualified Data.ByteString.Builder as BBuilder import qualified Chronos.Internal as I import qualified Data.Text as Text import qualified Data.Text.Read as Text@@ -22,4 +23,11 @@ zeroPadDayOfMonth :: DayOfMonth -> Builder zeroPadDayOfMonth (DayOfMonth d) = I.indexTwoDigitTextBuilder d++monthToZeroPaddedDigitBS :: Month -> BBuilder.Builder+monthToZeroPaddedDigitBS (Month x) =+ I.indexTwoDigitByteStringBuilder (x + 1)++zeroPadDayOfMonthBS :: DayOfMonth -> BBuilder.Builder+zeroPadDayOfMonthBS (DayOfMonth d) = I.indexTwoDigitByteStringBuilder d
src/Chronos/Locale/English/Text.hs view
@@ -6,16 +6,16 @@ import qualified Chronos.Month as Month import Data.Text (Text) -datetimeW3 :: DatetimeFormat Char+datetimeW3 :: DatetimeFormat datetimeW3 = DatetimeFormat (Just '-') (Just 'T') (Just ':') -datetimeSlash :: DatetimeFormat Char+datetimeSlash :: DatetimeFormat datetimeSlash = DatetimeFormat (Just '/') (Just ' ') (Just ':') -datetimeHyphen :: DatetimeFormat Char+datetimeHyphen :: DatetimeFormat datetimeHyphen = DatetimeFormat (Just '-') (Just ' ') (Just ':') -datetimeCompact :: DatetimeFormat Char+datetimeCompact :: DatetimeFormat datetimeCompact = DatetimeFormat Nothing (Just 'T') Nothing meridiemLower :: MeridiemLocale Text
src/Chronos/Nanoseconds.hs view
@@ -1,7 +1,13 @@+{-| Functions that operate on 'Nanoseconds'. All of these functions have+ trivial implementations and are provided for convenience. This module+ is intended to be imported qualified.+-}+ module Chronos.Nanoseconds ( -- * Arithmetic add , scale+ , negate -- * From Duration -- $fromduration , fromSeconds
+ src/Chronos/OffsetDatetime/ByteString/Char7.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BangPatterns #-}++-- | The naming conventions for offsets that are used in+-- function names are as follows:+--+-- * @%z@ - @z@ +hhmm numeric time zone (e.g., -0400)+-- * @%:z@ - @z1@ +hh:mm numeric time zone (e.g., -04:00)+-- * @%::z@ - @z2@ +hh:mm:ss numeric time zone (e.g., -04:00:00)+-- * @%:::z@ - @z3@ numeric time zone with : to necessary precision (e.g., -04, +05:30)++module Chronos.OffsetDatetime.ByteString.Char7 where++import Chronos.Types+import Data.ByteString (ByteString)+import Data.ByteString.Builder (Builder)+import Data.Vector (Vector)+import Data.Monoid+import Data.Attoparsec.ByteString (Parser)+import Control.Monad+import Data.Foldable+import Data.Int+import qualified Chronos.Internal as I+import qualified Chronos.Datetime.ByteString.Char7 as Datetime+import qualified Chronos.TimeOfDay.ByteString.Char7 as TimeOfDay+import qualified Data.ByteString.Char8 as ByteString+import qualified Data.Attoparsec.ByteString.Char8 as Atto+import qualified Data.Vector as Vector+import qualified Data.ByteString.Builder as Builder++builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder+builder_YmdHMSz offsetFormat sp datetimeFormat (OffsetDatetime datetime offset) =+ Datetime.builder_YmdHMS sp datetimeFormat datetime+ <> offsetBuilder offsetFormat offset++parser_YmdHMSz :: OffsetFormat -> DatetimeFormat -> Parser OffsetDatetime+parser_YmdHMSz offsetFormat datetimeFormat = OffsetDatetime+ <$> Datetime.parser_YmdHMS datetimeFormat+ <*> offsetParser offsetFormat++builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale ByteString -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder+builder_YmdIMS_p_z offsetFormat meridiemLocale sp datetimeFormat (OffsetDatetime datetime offset) =+ Datetime.builder_YmdIMS_p meridiemLocale sp datetimeFormat datetime+ <> " "+ <> offsetBuilder offsetFormat offset++builderW3 :: OffsetDatetime -> Builder+builderW3 = builder_YmdHMSz+ OffsetFormatColonOn+ SubsecondPrecisionAuto+ (DatetimeFormat (Just '-') (Just 'T') (Just ':'))++offsetBuilder :: OffsetFormat -> Offset -> Builder+offsetBuilder x = case x of+ OffsetFormatColonOff -> buildOffset_z+ OffsetFormatColonOn -> buildOffset_z1+ OffsetFormatSecondsPrecision -> buildOffset_z2+ OffsetFormatColonAuto -> buildOffset_z3++offsetParser :: OffsetFormat -> Parser Offset+offsetParser x = case x of+ OffsetFormatColonOff -> parseOffset_z+ OffsetFormatColonOn -> parseOffset_z1+ OffsetFormatSecondsPrecision -> parseOffset_z2+ OffsetFormatColonAuto -> parseOffset_z3++-- | True means positive, false means negative+parseSignedness :: Parser Bool+parseSignedness = do+ c <- Atto.anyChar+ if c == '-'+ then return False+ else if c == '+'+ then return True+ else fail "while parsing offset, expected [+] or [-]"+{-# INLINE parseSignedness #-}++parseOffset_z :: Parser Offset+parseOffset_z = do+ pos <- parseSignedness+ h <- I.parseFixedDigitsIntBS 2+ m <- I.parseFixedDigitsIntBS 2+ let !res = h * 60 + m+ return . Offset $ if pos+ then res+ else negate res++parseOffset_z1 :: Parser Offset+parseOffset_z1 = do+ pos <- parseSignedness+ h <- I.parseFixedDigitsIntBS 2+ _ <- Atto.char ':'+ m <- I.parseFixedDigitsIntBS 2+ let !res = h * 60 + m+ return . Offset $ if pos+ then res+ else negate res++parseOffset_z2 :: Parser Offset+parseOffset_z2 = do+ pos <- parseSignedness+ h <- I.parseFixedDigitsIntBS 2+ _ <- Atto.char ':'+ m <- I.parseFixedDigitsIntBS 2+ _ <- Atto.string ":00"+ let !res = h * 60 + m+ return . Offset $ if pos+ then res+ else negate res++-- | This is generous in what it accepts. If you give+-- something like +04:00 as the offset, it will be+-- allowed, even though it could be shorter.+parseOffset_z3 :: Parser Offset+parseOffset_z3 = do+ pos <- parseSignedness+ h <- I.parseFixedDigitsIntBS 2+ mc <- Atto.peekChar+ case mc of+ Just ':' -> do+ _ <- Atto.anyChar -- should be a colon+ m <- I.parseFixedDigitsIntBS 2+ let !res = h * 60 + m+ return . Offset $ if pos+ then res+ else negate res+ _ -> return . Offset $ if pos+ then h * 60+ else h * (-60)++buildOffset_z :: Offset -> Builder+buildOffset_z (Offset i) =+ let (!a,!b) = divMod (abs i) 60+ !prefix = if signum i == (-1) then "-" else "+"+ in prefix+ <> I.indexTwoDigitByteStringBuilder a+ <> I.indexTwoDigitByteStringBuilder b++buildOffset_z1 :: Offset -> Builder+buildOffset_z1 (Offset i) =+ let (!a,!b) = divMod (abs i) 60+ !prefix = if signum i == (-1) then "-" else "+"+ in prefix+ <> I.indexTwoDigitByteStringBuilder a+ <> ":"+ <> I.indexTwoDigitByteStringBuilder b++buildOffset_z2 :: Offset -> Builder+buildOffset_z2 (Offset i) =+ let (!a,!b) = divMod (abs i) 60+ !prefix = if signum i == (-1) then "-" else "+"+ in prefix+ <> I.indexTwoDigitByteStringBuilder a+ <> ":"+ <> I.indexTwoDigitByteStringBuilder b+ <> ":00"++buildOffset_z3 :: Offset -> Builder+buildOffset_z3 (Offset i) =+ let (!a,!b) = divMod (abs i) 60+ !prefix = if signum i == (-1) then "-" else "+"+ in if b == 0+ then prefix+ <> I.indexTwoDigitByteStringBuilder a+ else prefix+ <> I.indexTwoDigitByteStringBuilder a+ <> ":"+ <> I.indexTwoDigitByteStringBuilder b+
src/Chronos/OffsetDatetime/Text.hs view
@@ -30,17 +30,17 @@ import qualified Data.Text.Lazy.Builder as Builder import qualified Data.Text.Lazy.Builder.Int as Builder -builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat Char -> OffsetDatetime -> Builder+builder_YmdHMSz :: OffsetFormat -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder builder_YmdHMSz offsetFormat sp datetimeFormat (OffsetDatetime datetime offset) = Datetime.builder_YmdHMS sp datetimeFormat datetime <> offsetBuilder offsetFormat offset -parser_YmdHMSz :: OffsetFormat -> DatetimeFormat Char -> Parser OffsetDatetime+parser_YmdHMSz :: OffsetFormat -> DatetimeFormat -> Parser OffsetDatetime parser_YmdHMSz offsetFormat datetimeFormat = OffsetDatetime <$> Datetime.parser_YmdHMS datetimeFormat <*> offsetParser offsetFormat -builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat Char -> OffsetDatetime -> Builder+builder_YmdIMS_p_z :: OffsetFormat -> MeridiemLocale Text -> SubsecondPrecision -> DatetimeFormat -> OffsetDatetime -> Builder builder_YmdIMS_p_z offsetFormat meridiemLocale sp datetimeFormat (OffsetDatetime datetime offset) = Datetime.builder_YmdIMS_p meridiemLocale sp datetimeFormat datetime <> " "
src/Chronos/Posix.hs view
@@ -22,7 +22,6 @@ import Foreign.C.Types (CLong(..),CTime(..)) import Data.Word import Data.Int-import qualified Chronos.Day as Day import qualified Chronos.Nanoseconds as Nanoseconds epoch :: PosixTime@@ -49,11 +48,11 @@ -- first modified julian day. toUtc :: PosixTime -> UtcTime toUtc (PosixTime i) = let (d,t) = divMod i (getNanoseconds dayLength)- in UtcTime (Day.add (fromIntegral d) epochDay) (fromIntegral t)+ in UtcTime (Conv.addDay (fromIntegral d) epochDay) (fromIntegral t) fromUtc :: UtcTime -> PosixTime fromUtc (UtcTime d ns') = PosixTime $ getNanoseconds $ Nanoseconds.add- (Nanoseconds.scale (fromIntegral (Day.diff d epochDay)) dayLength)+ (Nanoseconds.scale (fromIntegral (Conv.diffDay d epochDay)) dayLength) (if ns > dayLength then dayLength else ns) where ns = Nanoseconds (fromIntegral ns')
+ src/Chronos/TimeOfDay/ByteString/Char7.hs view
@@ -0,0 +1,206 @@+{-| Functions for encoding 'TimeOfDay' to 'ByteString'. Any encoding+ that is a superset of ASCII is compatible with the functions in+ this module. This includes UTF-8 and ISO-8859-15 but does not+ include UTF-16 or UTF-32.+-}++{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BangPatterns #-}++module Chronos.TimeOfDay.ByteString.Char7 where++import Chronos.Types+import Data.ByteString (ByteString)+import Data.ByteString.Builder (Builder)+import Data.Vector (Vector)+import Data.Monoid+import Data.Attoparsec.ByteString.Char8 (Parser)+import Control.Monad+import Control.Applicative+import Data.Foldable+import Data.Word+import Data.Int+import Data.Char (isDigit)+import qualified Data.ByteString.Char8 as ByteString+import qualified Chronos.Internal as I+import qualified Data.Text as Text+import qualified Data.Text.Read as Text+import qualified Data.Attoparsec.ByteString.Char8 as Atto+import qualified Data.Vector as Vector+import qualified Data.ByteString.Builder as Builder++-- | This could be written much more efficiently since we know the+-- exact size the resulting 'Text' will be.+builder_HMS :: SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder+builder_HMS sp msep (TimeOfDay h m ns) =+ I.indexTwoDigitByteStringBuilder h+ <> internalBuilder_NS sp msep m ns++builder_IMS_p :: MeridiemLocale ByteString -> SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder+builder_IMS_p meridiemLocale sp msep (TimeOfDay h m ns) =+ internalBuilder_I h+ <> internalBuilder_NS sp msep h ns+ <> " "+ <> internalBuilder_p meridiemLocale h++internalBuilder_I :: Int -> Builder+internalBuilder_I h =+ I.indexTwoDigitByteStringBuilder $ if h > 12+ then h - 12+ else if h == 0+ then 12+ else h++internalBuilder_p :: MeridiemLocale ByteString -> Int -> Builder+internalBuilder_p (MeridiemLocale am pm) h = if h > 11+ then Builder.byteString pm+ else Builder.byteString am++builder_IMSp :: MeridiemLocale ByteString -> SubsecondPrecision -> Maybe Char -> TimeOfDay -> Builder+builder_IMSp meridiemLocale sp msep (TimeOfDay h m ns) =+ internalBuilder_I h+ <> internalBuilder_NS sp msep h ns+ <> internalBuilder_p meridiemLocale h++parser_HMS :: Maybe Char -> Parser TimeOfDay+parser_HMS msep = do+ h <- I.parseFixedDigitsIntBS 2+ when (h > 23) (fail "hour must be between 0 and 23")+ traverse_ Atto.char msep+ m <- I.parseFixedDigitsIntBS 2+ when (m > 59) (fail "minute must be between 0 and 59")+ traverse_ Atto.char msep+ ns <- parseSecondsAndNanoseconds+ return (TimeOfDay h m ns)++-- | Parses text that is formatted as either of the following:+--+-- * @%H:%M@+-- * @%H:%M:%S@+--+-- That is, the seconds and subseconds part is optional. If it is+-- not provided, it is assumed to be zero. This format shows up+-- in Google Chrome\'s @datetime-local@ inputs.+parser_HMS_opt_S :: Maybe Char -> Parser TimeOfDay+parser_HMS_opt_S msep = do+ h <- I.parseFixedDigitsIntBS 2+ when (h > 23) (fail "hour must be between 0 and 23")+ traverse_ Atto.char msep+ m <- I.parseFixedDigitsIntBS 2+ when (m > 59) (fail "minute must be between 0 and 59")+ mc <- Atto.peekChar+ case mc of+ Nothing -> return (TimeOfDay h m 0)+ Just c -> case msep of+ Just sep -> if c == sep+ then do+ _ <- Atto.anyChar -- should be the separator+ ns <- parseSecondsAndNanoseconds+ return (TimeOfDay h m ns)+ else return (TimeOfDay h m 0)+ -- if there is no separator, we will try to parse the+ -- remaining part as seconds. We commit to trying to+ -- parse as seconds if we see any number as the next+ -- character.+ Nothing -> if isDigit c+ then do+ ns <- parseSecondsAndNanoseconds+ return (TimeOfDay h m ns)+ else return (TimeOfDay h m 0)++parseSecondsAndNanoseconds :: Parser Int64+parseSecondsAndNanoseconds = do+ s' <- I.parseFixedDigitsIntBS 2+ let !s = fromIntegral s' :: Int64+ when (s > 60) (fail "seconds must be between 0 and 60")+ let nanoseconds = 1+ nanoseconds <-+ ( do _ <- Atto.char '.'+ numberOfZeroes <- countZeroes+ x <- Atto.decimal+ let totalDigits = I.countDigits x + numberOfZeroes+ result = if totalDigits == 9+ then x+ else if totalDigits < 9+ then x * I.raiseTenTo (9 - totalDigits)+ else quot x (I.raiseTenTo (totalDigits - 9))+ return (fromIntegral result)+ ) <|> return 0+ return (s * 1000000000 + nanoseconds)++countZeroes :: Parser Int+countZeroes = go 0 where+ go !i = do+ m <- Atto.peekChar+ case m of+ Nothing -> return i+ Just c -> if c == '0'+ then Atto.anyChar *> go (i + 1)+ else return i++nanosecondsBuilder :: Int64 -> Builder+nanosecondsBuilder w+ | w == 0 = mempty+ | w > 99999999 = "." <> int64Builder w+ | w > 9999999 = ".0" <> int64Builder w+ | w > 999999 = ".00" <> int64Builder w+ | w > 99999 = ".000" <> int64Builder w+ | w > 9999 = ".0000" <> int64Builder w+ | w > 999 = ".00000" <> int64Builder w+ | w > 99 = ".000000" <> int64Builder w+ | w > 9 = ".0000000" <> int64Builder w+ | otherwise = ".00000000" <> int64Builder w++microsecondsBuilder :: Int64 -> Builder+microsecondsBuilder w+ | w == 0 = mempty+ | w > 99999 = "." <> int64Builder w+ | w > 9999 = ".0" <> int64Builder w+ | w > 999 = ".00" <> int64Builder w+ | w > 99 = ".000" <> int64Builder w+ | w > 9 = ".0000" <> int64Builder w+ | otherwise = ".00000" <> int64Builder w++millisecondsBuilder :: Int64 -> Builder+millisecondsBuilder w+ | w == 0 = mempty+ | w > 99 = "." <> int64Builder w+ | w > 9 = ".0" <> int64Builder w+ | otherwise = ".00" <> int64Builder w++prettyNanosecondsBuilder :: SubsecondPrecision -> Int64 -> Builder+prettyNanosecondsBuilder sp nano = case sp of+ SubsecondPrecisionAuto+ | milliRem == 0 -> millisecondsBuilder milli+ | microRem == 0 -> microsecondsBuilder micro+ | otherwise -> nanosecondsBuilder nano+ SubsecondPrecisionFixed d -> if d == 0+ then mempty+ else+ let newSubsecondPart = quot nano (I.raiseTenTo (9 - d))+ in "."+ <> Builder.byteString (ByteString.replicate (d - I.countDigits newSubsecondPart) '0')+ <> int64Builder newSubsecondPart+ where+ (milli,milliRem) = quotRem nano 1000000+ (micro,microRem) = quotRem nano 1000++int64Builder :: Int64 -> Builder+int64Builder = Builder.integerDec . fromIntegral++internalBuilder_NS :: SubsecondPrecision -> Maybe Char -> Int -> Int64 -> Builder+internalBuilder_NS sp msep m ns = case msep of+ Nothing -> I.indexTwoDigitByteStringBuilder m+ <> I.indexTwoDigitByteStringBuilder s+ <> prettyNanosecondsBuilder sp nsRemainder+ Just sep -> let sepBuilder = Builder.char7 sep in+ sepBuilder+ <> I.indexTwoDigitByteStringBuilder m+ <> sepBuilder+ <> I.indexTwoDigitByteStringBuilder s+ <> prettyNanosecondsBuilder sp nsRemainder+ where+ (!sInt64,!nsRemainder) = quotRem ns 1000000000+ !s = fromIntegral sInt64++
src/Chronos/TimeOfDay/Text.hs view
@@ -104,7 +104,8 @@ parseSecondsAndNanoseconds :: Parser Int64 parseSecondsAndNanoseconds = do- s <- I.parseFixedDigits 2+ s' <- I.parseFixedDigits 2+ let s = fromIntegral s' :: Int64 when (s > 60) (fail "seconds must be between 0 and 60") nanoseconds <- ( do _ <- Atto.char '.'
src/Chronos/Types.hs view
@@ -4,6 +4,8 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE DeriveGeneric #-} +{-# OPTIONS_GHC -Wall -Werror #-}+ {- | Data types for representing different date and time-related information. @@ -33,10 +35,35 @@ -} -module Chronos.Types where+module Chronos.Types+ ( Day(..)+ , DayOfWeek(..)+ , DayOfMonth(..)+ , DayOfYear(..)+ , Month(..)+ , Year(..)+ , Offset(..)+ , TaiTime(..)+ , PosixTime(..)+ , UtcTime(..)+ , DayOfWeekMatch(..)+ , MonthMatch(..)+ , UnboxedMonthMatch(..)+ , Nanoseconds(..)+ , SubsecondPrecision(..)+ , Date(..)+ , OrdinalDate(..)+ , MonthDate(..)+ , Datetime(..)+ , OffsetDatetime(..)+ , TimeOfDay(..)+ , DatetimeFormat(..)+ , OffsetFormat(..)+ , DatetimeLocale(..)+ , MeridiemLocale(..)+ ) where import Data.Int-import Data.Word import Data.Vector (Vector) import Data.Aeson (FromJSON,ToJSON) import Data.Hashable (Hashable)@@ -47,35 +74,34 @@ import qualified Data.Vector.Unboxed as UVector import qualified Data.Vector.Primitive as PVector import qualified Data.Vector.Generic.Mutable as MGVector-import qualified Data.Vector.Unboxed.Mutable as MUVector-import qualified Data.Vector.Primitive.Mutable as MPVector +-- | 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)---- | A duration of days-newtype Days = Days { getDays :: Int }- deriving (Show,Read,Eq,Ord,Hashable)+ deriving (Show,Read,Eq,Ord,Hashable,Enum) +-- | The day of the week. newtype DayOfWeek = DayOfWeek { getDayOfWeek :: Int } deriving (Show,Read,Eq,Ord,Hashable) +-- | The day of the month. newtype DayOfMonth = DayOfMonth { getDayOfMonth :: Int } deriving (Show,Read,Eq,Ord,Prim,Enum) +-- | The day of the year. newtype DayOfYear = DayOfYear { getDayOfYear :: Int } deriving (Show,Read,Eq,Ord,Prim) +-- | The month of the year. newtype Month = Month { getMonth :: Int } deriving (Show,Read,Eq,Ord,Prim) -newtype Months = Months { getMonths :: Int }- deriving (Show,Read,Eq,Ord)- instance Bounded Month where minBound = Month 0 maxBound = Month 11 +-- | The number of years elapsed since the beginning+-- of the Common Era. newtype Year = Year { getYear :: Int } deriving (Show,Read,Eq,Ord) @@ -93,18 +119,17 @@ newtype PosixTime = PosixTime { getPosixTime :: Int64 } deriving (FromJSON,ToJSON,Hashable,Eq,Ord,Show,Read) --- newtype Day = Day { getDay :: Word8 }--- newtype Week = Week { getWeek :: Word8 }- newtype DayOfWeekMatch a = DayOfWeekMatch { getDayOfWeekMatch :: Vector a } newtype MonthMatch a = MonthMatch { getMonthMatch :: Vector a } newtype UnboxedMonthMatch a = UnboxedMonthMatch { getUnboxedMonthMatch :: UVector.Vector a } +-- | A nanosecond duration. newtype Nanoseconds = Nanoseconds { getNanoseconds :: Int64 }- deriving (Show,Read,Eq,Ord)+ deriving (Show,Read,Eq,Ord,ToJSON,FromJSON) +-- | The precision used when encoding seconds to a human-readable format. data SubsecondPrecision = SubsecondPrecisionAuto -- ^ Rounds to second, millisecond, microsecond, or nanosecond | SubsecondPrecisionFixed {-# UNPACK #-} !Int -- ^ Specify number of places after decimal@@ -116,14 +141,17 @@ , dateDay :: {-# UNPACK #-} !DayOfMonth } deriving (Show,Read,Eq,Ord) +-- | The year and number of days elapsed since the beginning it began. data OrdinalDate = OrdinalDate- { ordinalDateYear :: {-# UNPACK #-} !Year- , ordinalDateMonth :: {-# UNPACK #-} !DayOfYear+ { ordinalDateYear :: {-# UNPACK #-} !Year+ , ordinalDateDayOfYear :: {-# UNPACK #-} !DayOfYear } deriving (Show,Read,Eq,Ord) +-- | A month and the day of the month. This does not actually represent+-- a specific date, since this recurs every year. data MonthDate = MonthDate { monthDateMonth :: {-# UNPACK #-} !Month- , monthDateDay :: {-# UNPACK #-} !DayOfMonth+ , monthDateDay :: {-# UNPACK #-} !DayOfMonth } deriving (Show,Read,Eq,Ord) -- | A date as represented by the Gregorian calendar@@ -138,7 +166,7 @@ , offsetDatetimeOffset :: {-# UNPACK #-} !Offset } deriving (Show,Read,Eq,Ord) --- | A time of day, including the possibility of leap seconds.+-- | A time of day with nanosecond resolution. data TimeOfDay = TimeOfDay { timeOfDayHour :: {-# UNPACK #-} !Int , timeOfDayMinute :: {-# UNPACK #-} !Int@@ -150,15 +178,16 @@ , utcTimeNanoseconds :: {-# UNPACK #-} !Int64 } deriving (Show,Read,Eq,Ord) -data DatetimeFormat a = DatetimeFormat- { datetimeFormatDateSeparator :: !(Maybe a)+data DatetimeFormat = DatetimeFormat+ { datetimeFormatDateSeparator :: !(Maybe Char) -- ^ Separator in the date- , datetimeFormatSeparator :: !(Maybe a)+ , datetimeFormatSeparator :: !(Maybe Char) -- ^ Separator between date and time- , datetimeFormatTimeSeparator :: !(Maybe a)+ , datetimeFormatTimeSeparator :: !(Maybe Char) -- ^ Separator in the time } deriving (Show,Read,Eq,Ord) +-- | Formatting settings for a timezone offset. data OffsetFormat = OffsetFormatColonOff -- ^ @%z@ (e.g., -0400) | OffsetFormatColonOn -- ^ @%:z@ (e.g., -04:00)@@ -166,6 +195,9 @@ | OffsetFormatColonAuto -- ^ @%:::z@ (e.g., -04, +05:30) deriving (Show,Read,Eq,Ord,Enum,Bounded,Generic) +-- | Locale-specific formatting for weekdays and months. The+-- type variable will likely be instantiated to @Text@+-- or @ByteString@. data DatetimeLocale a = DatetimeLocale { datetimeLocaleDaysOfWeekFull :: !(DayOfWeekMatch a) -- ^ full weekdays starting with Sunday, 7 elements@@ -175,12 +207,9 @@ -- ^ full months starting with January, 12 elements , datetimeLocaleMonthsAbbreviated :: !(MonthMatch a) -- ^ abbreviated months starting with January, 12 elements- , datetimeLocaleAm :: !a- -- ^ Symbol for AM- , datetimeLocalePm :: !a- -- ^ Symbol for PM } +-- | Locale-specific formatting for AM and PM. data MeridiemLocale a = MeridiemLocale { meridiemLocaleAm :: !a , meridiemLocalePm :: !a
test/Spec.hs view
@@ -16,6 +16,7 @@ import Test.HUnit (Assertion,(@?=),assertBool) import Data.Text (Text)+import Data.ByteString (ByteString) import Data.Text.Lazy.Builder (Builder) import qualified Chronos.Internal.Conversion as Conv import qualified Chronos.Calendar as Month@@ -23,8 +24,12 @@ import qualified Data.Text as Text import qualified Data.Text.Lazy as LText import qualified Data.Text.Lazy.Builder as Builder+import qualified Data.ByteString.Builder as BBuilder+import qualified Data.ByteString.Lazy as LByteString import qualified Data.Attoparsec.Text as Atto+import qualified Data.Attoparsec.ByteString as AttoBS import qualified Chronos.TimeOfDay.Text as TimeOfDayText+import qualified Chronos.TimeOfDay.ByteString.Char7 as TimeOfDayByteString import qualified Chronos.Datetime.Text as DatetimeText import qualified Chronos.OffsetDatetime.Text as OffsetDatetimeText import qualified Chronos.Date.Text as DateText@@ -46,41 +51,80 @@ tests :: [Test] tests = [ testGroup "Time of Day"- [ testGroup "Parser Spec Tests" $- [ testCase "No Separator + microseconds"- (timeOfDayParse Nothing "165956.246052" (TimeOfDay 16 59 56246052000))- , testCase "Separator + microseconds"- (timeOfDayParse (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))- , testCase "Separator + milliseconds"- (timeOfDayParse (Just ':') "05:00:58.675" (TimeOfDay 05 00 58675000000))- , testCase "Separator + deciseconds"- (timeOfDayParse (Just ':') "05:00:58.9" (TimeOfDay 05 00 58900000000))- , testCase "Separator + no subseconds"- (timeOfDayParse (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))- , testCase "Separator + nanoseconds"- (timeOfDayParse (Just ':') "05:00:58.111222999" (TimeOfDay 05 00 58111222999))- , testCase "Separator + 10e-18 seconds (truncate)"- (timeOfDayParse (Just ':') "05:00:58.111222333444555666" (TimeOfDay 05 00 58111222333))- , testCase "Separator + opt seconds (absent)"- (parseMatch (TimeOfDayText.parser_HMS_opt_S (Just ':')) "00:01" (TimeOfDay 0 1 0))- , testCase "Separator + opt seconds (present)"- (parseMatch (TimeOfDayText.parser_HMS_opt_S (Just ':')) "00:01:05" (TimeOfDay 0 1 5000000000))- , testCase "No Separator + opt seconds (absent)"- (parseMatch (TimeOfDayText.parser_HMS_opt_S Nothing) "0001" (TimeOfDay 0 1 0))- , testCase "No Separator + opt seconds (present)"- (parseMatch (TimeOfDayText.parser_HMS_opt_S Nothing) "000105" (TimeOfDay 0 1 5000000000))+ [ testGroup "Text"+ [ testGroup "Text Parsing Spec Tests"+ [ testCase "No Separator + microseconds"+ (timeOfDayParse Nothing "165956.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + microseconds"+ (timeOfDayParse (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + milliseconds"+ (timeOfDayParse (Just ':') "05:00:58.675" (TimeOfDay 05 00 58675000000))+ , testCase "Separator + deciseconds"+ (timeOfDayParse (Just ':') "05:00:58.9" (TimeOfDay 05 00 58900000000))+ , testCase "Separator + no subseconds"+ (timeOfDayParse (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))+ , testCase "Separator + nanoseconds"+ (timeOfDayParse (Just ':') "05:00:58.111222999" (TimeOfDay 05 00 58111222999))+ , testCase "Separator + 10e-18 seconds (truncate)"+ (timeOfDayParse (Just ':') "05:00:58.111222333444555666" (TimeOfDay 05 00 58111222333))+ , testCase "Separator + opt seconds (absent)"+ (parseMatch (TimeOfDayText.parser_HMS_opt_S (Just ':')) "00:01" (TimeOfDay 0 1 0))+ , testCase "Separator + opt seconds (present)"+ (parseMatch (TimeOfDayText.parser_HMS_opt_S (Just ':')) "00:01:05" (TimeOfDay 0 1 5000000000))+ , testCase "No Separator + opt seconds (absent)"+ (parseMatch (TimeOfDayText.parser_HMS_opt_S Nothing) "0001" (TimeOfDay 0 1 0))+ , testCase "No Separator + opt seconds (present)"+ (parseMatch (TimeOfDayText.parser_HMS_opt_S Nothing) "000105" (TimeOfDay 0 1 5000000000))+ ]+ , testGroup "Text Builder Spec Tests"+ [ testCase "No Separator + microseconds"+ (timeOfDayBuilder (SubsecondPrecisionFixed 6) Nothing "165956.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + microseconds"+ (timeOfDayBuilder (SubsecondPrecisionFixed 6) (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + no subseconds"+ (timeOfDayBuilder (SubsecondPrecisionFixed 0) (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))+ ]+ , testProperty "Text Builder Parser Isomorphism (H:M:S)" $ propEncodeDecodeIso+ (LText.toStrict . Builder.toLazyText . TimeOfDayText.builder_HMS (SubsecondPrecisionFixed 9) (Just ':'))+ (either (const Nothing) Just . Atto.parseOnly (TimeOfDayText.parser_HMS (Just ':'))) ]- , testGroup "Builder Spec Tests"- [ testCase "No Separator + microseconds"- (timeOfDayBuilder (SubsecondPrecisionFixed 6) Nothing "165956.246052" (TimeOfDay 16 59 56246052000))- , testCase "Separator + microseconds"- (timeOfDayBuilder (SubsecondPrecisionFixed 6) (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))- , testCase "Separator + no subseconds"- (timeOfDayBuilder (SubsecondPrecisionFixed 0) (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))+ , testGroup "ByteString"+ [ testGroup "Parser Spec Tests"+ [ testCase "No Separator + microseconds"+ (bsTimeOfDayParse Nothing "165956.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + microseconds"+ (bsTimeOfDayParse (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + milliseconds"+ (bsTimeOfDayParse (Just ':') "05:00:58.675" (TimeOfDay 05 00 58675000000))+ , testCase "Separator + deciseconds"+ (bsTimeOfDayParse (Just ':') "05:00:58.9" (TimeOfDay 05 00 58900000000))+ , testCase "Separator + no subseconds"+ (bsTimeOfDayParse (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))+ , testCase "Separator + nanoseconds"+ (bsTimeOfDayParse (Just ':') "05:00:58.111222999" (TimeOfDay 05 00 58111222999))+ , testCase "Separator + 10e-18 seconds (truncate)"+ (bsTimeOfDayParse (Just ':') "05:00:58.111222333444555666" (TimeOfDay 05 00 58111222333))+ , testCase "Separator + opt seconds (absent)"+ (bsParseMatch (TimeOfDayByteString.parser_HMS_opt_S (Just ':')) "00:01" (TimeOfDay 0 1 0))+ , testCase "Separator + opt seconds (present)"+ (bsParseMatch (TimeOfDayByteString.parser_HMS_opt_S (Just ':')) "00:01:05" (TimeOfDay 0 1 5000000000))+ , testCase "No Separator + opt seconds (absent)"+ (bsParseMatch (TimeOfDayByteString.parser_HMS_opt_S Nothing) "0001" (TimeOfDay 0 1 0))+ , testCase "No Separator + opt seconds (present)"+ (bsParseMatch (TimeOfDayByteString.parser_HMS_opt_S Nothing) "000105" (TimeOfDay 0 1 5000000000))+ ]+ , testGroup "Builder Spec Tests"+ [ testCase "No Separator + microseconds"+ (bsTimeOfDayBuilder (SubsecondPrecisionFixed 6) Nothing "165956.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + microseconds"+ (bsTimeOfDayBuilder (SubsecondPrecisionFixed 6) (Just ':') "16:59:56.246052" (TimeOfDay 16 59 56246052000))+ , testCase "Separator + no subseconds"+ (bsTimeOfDayBuilder (SubsecondPrecisionFixed 0) (Just ':') "23:08:01" (TimeOfDay 23 8 1000000000))+ ]+ , testProperty "Builder Parser Isomorphism (H:M:S)" $ propEncodeDecodeIso+ (LByteString.toStrict . BBuilder.toLazyByteString . TimeOfDayByteString.builder_HMS (SubsecondPrecisionFixed 9) (Just ':'))+ (either (const Nothing) Just . AttoBS.parseOnly (TimeOfDayByteString.parser_HMS (Just ':'))) ]- , testProperty "Builder Parser Isomorphism (H:M:S)" $ propEncodeDecodeIso- (LText.toStrict . Builder.toLazyText . TimeOfDayText.builder_HMS (SubsecondPrecisionFixed 9) (Just ':'))- (either (const Nothing) Just . Atto.parseOnly (TimeOfDayText.parser_HMS (Just ':'))) ] , testGroup "Date" [ testGroup "Parser Spec Tests" $@@ -198,16 +242,32 @@ Atto.parseOnly (p <* Atto.endOfInput) t @?= Right expected +bsParseMatch :: (Eq a, Show a) => AttoBS.Parser a -> ByteString -> a -> Assertion+bsParseMatch p t expected = do+ AttoBS.parseOnly (p <* AttoBS.endOfInput) t+ @?= Right expected+ timeOfDayParse :: Maybe Char -> Text -> TimeOfDay -> Assertion timeOfDayParse m t expected = Atto.parseOnly (TimeOfDayText.parser_HMS m <* Atto.endOfInput) t @?= Right expected +bsTimeOfDayParse :: Maybe Char -> ByteString -> TimeOfDay -> Assertion+bsTimeOfDayParse m t expected =+ AttoBS.parseOnly (TimeOfDayByteString.parser_HMS m <* AttoBS.endOfInput) t+ @?= Right expected++ timeOfDayBuilder :: SubsecondPrecision -> Maybe Char -> Text -> TimeOfDay -> Assertion timeOfDayBuilder sp m expected tod = LText.toStrict (Builder.toLazyText (TimeOfDayText.builder_HMS sp m tod)) @?= expected +bsTimeOfDayBuilder :: SubsecondPrecision -> Maybe Char -> Text -> TimeOfDay -> Assertion+bsTimeOfDayBuilder sp m expected tod =+ LText.toStrict (Builder.toLazyText (TimeOfDayText.builder_HMS sp m tod))+ @?= expected+ dateParse :: Maybe Char -> Text -> Date -> Assertion dateParse m t expected = Atto.parseOnly (DateText.parser_Ymd m <* Atto.endOfInput) t@@ -247,7 +307,7 @@ <$> arbitrary <*> arbitrary -instance (Arbitrary a, a ~ Char) => Arbitrary (DatetimeFormat a) where+instance Arbitrary DatetimeFormat where arbitrary = DatetimeFormat <$> arbitrary <*> elements [Nothing, Just '/', Just ':', Just '-']