module Rel8.Expr.Time
  ( -- * Working with @Day@
    today
  , toDay
  , fromDay
  , addDays
  , diffDays
  , subtractDays

    -- * Working with @UTCTime@
  , now
  , addTime
  , diffTime
  , subtractTime

  -- * Working with @CalendarDiffTime@
  , scaleInterval
  , second, seconds
  , minute, minutes
  , hour, hours
  , day, days
  , week, weeks
  , month, months
  , year, years
  ) where

-- base
import Data.Int ( Int32 )
import Prelude

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Expr.Function ( binaryOperator, nullaryFunction )
import Rel8.Expr.Opaleye ( castExpr, unsafeCastExpr, unsafeLiteral )

-- time
import Data.Time.Calendar ( Day )
import Data.Time.Clock ( UTCTime )
import Data.Time.LocalTime ( CalendarDiffTime )


-- | Corresponds to @date(now())@.
today :: Expr Day
today :: Expr Day
today = Expr UTCTime -> Expr Day
toDay Expr UTCTime
now


-- | Corresponds to calling the @date@ function with a given time.
toDay :: Expr UTCTime -> Expr Day
toDay :: Expr UTCTime -> Expr Day
toDay = Expr UTCTime -> Expr Day
forall b a. Sql DBType b => Expr a -> Expr b
unsafeCastExpr


-- | Corresponds to @x::timestamptz@.
fromDay :: Expr Day -> Expr UTCTime
fromDay :: Expr Day -> Expr UTCTime
fromDay = Expr Day -> Expr UTCTime
forall b a. Sql DBType b => Expr a -> Expr b
unsafeCastExpr


-- | Move forward a given number of days from a particular day.
addDays :: Expr Int32 -> Expr Day -> Expr Day
addDays :: Expr Int32 -> Expr Day -> Expr Day
addDays = (Expr Day -> Expr Int32 -> Expr Day)
-> Expr Int32 -> Expr Day -> Expr Day
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String -> Expr Day -> Expr Int32 -> Expr Day
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"+")


-- | Find the number of days between two days. Corresponds to the @-@ operator.
diffDays :: Expr Day -> Expr Day -> Expr Int32
diffDays :: Expr Day -> Expr Day -> Expr Int32
diffDays = String -> Expr Day -> Expr Day -> Expr Int32
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"-"


-- | Subtract a given number of days from a particular 'Day'. 
subtractDays :: Expr Int32 -> Expr Day -> Expr Day
subtractDays :: Expr Int32 -> Expr Day -> Expr Day
subtractDays = (Expr Day -> Expr Int32 -> Expr Day)
-> Expr Int32 -> Expr Day -> Expr Day
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String -> Expr Day -> Expr Int32 -> Expr Day
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"-")


-- | Corresponds to @now()@.
now :: Expr UTCTime
now :: Expr UTCTime
now = String -> Expr UTCTime
forall a. Sql DBType a => String -> Expr a
nullaryFunction String
"now"


-- | Add a time interval to a point in time, yielding a new point in time.
addTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
addTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
addTime = (Expr UTCTime -> Expr CalendarDiffTime -> Expr UTCTime)
-> Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String -> Expr UTCTime -> Expr CalendarDiffTime -> Expr UTCTime
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"+")


-- | Find the duration between two times.
diffTime :: Expr UTCTime -> Expr UTCTime -> Expr CalendarDiffTime
diffTime :: Expr UTCTime -> Expr UTCTime -> Expr CalendarDiffTime
diffTime = String -> Expr UTCTime -> Expr UTCTime -> Expr CalendarDiffTime
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"-"


-- | Subtract a time interval from a point in time, yielding a new point in time.
subtractTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
subtractTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
subtractTime = (Expr UTCTime -> Expr CalendarDiffTime -> Expr UTCTime)
-> Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String -> Expr UTCTime -> Expr CalendarDiffTime -> Expr UTCTime
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"-")


scaleInterval :: Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
scaleInterval :: Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
scaleInterval = String
-> Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
forall c a b. Sql DBType c => String -> Expr a -> Expr b -> Expr c
binaryOperator String
"*"


-- | An interval of one second.
second :: Expr CalendarDiffTime
second :: Expr CalendarDiffTime
second = String -> Expr CalendarDiffTime
singleton String
"second"


-- | Create a literal interval from a number of seconds.
seconds :: Expr Double -> Expr CalendarDiffTime
seconds :: Expr Double -> Expr CalendarDiffTime
seconds = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
second)


-- | An interval of one minute.
minute :: Expr CalendarDiffTime
minute :: Expr CalendarDiffTime
minute = String -> Expr CalendarDiffTime
singleton String
"minute"


-- | Create a literal interval from a number of minutes.
minutes :: Expr Double -> Expr CalendarDiffTime
minutes :: Expr Double -> Expr CalendarDiffTime
minutes = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
minute)


-- | An interval of one hour.
hour :: Expr CalendarDiffTime
hour :: Expr CalendarDiffTime
hour = String -> Expr CalendarDiffTime
singleton String
"hour"


-- | Create a literal interval from a number of hours.
hours :: Expr Double -> Expr CalendarDiffTime
hours :: Expr Double -> Expr CalendarDiffTime
hours = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
hour)


-- | An interval of one day.
day :: Expr CalendarDiffTime
day :: Expr CalendarDiffTime
day = String -> Expr CalendarDiffTime
singleton String
"day"


-- | Create a literal interval from a number of days.
days ::  Expr Double -> Expr CalendarDiffTime
days :: Expr Double -> Expr CalendarDiffTime
days = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
day)


-- | An interval of one week.
week :: Expr CalendarDiffTime
week :: Expr CalendarDiffTime
week = String -> Expr CalendarDiffTime
singleton String
"week"


-- | Create a literal interval from a number of weeks.
weeks ::  Expr Double -> Expr CalendarDiffTime
weeks :: Expr Double -> Expr CalendarDiffTime
weeks = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
week)


-- | An interval of one month.
month :: Expr CalendarDiffTime
month :: Expr CalendarDiffTime
month = String -> Expr CalendarDiffTime
singleton String
"month"


-- | Create a literal interval from a number of months.
months ::  Expr Double -> Expr CalendarDiffTime
months :: Expr Double -> Expr CalendarDiffTime
months = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
month)


-- | An interval of one year.
year :: Expr CalendarDiffTime
year :: Expr CalendarDiffTime
year = String -> Expr CalendarDiffTime
singleton String
"year"


-- | Create a literal interval from a number of years.
years ::  Expr Double -> Expr CalendarDiffTime
years :: Expr Double -> Expr CalendarDiffTime
years = (Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
`scaleInterval` Expr CalendarDiffTime
year)


singleton :: String -> Expr CalendarDiffTime
singleton :: String -> Expr CalendarDiffTime
singleton String
unit = Expr CalendarDiffTime -> Expr CalendarDiffTime
forall a. Sql DBType a => Expr a -> Expr a
castExpr (Expr CalendarDiffTime -> Expr CalendarDiffTime)
-> Expr CalendarDiffTime -> Expr CalendarDiffTime
forall a b. (a -> b) -> a -> b
$ String -> Expr CalendarDiffTime
forall a. String -> Expr a
unsafeLiteral (String -> Expr CalendarDiffTime)
-> String -> Expr CalendarDiffTime
forall a b. (a -> b) -> a -> b
$ String
"'1 " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
unit String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'"