From 29958352dded80c433fc33463d0befaf15d01a6c Mon Sep 17 00:00:00 2001 From: Tom Ellis Date: Thu, 4 Nov 2021 11:41:59 +0000 Subject: [PATCH] Fix ISO 8601 formatting See https://github.com/tomjaguarpaw/haskell-opaleye/pull/532#issuecomment-960639489 Thanks to @kozak --- CHANGELOG.md | 2 ++ Test/Test.hs | 10 ++++++++++ src/Opaleye/Internal/PGTypes.hs | 11 ++++++----- src/Opaleye/Internal/PGTypesExternal.hs | 14 ++++++-------- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca731a2a..4307ef603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +Fix ISO 8601 date fomatting. Thanks to Michal @kozak. + ## 0.7.6.1 No user-visible changes diff --git a/Test/Test.hs b/Test/Test.hs index 113736050..4b1a2e36f 100644 --- a/Test/Test.hs +++ b/Test/Test.hs @@ -1241,6 +1241,8 @@ testLiterals = do exampleDate = Time.fromGregorian 2018 11 29 exampleTime = Time.TimeOfDay 11 22 33 exampleUTCTime = Time.UTCTime exampleDate (Time.sinceMidnight exampleTime) + exampleDatePadded = Time.fromGregorian 18 11 29 + exampleUTCTimePadded = Time.UTCTime exampleDatePadded (Time.sinceMidnight exampleTime) it "sqlString" $ testLiteral O.sqlString "Hello" it "sqlLazyByteString" $ testLiteral O.sqlLazyByteString "Hello" it "sqlNumeric" $ testLiteral O.sqlNumeric 3.14159 @@ -1250,8 +1252,11 @@ testLiterals = do it "sqlBool" $ testLiteral O.sqlBool True it "sqlUUID" $ testLiteral O.sqlUUID (read "c2cc10e1-57d6-4b6f-9899-38d972112d8c") it "sqlDay" $ testLiteral O.sqlDay exampleDate + it "sqlDayPadded" $ testLiteral O.sqlDay exampleDatePadded it "sqlUTCTime" $ testLiteral O.sqlUTCTime exampleUTCTime + it "sqlUTCTimePadded" $ testLiteral O.sqlUTCTime exampleUTCTimePadded it "sqlLocalTime" $ testLiteral O.sqlLocalTime (Time.LocalTime exampleDate exampleTime) + it "sqlLocalTimePadded" $ testLiteral O.sqlLocalTime (Time.LocalTime exampleDatePadded exampleTime) -- ZonedTime has no Eq instance, so we compare on the result of 'zonedTimeToUTC' it "sqlZonedTime" $ @@ -1259,6 +1264,11 @@ testLiterals = do testH (pure (O.sqlZonedTime value)) (\r -> map Time.zonedTimeToUTC r `shouldBe` [Time.zonedTimeToUTC value]) + it "sqlZonedTimePadded" $ + let value = Time.utcToZonedTime Time.utc exampleUTCTimePadded in + testH (pure (O.sqlZonedTime value)) + (\r -> map Time.zonedTimeToUTC r `shouldBe` [Time.zonedTimeToUTC value]) + it "sqlInterval" $ testLiteral O.sqlInterval (Time.calendarTimeTime 1) -- Check that MaybeFields's "Nothings" are not distinct, even if we diff --git a/src/Opaleye/Internal/PGTypes.hs b/src/Opaleye/Internal/PGTypes.hs index fdcb563e3..0454fb0ae 100644 --- a/src/Opaleye/Internal/PGTypes.hs +++ b/src/Opaleye/Internal/PGTypes.hs @@ -13,12 +13,13 @@ import qualified Data.Text.Lazy as LText import qualified Data.Text.Lazy.Encoding as LTextEncoding import qualified Data.ByteString as SByteString import qualified Data.ByteString.Lazy as LByteString -import qualified Data.Time.Compat as Time -import qualified Data.Time.Locale.Compat as Locale +import qualified Data.Time.Format.ISO8601.Compat as Time -unsafePgFormatTime :: Time.FormatTime t => HPQ.Name -> String -> t -> Column c -unsafePgFormatTime typeName formatString = castToType typeName . format - where format = Time.formatTime Locale.defaultTimeLocale formatString +unsafePgFormatTime :: Time.ISO8601 t => HPQ.Name -> t -> Column c +unsafePgFormatTime typeName = castToType typeName . format + where + format = quote . Time.iso8601Show + quote s = "'" ++ s ++ "'" literalColumn :: forall a. IsSqlType a => HPQ.Literal -> Column a literalColumn = Column . HPQ.CastExpr (showSqlType (Proxy :: Proxy a)) . HPQ.ConstExpr diff --git a/src/Opaleye/Internal/PGTypesExternal.hs b/src/Opaleye/Internal/PGTypesExternal.hs index cf833a5da..ab14ffaca 100644 --- a/src/Opaleye/Internal/PGTypesExternal.hs +++ b/src/Opaleye/Internal/PGTypesExternal.hs @@ -102,27 +102,25 @@ pgUUID :: UUID.UUID -> Column PGUuid pgUUID = IPT.literalColumn . HPQ.StringLit . UUID.toString pgDay :: Time.Day -> Column PGDate -pgDay = IPT.unsafePgFormatTime "date" "'%F'" +pgDay = IPT.unsafePgFormatTime "date" pgUTCTime :: Time.UTCTime -> Column PGTimestamptz -pgUTCTime = IPT.unsafePgFormatTime "timestamptz" "'%FT%T%QZ'" +pgUTCTime = IPT.unsafePgFormatTime "timestamptz" pgLocalTime :: Time.LocalTime -> Column PGTimestamp -pgLocalTime = IPT.unsafePgFormatTime "timestamp" "'%FT%T%Q'" +pgLocalTime = IPT.unsafePgFormatTime "timestamp" pgZonedTime :: Time.ZonedTime -> Column PGTimestamptz -pgZonedTime = IPT.unsafePgFormatTime "timestamptz" "'%FT%T%Q%z'" +pgZonedTime = IPT.unsafePgFormatTime "timestamptz" pgTimeOfDay :: Time.TimeOfDay -> Column PGTime -pgTimeOfDay = IPT.unsafePgFormatTime "time" "'%T%Q'" +pgTimeOfDay = IPT.unsafePgFormatTime "time" -- "We recommend not using the type time with time zone" -- http://www.postgresql.org/docs/8.3/static/datatype-datetime.html sqlInterval :: Time.CalendarDiffTime -> Column PGInterval -sqlInterval = IPT.castToType "interval" . quote . Time.Format.ISO8601.iso8601Show - where - quote s = "'" ++ s ++ "'" +sqlInterval = IPT.unsafePgFormatTime "interval" pgCiStrictText :: CI.CI SText.Text -> Column PGCitext pgCiStrictText = IPT.literalColumn . HPQ.StringLit . SText.unpack . CI.original