postgresql-simple-interval 0.2025.8.27 → 0.2025.9.5
raw patch · 4 files changed
+113/−3 lines, 4 filesdep +timePVP ok
version bump matches the API change (PVP)
Dependencies added: time
API changes (from Hackage documentation)
+ Database.PostgreSQL.Simple.Interval: fromTime :: CalendarDiffDays -> NominalDiffTime -> Maybe Interval
+ Database.PostgreSQL.Simple.Interval: fromTimeSaturating :: CalendarDiffDays -> NominalDiffTime -> Interval
+ Database.PostgreSQL.Simple.Interval: intoTime :: Interval -> (CalendarDiffDays, NominalDiffTime)
+ Database.PostgreSQL.Simple.Interval.Unstable: fromMicro :: Micro -> Integer
+ Database.PostgreSQL.Simple.Interval.Unstable: fromTime :: CalendarDiffDays -> NominalDiffTime -> Maybe Interval
+ Database.PostgreSQL.Simple.Interval.Unstable: fromTimeSaturating :: CalendarDiffDays -> NominalDiffTime -> Interval
+ Database.PostgreSQL.Simple.Interval.Unstable: intoMicro :: Integer -> Micro
+ Database.PostgreSQL.Simple.Interval.Unstable: intoTime :: Interval -> (CalendarDiffDays, NominalDiffTime)
Files
- postgresql-simple-interval.cabal +2/−1
- source/library/Database/PostgreSQL/Simple/Interval.hs +11/−2
- source/library/Database/PostgreSQL/Simple/Interval/Unstable.hs +63/−0
- source/test-suite/Main.hs +37/−0
postgresql-simple-interval.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: postgresql-simple-interval-version: 0.2025.8.27+version: 0.2025.9.5 synopsis: A simple interval type for PostgreSQL. description: This library provides a simple interval type for PostgreSQL. It supports@@ -28,6 +28,7 @@ base >=4.13 && <4.22, bytestring >=0.10.10 && <0.13, postgresql-simple ^>=0.7,+ time >=1.9.3 && <1.15, default-language: Haskell2010 ghc-options:
source/library/Database/PostgreSQL/Simple/Interval.hs view
@@ -1,7 +1,7 @@ module Database.PostgreSQL.Simple.Interval ( Unstable.Interval (..), - -- * Constructors+ -- * Construction Unstable.zero, Unstable.fromMicroseconds, Unstable.fromMilliseconds,@@ -29,10 +29,19 @@ Unstable.fromWeeksLiteral, Unstable.fromYearsLiteral, + -- * Conversion+ Unstable.intoTime,+ Unstable.fromTime,++ -- ** Saturating+ Unstable.fromTimeSaturating,+ -- * Arithmetic Unstable.negate,- Unstable.negateSaturating, Unstable.add,++ -- ** Saturating+ Unstable.negateSaturating, Unstable.addSaturating, ) where
source/library/Database/PostgreSQL/Simple/Interval/Unstable.hs view
@@ -15,10 +15,12 @@ import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Char8 as Ascii import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Fixed as Fixed import qualified Data.Function as Function import qualified Data.Int as Int import qualified Data.Scientific as Scientific import qualified Data.Text as Text+import qualified Data.Time as Time import qualified Database.Persist as Persist import qualified Database.Persist.Sql as Persist import qualified Database.PostgreSQL.Simple.FromField as Postgres@@ -427,6 +429,67 @@ (Function.on safeAdd months x y) (Function.on safeAdd days x y) (Function.on safeAdd microseconds x y)++-- | Converts an interval into types from the @time@ library. See 'fromTime'+-- for the opposite conversion.+--+-- >>> intoTime (MkInterval 1 2 3)+-- (P1M2D,0.000003s)+intoTime :: Interval -> (Time.CalendarDiffDays, Time.NominalDiffTime)+intoTime x =+ ( Time.CalendarDiffDays+ { Time.cdMonths = toInteger $ months x,+ Time.cdDays = toInteger $ days x+ },+ Time.secondsToNominalDiffTime+ . realToFrac+ . intoMicro+ . toInteger+ $ microseconds x+ )++-- | Converts types from the @time@ library into an interval. See 'intoTime'+-- for the opposite conversion.+--+-- >>> fromTime ('Time.CalendarDiffDays' 1 2) 3+-- Just (MkInterval {months = 1, days = 2, microseconds = 3000000})+--+-- Returns 'Nothing' if the result would overflow. See 'fromTimeSaturating' for+-- a version that uses saturating arithmetic instead.+--+-- >>> fromTime mempty 9223372036854.775808+-- Nothing+--+-- Note that this truncates extra precision.+--+-- >>> fromTime mempty 0.0000009+-- Just (MkInterval {months = 0, days = 0, microseconds = 0})+fromTime :: Time.CalendarDiffDays -> Time.NominalDiffTime -> Maybe Interval+fromTime x y =+ MkInterval+ <$> Bits.toIntegralSized (Time.cdMonths x)+ <*> Bits.toIntegralSized (Time.cdDays x)+ <*> Bits.toIntegralSized (fromMicro . realToFrac $ Time.nominalDiffTimeToSeconds y)++-- | Like 'fromTime' but uses saturating arithmetic rather than returning+-- 'Maybe'.+--+-- >>> fromTimeSaturating ('Time.CalendarDiffDays' 1 2) 3+-- MkInterval {months = 1, days = 2, microseconds = 3000000}+-- >>> fromTimeSaturating mempty 9223372036854.775808+-- MkInterval {months = 1, days = 2, microseconds = 9223372036854775807}+fromTimeSaturating :: Time.CalendarDiffDays -> Time.NominalDiffTime -> Interval+fromTimeSaturating x y =+ MkInterval+ (toIntegralSaturating $ Time.cdMonths x)+ (toIntegralSaturating $ Time.cdDays x)+ (toIntegralSaturating . fromMicro . realToFrac $ Time.nominalDiffTimeToSeconds y)++intoMicro :: Integer -> Fixed.Micro+intoMicro = Fixed.MkFixed++fromMicro :: Fixed.Micro -> Integer+fromMicro (Fixed.MkFixed x) = x -- | Renders an interval to a 'Builder'. This always has the same format: -- @"\@ A mon B day C hour D min E sec F us"@, where @A@, @B@, @C@, @D@, @E@,
source/test-suite/Main.hs view
@@ -11,6 +11,7 @@ import qualified Data.ByteString.Lazy as LazyByteString import qualified Data.Int as Int import qualified Data.Proxy as Proxy+import qualified Data.Time as Time import qualified Database.PostgreSQL.LibPQ as Pq import qualified Database.PostgreSQL.Simple as Postgres import qualified Database.PostgreSQL.Simple.Internal as Postgres@@ -230,6 +231,42 @@ H.it "succeeds with saturating microsecond" $ do I.negateSaturating (I.MkInterval 0 0 minBound) `H.shouldBe` I.MkInterval 0 0 maxBound++ H.describe "intoTime" $ do+ H.it "works" $ do+ I.intoTime (I.MkInterval 1 2 3) `H.shouldBe` (Time.CalendarDiffDays 1 2, 0.000003)++ H.describe "fromTime" $ do+ H.it "succeeds with no overflow" $ do+ I.fromTime (Time.CalendarDiffDays 1 2) 0.000003 `H.shouldBe` Just (I.MkInterval 1 2 3)++ H.it "truncates extra precision" $ do+ I.fromTime mempty 0.0000009 `H.shouldBe` Just (I.MkInterval 0 0 0)++ H.it "fails with microsecond overflow" $ do+ I.fromTime mempty 9223372036854.775808 `H.shouldBe` Nothing++ H.it "fails with day overflow" $ do+ I.fromTime (Time.CalendarDiffDays 0 2147483648) 0 `H.shouldBe` Nothing++ H.it "fails with month overflow" $ do+ I.fromTime (Time.CalendarDiffDays 2147483648 0) 0 `H.shouldBe` Nothing++ H.describe "fromTimeSaturating" $ do+ H.it "works without saturating" $ do+ I.fromTimeSaturating (Time.CalendarDiffDays 1 2) 0.000003 `H.shouldBe` I.MkInterval 1 2 3++ H.it "truncates extra precision" $ do+ I.fromTimeSaturating mempty 0.0000009 `H.shouldBe` I.MkInterval 0 0 0++ H.it "works with saturating microseconds" $ do+ I.fromTimeSaturating mempty 9223372036854.775808 `H.shouldBe` I.MkInterval 0 0 maxBound++ H.it "works with saturating days" $ do+ I.fromTimeSaturating (Time.CalendarDiffDays 0 2147483648) 0 `H.shouldBe` I.MkInterval 0 maxBound 0++ H.it "works with saturating months" $ do+ I.fromTimeSaturating (Time.CalendarDiffDays 2147483648 0) 0 `H.shouldBe` I.MkInterval maxBound 0 0 H.describe "render" $ do H.it "works with zero" $ do