postgresql-simple-interval 0.2025.7.10 → 0.2025.7.11
raw patch · 3 files changed
+74/−38 lines, 3 filesdep +persistentdep +textdep ~basedep ~bytestringPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependencies added: persistent, text
Dependency ranges changed: base, bytestring
API changes (from Hackage documentation)
+ Database.PostgreSQL.Simple.Interval.Unstable: instance Database.Persist.Class.PersistField.PersistField Database.PostgreSQL.Simple.Interval.Unstable.Interval
+ Database.PostgreSQL.Simple.Interval.Unstable: instance Database.Persist.Sql.Class.PersistFieldSql Database.PostgreSQL.Simple.Interval.Unstable.Interval
Files
- postgresql-simple-interval.cabal +14/−6
- source/library/Database/PostgreSQL/Simple/Interval/Unstable.hs +44/−15
- source/test-suite/Main.hs +16/−17
postgresql-simple-interval.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: postgresql-simple-interval-version: 0.2025.7.10+version: 0.2025.7.11 synopsis: A simple interval type for PostgreSQL. description: This library provides a simple interval type for PostgreSQL. It supports@@ -25,8 +25,8 @@ common library build-depends: attoparsec ^>=0.14.4,- base ^>={4.19, 4.20, 4.21},- bytestring ^>=0.12.1,+ base >=4.13 && <4.22,+ bytestring >=0.10.10 && <0.13, default-language: Haskell2010 ghc-options:@@ -35,12 +35,18 @@ -Wno-implicit-prelude -Wno-missing-deriving-strategies -Wno-missing-export-lists- -Wno-missing-kind-signatures- -Wno-missing-safe-haskell-mode- -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe + if impl(ghc >=8.10)+ ghc-options:+ -Wno-missing-safe-haskell-mode+ -Wno-prepositive-qualified-module++ if impl(ghc >=9.2)+ ghc-options:+ -Wno-missing-kind-signatures+ if flag(pedantic) ghc-options: -Werror @@ -54,8 +60,10 @@ library import: library build-depends:+ persistent ^>=2.17, postgresql-simple ^>=0.7, scientific ^>=0.3.8,+ text >=1.2.4 && <1.3 || >=2.0 && <2.2, -- cabal-gild: discover source/library exposed-modules:
source/library/Database/PostgreSQL/Simple/Interval/Unstable.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE LexicalNegation #-} {-# LANGUAGE NumDecimals #-} {-# LANGUAGE OverloadedStrings #-} @@ -10,9 +9,14 @@ import qualified Data.Bits as Bits import qualified Data.ByteString as ByteString import qualified Data.ByteString.Builder as Builder+import qualified Data.ByteString.Char8 as Ascii+import qualified Data.ByteString.Lazy as LazyByteString 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 Database.Persist as Persist+import qualified Database.Persist.Sql as Persist import qualified Database.PostgreSQL.Simple.FromField as Postgres import qualified Database.PostgreSQL.Simple.ToField as Postgres import qualified Database.PostgreSQL.Simple.TypeInfo.Static as Postgres@@ -26,11 +30,11 @@ -- Note that the @time@ library provides several duration types that are not -- appropriate to use as PostgreSQL intervals: ----- - @NominalDiffTime@: Does not handle days or months. Allows up to picosecond--- precision. Is not bounded.--- - @CalendarDiffTime@: Does not handle days. Embeds a @NominalDiffTime@. Is--- not bounded.--- - @CalendarDiffDays@: Does not handle seconds. Is not bounded.+-- - 'Data.Time.NominalDiffTime': Does not handle days or months. Allows up to+-- picosecond precision. Is not bounded.+-- - 'Data.Time.CalendarDiffTime': Does not handle days. Embeds a+-- @NominalDiffTime@. Is not bounded.+-- - 'Data.Time.CalendarDiffDays': Does not handle seconds. Is not bounded. data Interval = MkInterval { months :: !Int.Int32, days :: !Int.Int32,@@ -43,10 +47,35 @@ fromField = Postgres.attoFieldParser (== Postgres.intervalOid) parse -- | Uses 'render'. Always includes an @interval@ prefix, like--- @interval '2 months 3 days 4 microseconds'@.+-- @interval '\@ 0 mon -1 day +2 us'@. instance Postgres.ToField Interval where- toField = Postgres.Plain . ("interval " <>) . Postgres.inQuotes . render+ toField = Postgres.Plain . ("interval '" <>) . (<> "'") . render +-- | Behaves the same as the 'Postgres.FromField' and 'Postgres.ToField'+-- instances.+instance Persist.PersistField Interval where+ fromPersistValue persistValue = case persistValue of+ Persist.PersistLiteralEscaped byteString+ | Right interval <- A.parseOnly parse byteString ->+ Right interval+ Persist.PersistLiteral byteString+ | Just withoutPrefix <- Ascii.stripPrefix "interval '" byteString,+ Just withoutSuffix <- Ascii.stripSuffix "'" withoutPrefix,+ Right interval <- A.parseOnly parse withoutSuffix ->+ Right interval+ _ -> Left $ "Invalid interval: " <> Text.pack (show persistValue)+ toPersistValue =+ Persist.PersistLiteral+ . LazyByteString.toStrict+ . Builder.toLazyByteString+ . ("interval '" <>)+ . (<> "'")+ . render++-- | @'Persist.SqlOther' "interval"@+instance Persist.PersistFieldSql Interval where+ sqlType = const $ Persist.SqlOther "interval"+ -- | The empty interval, representing no time at all. -- -- >>> zero@@ -336,13 +365,13 @@ negateComponent :: Component -> Component negateComponent c = case c of- Years y -> Years -y- Months m -> Months -m- Days d -> Days -d- Hours h -> Hours -h- Minutes m -> Minutes -m- Seconds u -> Seconds -u- Microseconds u -> Microseconds -u+ Years y -> Years (-y)+ Months m -> Months (-m)+ Days d -> Days (-d)+ Hours h -> Hours (-h)+ Minutes m -> Minutes (-m)+ Seconds u -> Seconds (-u)+ Microseconds u -> Microseconds (-u) negateComponentsWhen :: (Functor f) => Bool -> f Component -> f Component negateComponentsWhen p = if p then fmap negateComponent else id
source/test-suite/Main.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE LexicalNegation #-} {-# LANGUAGE NumDecimals #-} {-# LANGUAGE OverloadedStrings #-} @@ -30,7 +29,7 @@ actual `H.shouldBe` Nothing H.it "fails with negative month overflow" $ do- let actual = I.add (I.fromMonths minBound) (I.fromMonths -1)+ let actual = I.add (I.fromMonths minBound) (I.fromMonths (-1)) actual `H.shouldBe` Nothing H.it "fails with positive day overflow" $ do@@ -38,7 +37,7 @@ actual `H.shouldBe` Nothing H.it "fails with negative day overflow" $ do- let actual = I.add (I.fromDays minBound) (I.fromDays -1)+ let actual = I.add (I.fromDays minBound) (I.fromDays (-1)) actual `H.shouldBe` Nothing H.it "fails with positive microsecond overflow" $ do@@ -46,7 +45,7 @@ actual `H.shouldBe` Nothing H.it "fails with negative microsecond overflow" $ do- let actual = I.add (I.fromMicroseconds minBound) (I.fromMicroseconds -1)+ let actual = I.add (I.fromMicroseconds minBound) (I.fromMicroseconds (-1)) actual `H.shouldBe` Nothing H.describe "fromMicroseconds" $ do@@ -113,7 +112,7 @@ actual `H.shouldBe` "@ +1 mon +2 day +3 us" H.it "works with negative components" $ do- let actual = Builder.toLazyByteString . I.render $ I.MkInterval -3 -2 -1+ let actual = Builder.toLazyByteString . I.render $ I.MkInterval (-3) (-2) (-1) actual `H.shouldBe` "@ -3 mon -2 day -1 us" H.describe "parse" $ do@@ -190,36 +189,36 @@ examples = [ mkExample 0 0 0 "PT0S" "00:00:00" "@ 0" "0", mkExample 1 0 0 "P1M" "1 mon" "@ 1 mon" "0-1",- mkExample -1 0 0 "P-1M" "-1 mons" "@ 1 mon ago" "-0-1",+ mkExample (-1) 0 0 "P-1M" "-1 mons" "@ 1 mon ago" "-0-1", mkExample 3 0 0 "P3M" "3 mons" "@ 3 mons" "0-3", mkExample 6 0 0 "P6M" "6 mons" "@ 6 mons" "0-6", mkExample 12 0 0 "P1Y" "1 year" "@ 1 year" "1-0",- mkExample -12 0 0 "P-1Y" "-1 years" "@ 1 year ago" "-1-0",+ mkExample (-12) 0 0 "P-1Y" "-1 years" "@ 1 year ago" "-1-0", mkExample 13 0 0 "P1Y1M" "1 year 1 mon" "@ 1 year 1 mon" "1-1",- mkExample -13 0 0 "P-1Y-1M" "-1 years -1 mons" "@ 1 year 1 mon ago" "-1-1",+ mkExample (-13) 0 0 "P-1Y-1M" "-1 years -1 mons" "@ 1 year 1 mon ago" "-1-1", mkExample 24 0 0 "P2Y" "2 years" "@ 2 years" "2-0", mkExample 0 1 0 "P1D" "1 day" "@ 1 day" "1 0:00:00",- mkExample 0 -1 0 "P-1D" "-1 days" "@ 1 day ago" "-1 0:00:00",+ mkExample 0 (-1) 0 "P-1D" "-1 days" "@ 1 day ago" "-1 0:00:00", mkExample 0 2 0 "P2D" "2 days" "@ 2 days" "2 0:00:00", mkExample 0 7 0 "P7D" "7 days" "@ 7 days" "7 0:00:00", mkExample 0 0 1 "PT0.000001S" "00:00:00.000001" "@ 0.000001 secs" "0:00:00.000001",- mkExample 0 0 -1 "PT-0.000001S" "-00:00:00.000001" "@ 0.000001 secs ago" "-0:00:00.000001",+ mkExample 0 0 (-1) "PT-0.000001S" "-00:00:00.000001" "@ 0.000001 secs ago" "-0:00:00.000001", mkExample 0 0 1e3 "PT0.001S" "00:00:00.001" "@ 0.001 secs" "0:00:00.001", mkExample 0 0 1e6 "PT1S" "00:00:01" "@ 1 sec" "0:00:01",- mkExample 0 0 -1e6 "PT-1S" "-00:00:01" "@ 1 sec ago" "-0:00:01",+ mkExample 0 0 (-1e6) "PT-1S" "-00:00:01" "@ 1 sec ago" "-0:00:01", mkExample 0 0 2e6 "PT2S" "00:00:02" "@ 2 secs" "0:00:02", mkExample 0 0 60e6 "PT1M" "00:01:00" "@ 1 min" "0:01:00",- mkExample 0 0 -60e6 "PT-1M" "-00:01:00" "@ 1 min ago" "-0:01:00",+ mkExample 0 0 (-60e6) "PT-1M" "-00:01:00" "@ 1 min ago" "-0:01:00", mkExample 0 0 120e6 "PT2M" "00:02:00" "@ 2 mins" "0:02:00", mkExample 0 0 3600e6 "PT1H" "01:00:00" "@ 1 hour" "01:00:00",- mkExample 0 0 -3600e6 "PT-1H" "-01:00:00" "@ 1 hour ago" "-01:00:00",+ mkExample 0 0 (-3600e6) "PT-1H" "-01:00:00" "@ 1 hour ago" "-01:00:00", mkExample 0 0 7200e6 "PT2H" "02:00:00" "@ 2 hours" "02:00:00", mkExample 0 0 86400e6 "PT24H" "24:00:00" "@ 24 hours" "24:00:00", mkExample 1 1 1e6 "P1M1DT1S" "1 mon 1 day 00:00:01" "@ 1 mon 1 day 1 sec" "+0-1 +1 +0:00:01",- mkExample -1 -1 -1e6 "P-1M-1DT-1S" "-1 mons -1 days -00:00:01" "@ 1 mon 1 day 1 sec ago" "-0-1 -1 -0:00:01",- mkExample -1 1 1e6 "P-1M1DT1S" "-1 mons +1 day 00:00:01" "@ 1 mon -1 days -1 sec ago" "-0-1 +1 +0:00:01",- mkExample 1 -1 1e6 "P1M-1DT1S" "1 mon -1 days +00:00:01" "@ 1 mon -1 days 1 sec" "+0-1 -1 +0:00:01",- mkExample 1 1 -1e6 "P1M1DT-1S" "1 mon 1 day -00:00:01" "@ 1 mon 1 day -1 sec" "+0-1 +1 -0:00:01",+ mkExample (-1) (-1) (-1e6) "P-1M-1DT-1S" "-1 mons -1 days -00:00:01" "@ 1 mon 1 day 1 sec ago" "-0-1 -1 -0:00:01",+ mkExample (-1) 1 1e6 "P-1M1DT1S" "-1 mons +1 day 00:00:01" "@ 1 mon -1 days -1 sec ago" "-0-1 +1 +0:00:01",+ mkExample 1 (-1) 1e6 "P1M-1DT1S" "1 mon -1 days +00:00:01" "@ 1 mon -1 days 1 sec" "+0-1 -1 +0:00:01",+ mkExample 1 1 (-1e6) "P1M1DT-1S" "1 mon 1 day -00:00:01" "@ 1 mon 1 day -1 sec" "+0-1 +1 -0:00:01", mkExample 14 3 14706000007 "P1Y2M3DT4H5M6.000007S" "1 year 2 mons 3 days 04:05:06.000007" "@ 1 year 2 mons 3 days 4 hours 5 mins 6.000007 secs" "+1-2 +3 +4:05:06.000007", mkExample maxBound 0 0 "P178956970Y7M" "178956970 years 7 mons" "@ 178956970 years 7 mons" "178956970-7", mkExample minBound 0 0 "P-178956970Y-8M" "-178956970 years -8 mons" "@ 178956970 years 8 mons ago" "-178956970-8",