Implement UTCTime decoding

Remove LocalTime decoding because it requires IO to convert from Postgres time.
This commit is contained in:
2024-01-08 15:28:14 +01:00
parent 68d747f605
commit 4bf489c554
4 changed files with 33 additions and 30 deletions
+13 -16
View File
@@ -19,8 +19,8 @@ import Data.Proxy (Proxy (..))
import Data.Time
( Day (..)
, DiffTime
, LocalTime (..)
, TimeOfDay
, UTCTime (..)
, addDays
, fromGregorian
, picosecondsToDiffTime
@@ -171,23 +171,20 @@ instance FromField TimeOfDay where
validOid Proxy = Oid.time
parseField = timeToTimeOfDay <$> parseField @DiffTime
fromPostgresTimestamp :: Int -> (Day, DiffTime)
fromPostgresTimestamp ts = (day, time)
where
(days, microseconds) = ts `divMod` (86400 * 1000000)
day = fromPostgresJulian $ fromIntegral days
time = picosecondsToDiffTime $ fromIntegral microseconds * 1000000
-- | See https://www.postgresql.org/docs/current/datatype-datetime.html.
-- | Accepts @timestamp@.
-- Note that Postgres uses the proleptic Gregorian calendar, whereas @Show Day@ and @fromGregorian@ use an astronomical calendar.
-- In short, Postgres treats 1 BC as a leap year and doesn't have a year zero.
-- This means that working with negative dates will be different in Postgres and your application code.
instance FromField LocalTime where
validOid Proxy = Oid.timestamp
parseField = fromPostgresTimestamp <$> readBigEndian @Int <$> AP.takeByteString
-- Accepts @timestamp with timezone@.
instance FromField UTCTime where
validOid Proxy = Oid.timestampWithTimezone
parseField = toUTCTime . fromPostgresTimestamp <$> readBigEndian @Int <$> AP.takeByteString
where
fromPostgresTimestamp :: Int -> LocalTime
fromPostgresTimestamp ts =
let
(days, microseconds) = ts `divMod` (86400 * 1000000)
day = fromPostgresJulian $ fromIntegral days
time = picosecondsToDiffTime $ fromIntegral microseconds * 1000000
in
LocalTime day (timeToTimeOfDay time)
toUTCTime (day, time) = UTCTime day time
newtype RawField a = RawField a
deriving (Eq, Show)
+4
View File
@@ -60,3 +60,7 @@ time = eq $ Oid 1083
-- | A point in time.
timestamp :: Oid -> Bool
timestamp = eq $ Oid 1114
-- | A point in time.
timestampWithTimezone :: Oid -> Bool
timestampWithTimezone = eq $ Oid 1184