module Database.HDBC.SqlValue
    (
     
     SqlValue(..),
     safeFromSql, toSql, fromSql,
     nToSql, iToSql, posixToSql
    )
where
import Data.Dynamic
import qualified Data.ByteString.UTF8 as BUTF8
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BSL
import Data.Char(ord,toUpper)
import Data.Word
import Data.Int
import qualified System.Time as ST
import Data.Time
import Data.Time.Clock.POSIX
import Database.HDBC.Locale (defaultTimeLocale, iso8601DateFormat)
import Data.Ratio
import Data.Convertible
import Data.Fixed
import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL
quickError :: (Typeable a, Convertible SqlValue a) => SqlValue -> ConvertResult a
quickError sv = convError "incompatible types" sv
  
toSql :: Convertible a SqlValue => a -> SqlValue
toSql = convert
safeFromSql :: Convertible SqlValue a => SqlValue -> ConvertResult a
safeFromSql = safeConvert
fromSql :: Convertible SqlValue a => SqlValue -> a
fromSql = convert
nToSql :: Integral a => a -> SqlValue
nToSql n = SqlInteger (toInteger n)
iToSql :: Int -> SqlValue
iToSql = toSql
posixToSql :: POSIXTime -> SqlValue
posixToSql x = SqlPOSIXTime x
data SqlValue = SqlString String 
              | SqlByteString B.ByteString
              | SqlWord32 Word32
              | SqlWord64 Word64
              | SqlInt32 Int32
              | SqlInt64 Int64
              | SqlInteger Integer
              | SqlChar Char
              | SqlBool Bool
              | SqlDouble Double
              | SqlRational Rational
              | SqlLocalDate Day            
              | SqlLocalTimeOfDay TimeOfDay 
              | SqlZonedLocalTimeOfDay TimeOfDay TimeZone 
              | SqlLocalTime LocalTime      
              | SqlZonedTime ZonedTime      
              | SqlUTCTime UTCTime          
              | SqlDiffTime NominalDiffTime 
              | SqlPOSIXTime POSIXTime      
              | SqlEpochTime Integer      
              | SqlTimeDiff Integer 
              | SqlNull         
     deriving (Show, Typeable)
instance Eq SqlValue where
    SqlString a == SqlString b = a == b
    SqlByteString a == SqlByteString b = a == b
    SqlWord32 a == SqlWord32 b = a == b
    SqlWord64 a == SqlWord64 b = a == b
    SqlInt32 a == SqlInt32 b = a == b
    SqlInt64 a == SqlInt64 b = a == b
    SqlInteger a == SqlInteger b = a == b
    SqlChar a == SqlChar b = a == b
    SqlBool a == SqlBool b = a == b
    SqlDouble a == SqlDouble b = a == b
    SqlRational a == SqlRational b = a == b
    SqlLocalTimeOfDay a == SqlLocalTimeOfDay b = a == b
    SqlZonedLocalTimeOfDay a b == SqlZonedLocalTimeOfDay c d = a == c && b == d
    SqlLocalTime a == SqlLocalTime b = a == b
    SqlLocalDate a == SqlLocalDate b = a == b
    SqlZonedTime a == SqlZonedTime b = zonedTimeToUTC a == zonedTimeToUTC b
    SqlUTCTime a == SqlUTCTime b = a == b
    SqlPOSIXTime a == SqlPOSIXTime b = a == b
    SqlDiffTime a == SqlDiffTime b = a == b
    SqlEpochTime a == SqlEpochTime b = a == b
    SqlTimeDiff a == SqlTimeDiff b = a == b
    SqlNull == SqlNull = True
    SqlNull == _ = False
    _ == SqlNull = False
    a == b = ((safeFromSql a)::ConvertResult String) == 
             ((safeFromSql b)::ConvertResult String)
deriving instance Typeable ST.ClockTime
deriving instance Typeable ST.TimeDiff
instance Convertible SqlValue SqlValue where
    safeConvert = return
instance Convertible String SqlValue where
    safeConvert = return . SqlString
