robx
robx

Reputation: 2329

Converting Postgres interval to Haskell NominalTimeDiff with postgresql-simple

I (think I) need FromField and ToField instances for NominalDiffTime, in order to interface with a Postgres table with a column of type interval.

I've found the interval TypeInfo and am in the process of figuring out how to interface with TypeInfo, but hopefully there is an easier way?

Upvotes: 1

Views: 259

Answers (1)

robx
robx

Reputation: 2329

First off, there are reasons not to provide such an instance. NominalDiffTime doesn't sensibly represent all values of interval. For example, 2 days is different from 48 hours. Particularly, 1 year can't be expressed in days.

Here's a FromField instance for NominalDiffTime that fails for intervals with components that are larger than an hour (something like 25:00:00 is ok, though).

instance FromField NominalDiffTime where
    fromField f mdat =
        if typeOid f /= typoid interval
            then returnError Incompatible f ""
            else case mdat of
                Nothing  -> returnError UnexpectedNull f ""
                Just dat -> case parseOnly (nominalDiffTime <* endOfInput) dat of
                    Left msg  -> returnError ConversionFailed f msg
                    Right t   -> return t


nominalDiffTime :: Parser NominalDiffTime
nominalDiffTime = do
    (h, m, s) <- interval
    return . fromRational . toRational $ s + 60*(fromIntegral m) + 60*60*(fromIntegral h)

-- | Parse a limited postgres interval of the form [-]HHH:MM:SS.[SSSS] (no larger units than hours).
interval :: Parser (Int, Int, Pico)
interval = do
    h <- signed decimal <* char ':'
    m <- twoDigits <* char ':'
    s <- seconds
    if m < 60 && s <= 60
        then return (h, m, s)
        else fail "invalid interval"

-- functions below borrowed from postgresql-simple
seconds :: Parser Pico
twoDigits :: Parser Int

Upvotes: 3

Related Questions