om-time-0.3.0.2: src/OM/Time.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
{- | Basic missing time utilities. -}
module OM.Time (
MonadTimeSpec(..),
Time(..),
timed,
diffTimeSpec,
addTime,
) where
import Control.Monad.Trans.Class (MonadTrans(lift))
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
import Data.Binary (Binary, get, put)
import Data.Int (Int64)
import Data.Time (Day(ModifiedJulianDay), UTCTime(UTCTime), DiffTime)
import System.Clock (TimeSpec)
import qualified System.Clock as Clock
{- | Wrapper around 'UTCTime', used mainly to provide a 'Binary' instance. -}
newtype Time = Time {
unTime :: UTCTime
}
deriving newtype (Eq, Ord, ToJSON, FromJSON, ToJSONKey, FromJSONKey)
instance Show Time where
showsPrec n = showsPrec n . unTime
instance Binary Time where
put (Time (UTCTime (ModifiedJulianDay day) tod)) =
put (day, toRational tod)
get = do
(day, tod) <- get
return (Time (UTCTime (ModifiedJulianDay day) (fromRational tod)))
{- | A monad that can produce the current time as a TimeSpec. -}
class (Monad m) => MonadTimeSpec m where
getTime :: m TimeSpec
{- | The IO instances uses 'Clock.getTime' 'Clock.MonotonicCoarse'. -}
instance MonadTimeSpec IO where
getTime = Clock.getTime Clock.MonotonicCoarse
instance {-# OVERLAPPABLE #-}
( Monad (t m)
, MonadTimeSpec m
, MonadTrans t
)
=>
MonadTimeSpec (t m)
where
getTime = lift getTime
{- | Perform an action and measure how long it takes. -}
timed
:: MonadTimeSpec m
=> m a
-> m (a, DiffTime)
timed action = do
start <- getTime
result <- action
end <- getTime
pure (result, diffTimeSpec end start)
{- | Take the difference of two time specs, as a 'DiffTime'. -}
diffTimeSpec :: TimeSpec -> TimeSpec -> DiffTime
diffTimeSpec a b =
realToFrac (Clock.toNanoSecs (Clock.diffTimeSpec a b)) / 1_000_000_000
{- | Add a 'DiffTime' to a 'TimeSpec'. -}
addTime :: DiffTime -> TimeSpec -> TimeSpec
addTime diff time =
let
rat = toRational diff
secDiff :: Int64
secDiff = truncate rat
nsecDiff :: Int64
nsecDiff = truncate ((toRational diff - toRational secDiff) * 1_000_000_000)
in
Clock.TimeSpec {
Clock.sec = Clock.sec time + secDiff,
Clock.nsec = Clock.nsec time + nsecDiff
}