-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathTime.hs
More file actions
191 lines (131 loc) · 4.88 KB
/
Time.hs
File metadata and controls
191 lines (131 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
{-# language OverloadedStrings #-}
module Rel8.Expr.Time
( -- * Working with @Day@
today
, toDay
, fromDay
, addDays
, diffDays
, subtractDays
-- * Working with @UTCTime@
, now
, addTime
, diffTime
, subtractTime
, unsafeExtractFromTime
-- * Working with @CalendarDiffTime@
, scaleInterval
, second, seconds
, minute, minutes
, hour, hours
, day, days
, week, weeks
, month, months
, year, years
, unsafeExtractFromInterval
) where
-- base
import Data.Int ( Int32 )
import Prelude
-- rel8
import Rel8.Expr ( Expr )
import Rel8.Expr.Function ( binaryOperator, function )
import Rel8.Expr.Opaleye ( castExpr, unsafeCastExpr, unsafeLiteral, fromPrimExpr, toPrimExpr )
import Rel8.Type ( DBType )
import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
-- time
import Data.Time.Calendar ( Day )
import Data.Time.Clock ( UTCTime )
import Data.Time.LocalTime ( CalendarDiffTime )
-- | Corresponds to @date(now())@.
today :: Expr Day
today = toDay now
-- | Corresponds to calling the @date@ function with a given time.
toDay :: Expr UTCTime -> Expr Day
toDay = unsafeCastExpr
-- | Corresponds to @x::timestamptz@.
fromDay :: Expr Day -> Expr UTCTime
fromDay = unsafeCastExpr
-- | Move forward a given number of days from a particular day.
addDays :: Expr Int32 -> Expr Day -> Expr Day
addDays = flip (binaryOperator "+")
-- | Find the number of days between two days. Corresponds to the @-@ operator.
diffDays :: Expr Day -> Expr Day -> Expr Int32
diffDays = binaryOperator "-"
-- | Subtract a given number of days from a particular 'Day'.
subtractDays :: Expr Int32 -> Expr Day -> Expr Day
subtractDays = flip (binaryOperator "-")
-- | Corresponds to @now()@.
now :: Expr UTCTime
now = function "now" ()
-- | Add a time interval to a point in time, yielding a new point in time.
addTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
addTime = flip (binaryOperator "+")
-- | Find the duration between two times.
diffTime :: Expr UTCTime -> Expr UTCTime -> Expr CalendarDiffTime
diffTime = binaryOperator "-"
-- | Subtract a time interval from a point in time, yielding a new point in time.
subtractTime :: Expr CalendarDiffTime -> Expr UTCTime -> Expr UTCTime
subtractTime = flip (binaryOperator "-")
-- | Extract a part of a point in time. See possibilities
-- [here](https://www.postgresqltutorial.com/postgresql-date-functions/postgresql-extract/).
-- This function is unsafe because you must decide yourself the output type.
unsafeExtractFromTime :: DBType a => String -> Expr UTCTime -> Expr a
unsafeExtractFromTime name expr =
castExpr $
fromPrimExpr $
Opaleye.FunExpr "EXTRACT" [Opaleye.FunExpr (name <> " FROM") [toPrimExpr expr]]
scaleInterval :: Expr Double -> Expr CalendarDiffTime -> Expr CalendarDiffTime
scaleInterval = binaryOperator "*"
-- | An interval of one second.
second :: Expr CalendarDiffTime
second = singleton "second"
-- | Create a literal interval from a number of seconds.
seconds :: Expr Double -> Expr CalendarDiffTime
seconds = (`scaleInterval` second)
-- | An interval of one minute.
minute :: Expr CalendarDiffTime
minute = singleton "minute"
-- | Create a literal interval from a number of minutes.
minutes :: Expr Double -> Expr CalendarDiffTime
minutes = (`scaleInterval` minute)
-- | An interval of one hour.
hour :: Expr CalendarDiffTime
hour = singleton "hour"
-- | Create a literal interval from a number of hours.
hours :: Expr Double -> Expr CalendarDiffTime
hours = (`scaleInterval` hour)
-- | An interval of one day.
day :: Expr CalendarDiffTime
day = singleton "day"
-- | Create a literal interval from a number of days.
days :: Expr Double -> Expr CalendarDiffTime
days = (`scaleInterval` day)
-- | An interval of one week.
week :: Expr CalendarDiffTime
week = singleton "week"
-- | Create a literal interval from a number of weeks.
weeks :: Expr Double -> Expr CalendarDiffTime
weeks = (`scaleInterval` week)
-- | An interval of one month.
month :: Expr CalendarDiffTime
month = singleton "month"
-- | Create a literal interval from a number of months.
months :: Expr Double -> Expr CalendarDiffTime
months = (`scaleInterval` month)
-- | An interval of one year.
year :: Expr CalendarDiffTime
year = singleton "year"
-- | Create a literal interval from a number of years.
years :: Expr Double -> Expr CalendarDiffTime
years = (`scaleInterval` year)
singleton :: String -> Expr CalendarDiffTime
singleton unit = castExpr $ unsafeLiteral $ "'1 " ++ unit ++ "'"
-- | Extract a part of an interval. See possibilities
-- [here](https://www.postgresqltutorial.com/postgresql-date-functions/postgresql-extract/).
-- This function is unsafe because you must decide yourself the output type.
unsafeExtractFromInterval :: DBType a => String -> Expr CalendarDiffTime -> Expr a
unsafeExtractFromInterval name expr =
castExpr $
fromPrimExpr $
Opaleye.FunExpr "EXTRACT" [Opaleye.FunExpr (name <> " FROM") [toPrimExpr expr]]