instance Convertible SqlValue String where
    safeConvert (SqlString x) = return x
    safeConvert (SqlByteString x) = return . BUTF8.toString $ x
    safeConvert (SqlInt32 x) = return . show $ x
    safeConvert (SqlInt64 x) = return . show $ x
    safeConvert (SqlWord32 x) = return . show $ x
    safeConvert (SqlWord64 x) = return . show $ x
    safeConvert (SqlInteger x) = return . show $ x
    safeConvert (SqlChar x) = return [x]
    safeConvert (SqlBool x) = return . show $ x
    safeConvert (SqlDouble x) = return . show $ x
    safeConvert (SqlRational x) = return . show $ x
    safeConvert (SqlLocalDate x) = 
        return . formatTime defaultTimeLocale (iso8601DateFormat Nothing) $ x
    safeConvert (SqlLocalTimeOfDay x) = 
        return . formatTime defaultTimeLocale "%T%Q" $ x
    safeConvert (SqlZonedLocalTimeOfDay tod tz) = 
        return $ formatTime defaultTimeLocale "%T%Q " tod ++
                 formatTime defaultTimeLocale "%z" tz
    safeConvert (SqlLocalTime x) = 
        return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q")) $ x
    safeConvert (SqlZonedTime x) = 
        return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q %z")) $ x
    safeConvert (SqlUTCTime x) = 
        return . formatTime defaultTimeLocale (iso8601DateFormat (Just "%T%Q")) $ x
    safeConvert (SqlDiffTime x) = return $ showFixed True fixedval
            where fixedval :: Pico
                  fixedval = fromRational . toRational $ x
    safeConvert (SqlPOSIXTime x) = return $ showFixed True fixedval
            where fixedval :: Pico
                  fixedval = fromRational . toRational $ x
    safeConvert (SqlEpochTime x) = return . show $ x
    safeConvert (SqlTimeDiff x) = return . show $ x
    safeConvert y@(SqlNull) = quickError y
instance Convertible TS.Text SqlValue where
    safeConvert = return . SqlString . TS.unpack
instance Convertible SqlValue TS.Text where
    safeConvert = fmap TS.pack . safeConvert
instance Convertible TL.Text SqlValue where
    safeConvert = return . SqlString . TL.unpack
instance Convertible SqlValue TL.Text where
    safeConvert = fmap TL.pack . safeConvert
#ifdef __HUGS__
instance Typeable B.ByteString where
    typeOf _ = mkTypeName "ByteString"
#endif
instance Convertible B.ByteString SqlValue where
    safeConvert = return . SqlByteString
instance Convertible SqlValue B.ByteString where
    safeConvert (SqlByteString x) = return x
    safeConvert y@(SqlNull) = quickError y
    safeConvert x = safeConvert x >>= return . BUTF8.fromString
instance Convertible BSL.ByteString SqlValue where
    safeConvert = return . SqlByteString . B.concat . BSL.toChunks
instance Convertible SqlValue BSL.ByteString where
    safeConvert x = do bs <- safeConvert x
                       return (BSL.fromChunks [bs])
instance Convertible Int SqlValue where
    safeConvert x = 
        do i <- ((safeConvert x)::ConvertResult Int64)
           return $ SqlInt64 i
instance Convertible SqlValue Int where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral
    safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral 
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral 
    safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral
    safeConvert (SqlUTCTime x) = safeConvert x
    safeConvert (SqlDiffTime x) = safeConvert x
    safeConvert (SqlPOSIXTime x) = safeConvert x
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Int32 SqlValue where
    safeConvert = return . SqlInt32
instance Convertible SqlValue Int32 where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = return x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral
    safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Int64 SqlValue where
    safeConvert = return . SqlInt64
instance Convertible SqlValue Int64 where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = return x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral
    safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Word32 SqlValue where
    safeConvert = return . SqlWord32
instance Convertible SqlValue Word32 where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = return x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral
    safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Word64 SqlValue where
    safeConvert = return . SqlWord64
instance Convertible SqlValue Word64 where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = return x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = viaInteger y fromIntegral
    safeConvert y@(SqlLocalTimeOfDay _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlZonedTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlUTCTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlDiffTime _) = viaInteger y fromIntegral
    safeConvert y@(SqlPOSIXTime _) = viaInteger y fromIntegral
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Integer SqlValue where
    safeConvert = return . SqlInteger
