{-# LANGUAGE OverloadedStrings #-} module Database.RethinkDB.Time where import Data.Text (Text) import qualified Data.Time as Time import qualified Data.Time.Clock.POSIX as Time import Data.Aeson as JSON import Data.Aeson.Types (Parser) import Control.Monad import Control.Applicative import Database.RethinkDB.ReQL import Database.RethinkDB.Protobuf.Ql2.Term.TermType -- | The time and date when the query is executed now :: ReQL now = op NOW () () -- | Build a time object from the year, month, day, hour, minute, second and timezone fields time :: ReQL -> ReQL -> ReQL -> ReQL -> ReQL -> ReQL -> ReQL -> ReQL time y m d hh mm ss tz = op TIME [y, m, d, hh, mm, ss, tz] () -- | Build a time object given the number of seconds since the unix epoch epochTime :: ReQL -> ReQL epochTime t = op EPOCH_TIME [t] () -- | Build a time object given an iso8601 string iso8601 :: ReQL -> ReQL iso8601 t = op ISO8601 [t] () -- | The same time in a different timezone inTimezone :: Expr time => ReQL -> time -> ReQL inTimezone tz t = op IN_TIMEZONE (t, tz) () data Bound a = Open { boundValue :: a } | Closed { boundValue :: a } boundString :: Bound a -> Text boundString Open{} = "open" boundString Closed{} = "closed" -- | Test if a time is between two other times during :: (Expr left, Expr right, Expr time) => Bound left -> Bound right -> time -> ReQL during l r t = op DURING (t, boundValue l, boundValue r) [ "left_bound" := boundString l, "right_bound" := boundString r] -- | Extract part of a time value timezone, date, timeOfDay, year, month, day, dayOfWeek, dayOfYear, hours, minutes, seconds :: Expr time => time -> ReQL timezone t = op TIMEZONE [t] () date t = op DATE [t] () timeOfDay t = op TIME_OF_DAY [t] () year t = op YEAR [t] () month t = op MONTH [t] () day t = op DAY [t] () dayOfWeek t = op DAY_OF_WEEK [t] () dayOfYear t = op DAY_OF_YEAR [t] () hours t = op HOURS [t] () minutes t = op MINUTES [t] () seconds t = op SECONDS [t] () -- | Convert a time to another representation toIso8601, toEpochTime :: Expr t => t -> ReQL toIso8601 t = op TO_ISO8601 [t] () toEpochTime t = op TO_EPOCH_TIME [t] () -- | Time with no time zone -- The default FromJSON instance for Data.Time.UTCTime is incompatible with ReQL's time type newtype UTCTime = UTCTime Time.UTCTime -- | Time with a time zone -- The default FromJSON instance for Data.Time.ZonedTime is incompatible with ReQL's time type newtype ZonedTime = ZonedTime Time.ZonedTime instance FromJSON UTCTime where parseJSON (JSON.Object v) = UTCTime . Time.posixSecondsToUTCTime . fromRational <$> v .: "epoch_time" parseJSON _ = mzero instance FromJSON ZonedTime where parseJSON (JSON.Object v) = do tz <- v .: "timezone" t <- v.: "epoch_time" tz' <- parseTimeZone tz return . ZonedTime $ Time.utcToZonedTime tz' (Time.posixSecondsToUTCTime (fromRational t)) parseJSON _ = mzero parseTimeZone :: String -> Parser Time.TimeZone parseTimeZone "Z" = return Time.utc parseTimeZone tz = Time.minutesToTimeZone <$> case tz of ('-':tz') -> negate <$> go tz' ('+':tz') -> go tz' _ -> go tz where go tz' = do (h, _:m) <- return $ break (==':') tz' ([(hh, "")], [(mm, "")]) <- return $ (reads h, reads m) return $ hh * 60 + mm instance Expr UTCTime where expr (UTCTime t) = expr t instance Expr ZonedTime where expr (ZonedTime t) = expr t