2 | import Control.Monad.Trans
4 | import public Data.Fin
5 | import public Data.Nat
17 | public export %inline
22 | interface TimeValue a where
23 | (.millis) : UntypedTime -> a
24 | (.seconds) : UntypedTime -> a
25 | (.minutes) : UntypedTime -> a
26 | (.hours) : UntypedTime -> a
27 | (.days) : UntypedTime -> a
30 | (.asMillis) : a -> UntypedTime
31 | (.asSeconds) : a -> UntypedTime
32 | (.asMinutes) : a -> UntypedTime
33 | (.asHours) : a -> UntypedTime
34 | (.asDays) : a -> UntypedTime
37 | (.millisComponent) : a -> Fin 1000
38 | (.secondsComponent) : a -> Fin 60
39 | (.minutesComponent) : a -> Fin 60
40 | (.hoursComponent) : a -> Fin 24
41 | (.daysComponent) : a -> Nat
43 | x.seconds = (1000 * x).millis
44 | x.minutes = (60 * x).seconds
45 | x.hours = (60 * x).minutes
46 | x.days = (24 * x).hours
48 | x.asSeconds = divNatNZ x.asMillis 1000 %search
49 | x.asMinutes = divNatNZ x.asSeconds 60 %search
50 | x.asHours = divNatNZ x.asMinutes 60 %search
51 | x.asDays = divNatNZ x.asHours 24 %search
53 | x.millisComponent = restrict _ $
cast x.asMillis
54 | x.secondsComponent = restrict _ $
cast x.asSeconds
55 | x.minutesComponent = restrict _ $
cast x.asMinutes
56 | x.hoursComponent = restrict _ $
cast x.asHours
57 | x.daysComponent = x.asDays
69 | millis : UntypedTime
72 | TimeValue Time where
74 | (.asMillis) = millis
78 | (==) = (==) `on` (.asMillis)
82 | compare = compare `on` (.asMillis)
88 | namespace FiniteDuration
91 | record FinDuration where
92 | constructor MkFinDuration
93 | millis : UntypedTime
96 | TimeValue FinDuration where
97 | (.millis) = MkFinDuration
98 | (.asMillis) = millis
100 | export %defaulthint
101 | DefaultTimeValue : TimeValue FinDuration
102 | DefaultTimeValue = %search
105 | Eq FinDuration where
106 | (==) = (==) `on` (.asMillis)
109 | Ord FinDuration where
110 | compare = compare `on` (.asMillis)
113 | Semigroup FinDuration where
114 | (<+>) = MkFinDuration .: (+) `on` (.asMillis)
117 | Monoid FinDuration where
118 | neutral = MkFinDuration 0
121 | (*) : Nat -> FinDuration -> FinDuration
122 | n * d = (n * d.asMillis).millis
126 | (/) : FinDuration -> (z : Nat) -> (0 _ : NonZero z) => FinDuration
127 | d / z = (divNatNZ d.asMillis z %search).millis
131 | (-) : FinDuration -> FinDuration -> FinDuration
132 | x - y = (x.asMillis `minus` y.asMillis).millis
140 | (-) : Time -> Time -> FinDuration
141 | x - y = ((x `max` y).asMillis `minus` (x `min` y).asMillis).millis
144 | (+) : Time -> FinDuration -> Time
145 | t + d = (t.asMillis + d.asMillis).millis
151 | timeComponent : Cast v Nat => v -> (pre, descSg, descPl : String) -> List String
152 | timeComponent d pre descSg descPl = let d : Nat := cast d in if d == 0 then [] else
153 | ["\{pre}\{show d}\{if d == 1 then descSg else descPl}"]
155 | joinOr : (ifEmpty : String) -> List (List String) -> List String
156 | joinOr ifEmpty [] = [ ifEmpty ]
157 | joinOr _ xs = join xs
159 | ifNotNull : (List a -> List a) -> List a -> List a
160 | ifNotNull _ [] = []
161 | ifNotNull f xs = f xs
163 | stripFinZeros : String -> String
164 | stripFinZeros = pack . reverse . dropWhile (== '0') . reverse . unpack
166 | namespace FiniteDuration
169 | [Wrds] Interpolation FinDuration where
170 | interpolate tv = joinBy " " $
joinOr "0 sec"
171 | [ timeComponent tv.daysComponent "" " day" " days"
172 | , timeComponent tv.hoursComponent "" " hr" " hr"
173 | , timeComponent tv.minutesComponent "" " min" " min"
174 | , timeComponent tv.secondsComponent "" " sec" " sec"
175 | , timeComponent tv.millisComponent "" " ms" " ms"
179 | [Words] Interpolation FinDuration where
180 | interpolate tv = joinBy ", " $
joinOr "0 seconds"
181 | [ timeComponent tv.daysComponent "" " day" " days"
182 | , timeComponent tv.hoursComponent "" " hour" " hours"
183 | , timeComponent tv.minutesComponent "" " minute" " minutes"
184 | , timeComponent tv.secondsComponent "" " second" " seconds"
185 | , timeComponent tv.millisComponent "" " millisecond" " milliseconds"
189 | [Semicoloned] Interpolation FinDuration where
190 | interpolate tv = concat $
do
191 | let s = tv.secondsComponent
192 | ms = tv.millisComponent
193 | timeComponent tv.daysComponent "" "d" "d" ++
194 | [ padLeft 2 '0' $
show tv.hoursComponent
196 | , padLeft 2 '0' $
show tv.minutesComponent
197 | ] ++ if s == 0 && ms == 0 then [] else
198 | ":" :: padLeft 2 '0' (show s) :: (("." ++) . padLeft 3 '0' <$> timeComponent ms "" "" "")
201 | [ISO8601] Interpolation FinDuration where
203 | let date = [ timeComponent d.daysComponent "" "D" "D" ]
204 | let time = ifNotNull (["T"]::)
205 | [ timeComponent d.hoursComponent "" "H" "H"
206 | , timeComponent d.minutesComponent "" "M" "M"
207 | , let s = d.secondsComponent
208 | ms = d.millisComponent
209 | in if s == 0 && ms == 0 then [] else
210 | show s :: (("." ++) . stripFinZeros . padLeft 3 '0'<$> timeComponent ms "" "" "") ++ ["S"]
212 | strCons 'P' . concat . joinOr "T0S" $
date ++ time
214 | export %defaulthint %inline
215 | DefaultFinDurationInterpolation : Interpolation FinDuration
216 | DefaultFinDurationInterpolation = Wrds
223 | interface Timed m where
224 | currentTime : m Time
227 | interface Timed m => Monad m => CanSleep m where
228 | sleepTill : Time -> m Unit
229 | sleepTill t = sleepFor $
t - !currentTime
231 | sleepFor : FinDuration -> m Unit
232 | sleepFor d = sleepTill $
!currentTime + d
241 | [Trans] Timed m => MonadTrans t => Monad m => Timed (t m) where
242 | currentTime = lift currentTime
247 | [Trans] CanSleep m => MonadTrans t => Monad m => Monad (t m) => CanSleep (t m) using Timed.Trans where
248 | sleepFor = lift . sleepFor
249 | sleepTill = lift . sleepTill
258 | [HasIO] HasIO io => Timed io where
259 | currentTime = liftIO $
(.millis) . fromInteger . millisOfClock <$> clockTime UTC where
260 | millisOfClock : Clock _ -> Integer
261 | millisOfClock (MkClock secs nanos) = secs * 1000 + nanos `div` 1000000
266 | [HasIO] HasIO io => CanSleep io using Timed.HasIO where
268 | sleep $
cast d.asSeconds
269 | let (
msComp ** _)
= toIntWithPrf d.millisComponent
273 | toIntWithPrf : Fin 1000 -> (x : Int ** So (x >= 0))
274 | toIntWithPrf k = (
cast $
finToNat k ** believe_me Oh)
278 | currentTime = currentTime @{HasIO}
282 | sleepFor = sleepFor @{HasIO}