Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for datetimeoffset data type #26

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Database/ODBC/Conversion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ instance FromValue LocalTime where
LocalTimeValue x -> pure (id x)
v -> Left ("Expected LocalTime, but got: " ++ show v))

instance FromValue ZonedTime where
fromValue =
(\case
ZonedTimeValue lt tz -> pure (ZonedTime lt tz)
v -> Left ("Expected ZonedTime, but got: " ++ show v))

--------------------------------------------------------------------------------
-- Producing rows

Expand Down
27 changes: 26 additions & 1 deletion src/Database/ODBC/SQLServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module Database.ODBC.SQLServer
, Internal.Binary(..)
, Datetime2(..)
, Smalldatetime(..)
, Datetimeoffset(..)

-- * Streaming results
-- $streaming
Expand Down Expand Up @@ -257,6 +258,22 @@ newtype Smalldatetime = Smalldatetime
{ unSmalldatetime :: LocalTime
} deriving (Eq, Ord, Show, Typeable, Generic, Data, FromValue)

newtype Datetimeoffset = Datetimeoffset
{ unDatetimeoffset :: ZonedTime
} deriving (Show, Typeable, Generic, Data, FromValue)

-- SQL Server considers two datetimeoffset values to be equal as long as they
-- represent the same instant in time; i.e. they are equavalent to the same UTC
-- time and date. This instance reproduces that behaviour.
instance Eq Datetimeoffset where
Datetimeoffset x == Datetimeoffset y = zonedTimeToUTC x == zonedTimeToUTC y

-- SQL Server considers datetimeoffset values to be ordered according to their
-- UTC equivalent values. This instance reproduces that behaviour.
instance Ord Datetimeoffset where
compare (Datetimeoffset x) (Datetimeoffset y) =
compare (zonedTimeToUTC x) (zonedTimeToUTC y)

chrisdone marked this conversation as resolved.
Show resolved Hide resolved
--------------------------------------------------------------------------------
-- Conversion to SQL

Expand Down Expand Up @@ -376,6 +393,9 @@ instance ToSql Smalldatetime where
shrink (LocalTime dd (TimeOfDay hh mm _ss)) =
LocalTime dd (TimeOfDay hh mm 0)

instance ToSql Datetimeoffset where
toSql (Datetimeoffset (ZonedTime lt tzone)) = toSql $ ZonedTimeValue lt tzone

--------------------------------------------------------------------------------
-- Top-level functions

Expand Down Expand Up @@ -512,7 +532,12 @@ renderFractional x = trim (printf "%.7f" (realToFrac x :: Double) :: String)
s' -> s')

renderTimeZone :: TimeZone -> String
renderTimeZone tzone = if tzone == utc then "Z" else timeZoneOffsetString tzone
renderTimeZone (TimeZone 0 _ _) = "Z"
renderTimeZone (TimeZone t _ _) | t < 0 = '-' : renderTimeZone' (negate t)
renderTimeZone (TimeZone t _ _) = '+' : renderTimeZone' t

renderTimeZone' :: Int -> String
renderTimeZone' t = printf "%02d:%02d" (t `div` 60) (t `mod` 60)
chrisdone marked this conversation as resolved.
Show resolved Hide resolved

-- | A very conservative character escape.
escapeChar8 :: Word8 -> Text
Expand Down
9 changes: 8 additions & 1 deletion test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import Data.Word
import Database.ODBC.Conversion (FromValue(..))
import Database.ODBC.Internal (Value (..), Connection, ODBCException(..), Step(..), Binary)
import qualified Database.ODBC.Internal as Internal
import Database.ODBC.SQLServer (Datetime2(..), Smalldatetime(..), ToSql(..))
import Database.ODBC.SQLServer (Datetime2(..), Smalldatetime(..), Datetimeoffset(..), ToSql(..))
import qualified Database.ODBC.SQLServer as SQLServer
import Database.ODBC.TH (partsParser, Part(..))
import System.Environment
Expand Down Expand Up @@ -146,6 +146,7 @@ conversionTo = do
quickCheckRoundtrip @Datetime2 "Datetime2" "datetime2"
quickCheckRoundtrip @Smalldatetime "Smalldatetime" "smalldatetime"
quickCheckRoundtrip @TestDateTime "TestDateTime" "datetime"
quickCheckRoundtrip @Datetimeoffset "Datetimeoffset" "datetimeoffset"
chrisdone marked this conversation as resolved.
Show resolved Hide resolved
quickCheckOneway @TimeOfDay "TimeOfDay" "time"
quickCheckRoundtrip @TestTimeOfDay "TimeOfDay" "time"
quickCheckRoundtrip @Float "Float" "real"
Expand Down Expand Up @@ -678,3 +679,9 @@ instance Arbitrary Smalldatetime where
pure
(Smalldatetime
(LocalTime day (timeToTimeOfDay (secondsToDiffTime (minutes * 60)))))

instance Arbitrary Datetimeoffset where
arbitrary = do
lt <- arbitrary
offset <- choose (-12 * 60, 14 * 60)
chrisdone marked this conversation as resolved.
Show resolved Hide resolved
return $ Datetimeoffset $ ZonedTime lt $ TimeZone offset False ""