instance Convertible SqlValue Integer where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = return x
    safeConvert (SqlChar x) = safeConvert x
    safeConvert (SqlBool x) = return (if x then 1 else 0)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert (SqlLocalDate x) = return . toModifiedJulianDay $ x
    safeConvert (SqlLocalTimeOfDay x) = 
        return . fromIntegral . fromEnum . timeOfDayToTime $ x
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = 
        return . truncate . utcTimeToPOSIXSeconds . zonedTimeToUTC $ x
    safeConvert (SqlUTCTime x) = safeConvert x
    safeConvert (SqlDiffTime x) = safeConvert x
    safeConvert (SqlPOSIXTime x) = safeConvert x
    safeConvert (SqlEpochTime x) = return x
    safeConvert (SqlTimeDiff x) = return x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Bool SqlValue where
    safeConvert = return . SqlBool
instance Convertible SqlValue Bool where
    safeConvert y@(SqlString x) = 
        case map toUpper x of
          "TRUE" -> Right True
          "T" -> Right True
          "FALSE" -> Right False
          "F" -> Right False
          "0" -> Right False
          "1" -> Right True
          _ -> convError "Cannot parse given String as Bool" y
    safeConvert (SqlByteString x) = (safeConvert . SqlString . BUTF8.toString) x
    safeConvert (SqlInt32 x) = numToBool x
    safeConvert (SqlInt64 x) = numToBool x
    safeConvert (SqlWord32 x) = numToBool x
    safeConvert (SqlWord64 x) = numToBool x
    safeConvert (SqlInteger x) = numToBool x
    safeConvert (SqlChar x) = numToBool (ord x)
    safeConvert (SqlBool x) = return x
    safeConvert (SqlDouble x) = numToBool x
    safeConvert (SqlRational x) = numToBool x
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlZonedTime _) = quickError y
    safeConvert y@(SqlUTCTime _) = quickError y
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = quickError y
    safeConvert (SqlEpochTime x) = numToBool x
    safeConvert (SqlTimeDiff x) = numToBool x
    safeConvert y@(SqlNull) = quickError y
numToBool :: (Eq a, Num a) => a -> ConvertResult Bool
numToBool x = Right (x /= 0)
instance Convertible Char SqlValue where
    safeConvert = return . SqlChar
instance Convertible SqlValue Char where
    safeConvert (SqlString [x]) = return x
    safeConvert y@(SqlString _) = convError "String length /= 1" y
    safeConvert (SqlByteString x) =
          safeConvert . SqlString . BUTF8.toString $ x
    safeConvert y@(SqlInt32 _) = quickError y
    safeConvert y@(SqlInt64 _) = quickError y
    safeConvert y@(SqlWord32 _) = quickError y
    safeConvert y@(SqlWord64 _) = quickError y
    safeConvert y@(SqlInteger _) = quickError y
    safeConvert (SqlChar x) = return x
    safeConvert (SqlBool x) = return (if x then '1' else '0')
    safeConvert y@(SqlDouble _) = quickError y
    safeConvert y@(SqlRational _) = quickError y
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlZonedTime _) = quickError y
    safeConvert y@(SqlUTCTime _) = quickError y
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = quickError y
    safeConvert y@(SqlEpochTime _) = quickError y
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@(SqlNull) = quickError y
instance Convertible Double SqlValue where
    safeConvert = return . SqlDouble
instance Convertible SqlValue Double where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = return . fromIntegral . fromEnum $ x
    safeConvert (SqlBool x) = return (if x then 1.0 else 0.0)
    safeConvert (SqlDouble x) = return x
    safeConvert (SqlRational x) = safeConvert x
    safeConvert y@(SqlLocalDate _) = ((safeConvert y)::ConvertResult Integer) >>= 
                                     (return . fromIntegral)
    safeConvert (SqlLocalTimeOfDay x) = 
        return . fromRational . toRational . timeOfDayToTime $ x
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = 
        safeConvert . SqlUTCTime . zonedTimeToUTC $ x
    safeConvert (SqlUTCTime x) = 
        return . fromRational . toRational . utcTimeToPOSIXSeconds $ x
    safeConvert (SqlDiffTime x) = safeConvert x
    safeConvert (SqlPOSIXTime x) = safeConvert x
    safeConvert (SqlEpochTime x) = safeConvert x
    safeConvert (SqlTimeDiff x) = safeConvert x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Rational SqlValue where
    safeConvert = return . SqlRational
