hpgsql-simple-compat-0.1.0.0: src/Database/PostgreSQL/Simple/Time/Internal/Printer.hs
------------------------------------------------------------------------------
-- Module: Database.PostgreSQL.Simple.Time.Internal.Printer
-- Copyright: (c) 2012-2015 Leon P Smith
-- License: BSD3
-- Maintainer: Leon P Smith <leon@melding-monads.com>
-- Stability: experimental
------------------------------------------------------------------------------
module Database.PostgreSQL.Simple.Time.Internal.Printer
( day,
timeOfDay,
timeZone,
utcTime,
localTime,
zonedTime,
nominalDiffTime,
calendarDiffTime,
)
where
import Control.Arrow ((>>>))
import Data.ByteString.Builder (Builder, byteString, integerDec)
import Data.ByteString.Builder.Prim
( BoundedPrim,
FixedPrim,
char8,
condB,
emptyB,
int32Dec,
liftFixedToBounded,
primBounded,
(>$<),
(>*<),
)
import Data.Char (chr)
import Data.Int (Int32, Int64)
import Data.String (fromString)
import Data.Time.Compat
( Day,
LocalTime (..),
NominalDiffTime,
TimeOfDay (..),
TimeZone,
UTCTime (..),
ZonedTime (..),
timeToTimeOfDay,
timeZoneMinutes,
toGregorian,
)
import Data.Time.Format.ISO8601.Compat (iso8601Show)
import Data.Time.LocalTime.Compat (CalendarDiffTime)
import Database.PostgreSQL.Simple.Compat (fromPico)
import Unsafe.Coerce (unsafeCoerce)
liftB :: FixedPrim a -> BoundedPrim a
liftB = liftFixedToBounded
digit :: FixedPrim Int
digit = (\x -> chr (x + 48)) >$< char8
digits2 :: FixedPrim Int
digits2 = (`quotRem` 10) >$< (digit >*< digit)
digits3 :: FixedPrim Int
digits3 = (`quotRem` 10) >$< (digits2 >*< digit)
digits4 :: FixedPrim Int
digits4 = (`quotRem` 10) >$< (digits3 >*< digit)
frac :: BoundedPrim Int64
frac = condB (== 0) emptyB ((,) '.' >$< (liftB char8 >*< trunc12))
where
trunc12 :: BoundedPrim Int64
trunc12 =
(`quotRem` 1000000)
>$< condB
(\(_, y) -> y == 0)
(fst >$< trunc6)
(liftB digits6 >*< trunc6)
digitB = liftB digit
digits6 = (fromIntegral >>> (`quotRem` 10)) >$< (digits5 >*< digit)
digits5 = (`quotRem` 10) >$< (digits4 >*< digit)
trunc6 = (fromIntegral >>> (`quotRem` 100000)) >$< (digitB >*< trunc5)
trunc5 = condB (== 0) emptyB ((`quotRem` 10000) >$< (digitB >*< trunc4))
trunc4 = condB (== 0) emptyB ((`quotRem` 1000) >$< (digitB >*< trunc3))
trunc3 = condB (== 0) emptyB ((`quotRem` 100) >$< (digitB >*< trunc2))
trunc2 = condB (== 0) emptyB ((`quotRem` 10) >$< (digitB >*< trunc1))
trunc1 = condB (== 0) emptyB digitB
year :: BoundedPrim Int32
year = condB (>= 10000) int32Dec (checkBCE >$< liftB digits4)
where
checkBCE :: Int32 -> Int
checkBCE y
| y > 0 = fromIntegral y
| otherwise = error msg
msg = "Database.PostgreSQL.Simple.Time.Printer.year: years BCE not supported"
day :: BoundedPrim Day
day = toYMD >$< (year >*< liftB (char8 >*< digits2 >*< char8 >*< digits2))
where
toYMD (toGregorian -> (fromIntegral -> !y, !m, !d)) = (y, ('-', (m, ('-', d))))
timeOfDay :: BoundedPrim TimeOfDay
timeOfDay = f >$< (hh_mm_ >*< ss)
where
f (TimeOfDay h m s) = ((h, (':', (m, ':'))), s)
hh_mm_ = liftB (digits2 >*< char8 >*< digits2 >*< char8)
ss =
(\s -> fromIntegral (fromPico s) `quotRem` 1000000000000)
>$< (liftB (fromIntegral >$< digits2) >*< frac)
timeZone :: BoundedPrim TimeZone
timeZone = timeZoneMinutes >$< tz
where
tz = condB (>= 0) ((,) '+' >$< tzh) ((,) '-' . negate >$< tzh)
tzh = liftB char8 >*< ((`quotRem` 60) >$< (liftB digits2 >*< tzm))
tzm = condB (== 0) emptyB ((,) ':' >$< liftB (char8 >*< digits2))
utcTime :: BoundedPrim UTCTime
utcTime = f >$< (day >*< liftB char8 >*< timeOfDay >*< liftB char8)
where
f (UTCTime d (timeToTimeOfDay -> tod)) = (d, (' ', (tod, 'Z')))
localTime :: BoundedPrim LocalTime
localTime = f >$< (day >*< liftB char8 >*< timeOfDay)
where
f (LocalTime d tod) = (d, (' ', tod))
zonedTime :: BoundedPrim ZonedTime
zonedTime = f >$< (localTime >*< timeZone)
where
f (ZonedTime lt tz) = (lt, tz)
nominalDiffTime :: NominalDiffTime -> Builder
nominalDiffTime xy = integerDec x <> primBounded frac (abs (fromIntegral y))
where
(x, y) = fromPico (unsafeCoerce xy) `quotRem` 1000000000000
calendarDiffTime :: CalendarDiffTime -> Builder
calendarDiffTime =
byteString
. fromString
-- from the docs: "Beware: fromString truncates multi-byte characters to octets".
-- However, I think this is a safe usage, because ISO8601-encoding seems restricted
-- to ASCII output.
. iso8601Show