instance Convertible SqlValue Rational where
    safeConvert (SqlString x) = read' x
    safeConvert (SqlByteString x) = (read' . BUTF8.toString) x
    safeConvert (SqlInt32 x) = safeConvert x
    safeConvert (SqlInt64 x) = safeConvert x
    safeConvert (SqlWord32 x) = safeConvert x
    safeConvert (SqlWord64 x) = safeConvert x
    safeConvert (SqlInteger x) = safeConvert x
    safeConvert (SqlChar x) = return . fromIntegral . fromEnum $ x
    safeConvert (SqlBool x) = return $ if x then fromIntegral (1::Int) 
                                       else fromIntegral (0::Int)
    safeConvert (SqlDouble x) = safeConvert x
    safeConvert (SqlRational x) = return x
    safeConvert y@(SqlLocalDate _) = ((safeConvert y)::ConvertResult Integer) >>= 
                                     (return . fromIntegral)
    safeConvert (SqlLocalTimeOfDay x) = return . toRational . timeOfDayToTime $ x
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = safeConvert . SqlUTCTime . zonedTimeToUTC $ x
    safeConvert (SqlUTCTime x) = safeConvert x
    safeConvert (SqlDiffTime x) = safeConvert x
    safeConvert (SqlPOSIXTime x) = safeConvert x
    safeConvert (SqlEpochTime x) = return . fromIntegral $ x
    safeConvert (SqlTimeDiff x) = return . fromIntegral $ x
    safeConvert y@(SqlNull) = quickError y
instance Convertible Day SqlValue where
    safeConvert = return . SqlLocalDate
instance Convertible SqlValue Day where
    safeConvert (SqlString x) = parseTime' (iso8601DateFormat Nothing) x
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert (SqlInt32 x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x}
    safeConvert (SqlInt64 x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x}
    safeConvert (SqlWord32 x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x}
    safeConvert (SqlWord64 x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = fromIntegral x}
    safeConvert (SqlInteger x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = x}
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = 
        return $ ModifiedJulianDay {toModifiedJulianDay = truncate x}
    safeConvert (SqlRational x) = safeConvert . SqlDouble . fromRational $ x
    safeConvert (SqlLocalDate x) = return x
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert (SqlLocalTime x) = return . localDay $ x
    safeConvert y@(SqlZonedTime _) = safeConvert y >>= return . localDay
    safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . localDay
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . localDay
    safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . localDay
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@(SqlNull) = quickError y
instance Convertible TimeOfDay SqlValue where
    safeConvert = return . SqlLocalTimeOfDay
instance Convertible SqlValue TimeOfDay where
    safeConvert (SqlString x) = parseTime' "%T%Q" x
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert (SqlInt32 x) = return . timeToTimeOfDay . fromIntegral $ x
    safeConvert (SqlInt64 x) = return . timeToTimeOfDay . fromIntegral $ x
    safeConvert (SqlWord32 x) = return . timeToTimeOfDay . fromIntegral $ x
    safeConvert (SqlWord64 x) = return . timeToTimeOfDay . fromIntegral $ x
    safeConvert (SqlInteger x) = return . timeToTimeOfDay . fromInteger $ x
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = 
        return . timeToTimeOfDay . fromIntegral $ ((truncate x)::Integer)
    safeConvert (SqlRational x) = safeConvert . SqlDouble . fromRational $ x
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert (SqlLocalTimeOfDay x) = return x
    safeConvert (SqlZonedLocalTimeOfDay tod _) = return tod
    safeConvert (SqlLocalTime x) = return . localTimeOfDay $ x
    safeConvert y@(SqlZonedTime _) = safeConvert y >>= return . localTimeOfDay
    safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . localTimeOfDay
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . localTimeOfDay
    safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . localTimeOfDay
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@SqlNull = quickError y
instance Convertible (TimeOfDay, TimeZone) SqlValue where
    safeConvert (tod, tz) = return (SqlZonedLocalTimeOfDay tod tz)
instance Convertible SqlValue (TimeOfDay, TimeZone) where
    safeConvert (SqlString x) = 
        do tod <- parseTime' "%T%Q %z" x
           tz <- case parseTime defaultTimeLocale "%T%Q %z" x of
                      Nothing -> convError "Couldn't extract timezone in" (SqlString x)
                      Just y -> Right y
           return (tod, tz)
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert y@(SqlInt32 _) = quickError y
    safeConvert y@(SqlInt64 _) = quickError y
    safeConvert y@(SqlWord32 _) = quickError y
    safeConvert y@(SqlWord64 _) = quickError y
    safeConvert y@(SqlInteger _) = quickError y
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert y@(SqlDouble _) = quickError y
    safeConvert y@(SqlRational _) = quickError y
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert (SqlZonedLocalTimeOfDay x y) = return (x, y)
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = return (localTimeOfDay . zonedTimeToLocalTime $ x,
                                           zonedTimeZone x)
    safeConvert y@(SqlUTCTime _) = quickError y
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = quickError y
    safeConvert y@(SqlEpochTime _) = quickError y
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@SqlNull = quickError y
instance Convertible LocalTime SqlValue where
    safeConvert = return . SqlLocalTime
instance Convertible SqlValue LocalTime where
    safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q")) x
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert y@(SqlInt32 _) = quickError y
    safeConvert y@(SqlInt64 _) = quickError y
    safeConvert y@(SqlWord32 _) = quickError y
    safeConvert y@(SqlWord64 _) = quickError y
    safeConvert y@(SqlInteger _) = quickError y
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert y@(SqlDouble _) = quickError y
    safeConvert y@(SqlRational _) = quickError y
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert (SqlLocalTime x) = return x
    safeConvert (SqlZonedTime x) = return . zonedTimeToLocalTime $ x
    safeConvert y@(SqlUTCTime _) = safeConvert y >>= return . zonedTimeToLocalTime
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . zonedTimeToLocalTime
    safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . zonedTimeToLocalTime
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@SqlNull = quickError y
instance Convertible ZonedTime SqlValue where
    safeConvert = return . SqlZonedTime
instance Convertible SqlValue ZonedTime where
    safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q %z")) x
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert (SqlInt32 x) = safeConvert (SqlInteger (fromIntegral x))
    safeConvert (SqlInt64 x) = safeConvert (SqlInteger (fromIntegral x))
    safeConvert (SqlWord32 x) = safeConvert (SqlInteger (fromIntegral x))
    safeConvert (SqlWord64 x) = safeConvert (SqlInteger (fromIntegral x))
    safeConvert y@(SqlInteger _) = safeConvert y >>= return . utcToZonedTime utc
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert y@(SqlDouble _) = safeConvert y >>= return . utcToZonedTime utc
    safeConvert y@(SqlRational _) = safeConvert y >>= return . utcToZonedTime utc
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert (SqlZonedTime x) = return x
    safeConvert (SqlUTCTime x) = return . utcToZonedTime utc $ x
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= return . utcToZonedTime utc
    safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . utcToZonedTime utc
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@SqlNull = quickError y
instance Convertible UTCTime SqlValue where
    safeConvert = return . SqlUTCTime
instance Convertible SqlValue UTCTime where
    safeConvert (SqlString x) = parseTime' (iso8601DateFormat (Just "%T%Q")) x
    safeConvert (SqlByteString x) = safeConvert (SqlString (BUTF8.toString x))
    safeConvert y@(SqlInt32 _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlInt64 _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlWord32 _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlWord64 _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlInteger _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert y@(SqlDouble _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlRational _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = return . zonedTimeToUTC $ x
    safeConvert (SqlUTCTime x) = return x
    safeConvert y@(SqlDiffTime _) = convError "incompatible types (did you mean SqlPOSIXTime?)" y
    safeConvert (SqlPOSIXTime x) = return . posixSecondsToUTCTime $ x
    safeConvert y@(SqlEpochTime _) = safeConvert y >>= return . posixSecondsToUTCTime
    safeConvert y@(SqlTimeDiff _) = convError "incompatible types (did you mean SqlPOSIXTime?)" y
    safeConvert y@SqlNull = quickError y
stringToPico :: String -> ConvertResult Pico
stringToPico s =
    let (base, fracwithdot) = span (/= '.') s
        shortfrac = drop 1 fracwithdot 
        frac = take 12 (rpad 12 '0' shortfrac)
        rpad :: Int -> a -> [a] -> [a]
                
        rpad n c xs = xs ++ replicate (n  length xs) c
        mkPico :: Integer -> Integer -> Pico
                
        mkPico i f = fromInteger i + fromRational (f % 1000000000000)
    in do parsedBase <- read' base
          parsedFrac <- read' frac
          return (mkPico parsedBase parsedFrac)
instance Convertible NominalDiffTime SqlValue where
    safeConvert = return . SqlDiffTime
instance Convertible SqlValue NominalDiffTime where
    safeConvert (SqlString x) = stringToPico x >>= 
                                return . realToFrac
    safeConvert (SqlByteString x) = (stringToPico (BUTF8.toString x)) >>=
                                    return . realToFrac
    safeConvert (SqlInt32 x) = return . fromIntegral $ x
    safeConvert (SqlInt64 x) = return . fromIntegral $ x
    safeConvert (SqlWord32 x) = return . fromIntegral $ x
    safeConvert (SqlWord64 x) = return . fromIntegral $ x
    safeConvert (SqlInteger x) = return . fromIntegral $ x
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = return . fromRational . toRational $ x
    safeConvert (SqlRational x) = return . fromRational $ x
    safeConvert (SqlLocalDate x) = return . fromIntegral . (\y -> y * 60 * 60 * 24) . 
                               toModifiedJulianDay $ x
    safeConvert (SqlLocalTimeOfDay x) = 
        return . fromRational . toRational . timeOfDayToTime $ x
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert (SqlZonedTime x) = return . utcTimeToPOSIXSeconds . zonedTimeToUTC $ x
    safeConvert (SqlUTCTime x) = return . utcTimeToPOSIXSeconds $ x
    safeConvert (SqlDiffTime x) = return x
    safeConvert (SqlPOSIXTime x) = return x
    safeConvert (SqlEpochTime x) = return . fromIntegral $ x
    safeConvert (SqlTimeDiff x) = return . fromIntegral $ x
    safeConvert y@SqlNull = quickError y
instance Convertible ST.ClockTime SqlValue where
    safeConvert x = safeConvert x >>= return . SqlPOSIXTime
instance Convertible SqlValue ST.ClockTime where
    safeConvert (SqlString x) = do r <- read' x
                                   return $ ST.TOD r 0
    safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x
    safeConvert (SqlInt32 x) = return $ ST.TOD (fromIntegral x) 0
    safeConvert (SqlInt64 x) = return $ ST.TOD (fromIntegral x) 0
    safeConvert (SqlWord32 x) = return $ ST.TOD (fromIntegral x) 0
    safeConvert (SqlWord64 x) = return $ ST.TOD (fromIntegral x) 0
    safeConvert (SqlInteger x) = return $ ST.TOD x 0
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = return $ ST.TOD (truncate x) 0
    safeConvert (SqlRational x) = return $ ST.TOD (truncate x) 0
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlZonedTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0)
    safeConvert y@(SqlUTCTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0)
    safeConvert y@(SqlDiffTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = safeConvert y >>= (\z -> return $ ST.TOD z 0)
    safeConvert (SqlEpochTime x) = return $ ST.TOD x 0
    safeConvert y@(SqlTimeDiff _) = quickError y
    safeConvert y@SqlNull = quickError y
instance Convertible ST.TimeDiff SqlValue where
    safeConvert x = safeConvert x >>= return . SqlDiffTime
instance Convertible SqlValue ST.TimeDiff where
    safeConvert y@(SqlString _) = 
        do r <- safeConvert y
           safeConvert (SqlDiffTime r)
    safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x
    safeConvert (SqlInt32 x) = secs2td (fromIntegral x)
    safeConvert (SqlInt64 x) = secs2td (fromIntegral x)
    safeConvert (SqlWord32 x) = secs2td (fromIntegral x)
    safeConvert (SqlWord64 x) = secs2td (fromIntegral x)
    safeConvert (SqlInteger x) = secs2td x
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = secs2td (truncate x)
    safeConvert (SqlRational x) = secs2td (truncate x)
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlZonedTime _) = quickError y
    safeConvert y@(SqlUTCTime _) = quickError y
    safeConvert y@(SqlPOSIXTime _) = quickError y
    safeConvert (SqlDiffTime x) = safeConvert x
    safeConvert y@(SqlEpochTime _) = quickError y
    safeConvert (SqlTimeDiff x) = secs2td x
    safeConvert y@SqlNull = quickError y
instance Convertible DiffTime SqlValue where
    safeConvert = return . SqlDiffTime . fromRational . toRational
instance Convertible SqlValue DiffTime where
    safeConvert (SqlString x) = read' x >>= return . fromInteger
    safeConvert (SqlByteString x) = safeConvert . SqlString . BUTF8.toString $ x
    safeConvert (SqlInt32 x) = return . fromIntegral $ x
    safeConvert (SqlInt64 x) = return . fromIntegral $ x
    safeConvert (SqlWord32 x) = return . fromIntegral $ x
    safeConvert (SqlWord64 x) = return . fromIntegral $ x
    safeConvert (SqlInteger x) = return . fromIntegral $ x
    safeConvert y@(SqlChar _) = quickError y
    safeConvert y@(SqlBool _) = quickError y
    safeConvert (SqlDouble x) = return . fromRational . toRational $ x
    safeConvert (SqlRational x) = return . fromRational $ x
    safeConvert y@(SqlLocalDate _) = quickError y
    safeConvert y@(SqlLocalTimeOfDay _) = quickError y
    safeConvert y@(SqlZonedLocalTimeOfDay _ _) = quickError y
    safeConvert y@(SqlLocalTime _) = quickError y
    safeConvert y@(SqlZonedTime _) = quickError y
    safeConvert y@(SqlUTCTime _) = quickError y
    safeConvert (SqlDiffTime x) = return . fromRational . toRational $ x
    safeConvert y@(SqlPOSIXTime _) = quickError y
    safeConvert y@(SqlEpochTime _) = quickError y
    safeConvert (SqlTimeDiff x) = return . fromIntegral $ x
    safeConvert y@SqlNull = quickError y
instance Convertible ST.CalendarTime SqlValue where
    
    safeConvert x = safeConvert x >>= return . SqlZonedTime
instance Convertible SqlValue ST.CalendarTime where
    
    safeConvert = convertVia (undefined::ZonedTime)
instance (Convertible a SqlValue) => Convertible (Maybe a) SqlValue where
    safeConvert Nothing = return SqlNull
    safeConvert (Just a) = safeConvert a
instance (Convertible SqlValue a) => Convertible SqlValue (Maybe a) where
    safeConvert SqlNull = return Nothing
    safeConvert a = safeConvert a >>= (return . Just)
viaInteger' :: (Convertible SqlValue a, Bounded a, Show a, Convertible a Integer,
               Typeable a) => SqlValue -> (Integer -> ConvertResult a) -> ConvertResult a
viaInteger' sv func = 
    do i <- ((safeConvert sv)::ConvertResult Integer)
       boundedConversion func i
viaInteger :: (Convertible SqlValue a, Bounded a, Show a, Convertible a Integer,
               Typeable a) => SqlValue -> (Integer -> a) -> ConvertResult a
viaInteger sv func = viaInteger' sv (return . func)
secs2td :: Integer -> ConvertResult ST.TimeDiff
secs2td x = safeConvert x
read' :: (Typeable a, Read a, Convertible SqlValue a) => String -> ConvertResult a
read' s = 
    case reads s of
      [(x,"")] -> Right x
      _ -> convError "Cannot read source value as dest type" (SqlString s)
#ifdef __HUGS__
parseTime' :: (Typeable t, Convertible SqlValue t) => String -> String -> ConvertResult t
parseTime' _ inpstr =
    convError "Hugs does not support time parsing" (SqlString inpstr)
#else
parseTime' :: (Typeable t, Convertible SqlValue t, ParseTime t) => String -> String -> ConvertResult t
parseTime' fmtstr inpstr = 
    case parseTime defaultTimeLocale fmtstr inpstr of
      Nothing -> convError ("Cannot parse using default format string " ++ show fmtstr)
                 (SqlString inpstr)
      Just x -> Right x
#endif