-- | An interface for /clocks/. Since there are many ways to calculate different -- ideas of /time/, e.g. system time, wall clock time or stream time, a type -- class 'Clock' is provided with the basic interface for getting the current -- time and comparing time stamps. module Data.MediaBus.Basics.Clock ( IsClock(..) , timeSince , UtcClock(..) , useUtcClock , utcClockTimeDiff ) where import Control.DeepSeq import Control.Lens import Control.Monad.IO.Class import Data.Default import Data.Function (on) import Data.Kind import Data.MediaBus.Basics.Monotone import Data.Proxy import Data.Time.Calendar import Data.Time.Clock import Data.Word import GHC.Generics (Generic) import Test.QuickCheck -- * Clocks -- | Clocks can generate reference times, and they can convert these to tickss. Tickss are mere integrals class ( Default (ClockTimeDiff c) , Ord (ClockTimeDiff c) , Eq (ClockTimeDiff c) , Num (ClockTimeDiff c) , Show (ClockTime c) , Eq (ClockTime c) , Show (ClockTimeDiff c) , LocalOrd (ClockTimeDiff c) ) => IsClock c where data ClockTime c data ClockTimeDiff c type MonadClock c (m :: Type -> Type) :: Constraint -- | Return the current absolute time as a 'ClockTime' now :: MonadClock c m => m (ClockTime c) -- | Convert a 'ClockTime' to a 'ClockTimeDiff' by 'diffTime'ing the time -- with the beginning of time. timeAsTimeDiff :: ClockTime c -> ClockTimeDiff c -- | The time difference (a 'ClockTimeDiff') between two 'ClockTime's diffTime :: ClockTime c -> ClockTime c -> ClockTimeDiff c -- | Add the relative time to an absolute time to obtain a new absolute -- time. timeAddTimeDiff :: ClockTime c -> ClockTimeDiff c -> ClockTime c -- | Return the time elapsed between since the given absolute time. timeSince :: (IsClock c, MonadClock c m, Monad m) => ClockTime c -> m (ClockTimeDiff c) timeSince t0 = do t1 <- now return (diffTime t1 t0) -- * UTC System Time Clock -- | A UTC System Time Clock data UtcClock = MkUtcClock deriving (Generic) instance NFData UtcClock -- | A 'Proxy' for 'UtcClock' useUtcClock :: Proxy UtcClock useUtcClock = Proxy instance IsClock UtcClock where newtype ClockTime UtcClock = MkUtcClockTime{_utcClockTime :: UTCTime} deriving (Eq, Generic) newtype ClockTimeDiff UtcClock = MkUtcClockTimeDiff{_utcClockTimeDiff :: NominalDiffTime} deriving (Ord, Eq, Num, Generic) type MonadClock UtcClock m = MonadIO m now = MkUtcClockTime <$> liftIO getCurrentTime timeAsTimeDiff (MkUtcClockTime ref) = MkUtcClockTimeDiff $ diffUTCTime ref $ UTCTime (toEnum 0) 0 timeAddTimeDiff (MkUtcClockTime t) (MkUtcClockTimeDiff dt) = MkUtcClockTime (addUTCTime dt t) diffTime (MkUtcClockTime later) (MkUtcClockTime sooner) = MkUtcClockTimeDiff $ diffUTCTime later sooner instance NFData (ClockTime UtcClock) instance Show (ClockTime UtcClock) where show (MkUtcClockTime !t) = show t instance Show (ClockTimeDiff UtcClock) where showsPrec d (MkUtcClockTimeDiff t) = showParen (d > 10) $ showString "Δt " . showsPrec 11 t instance NFData (ClockTimeDiff UtcClock) instance Default (ClockTimeDiff UtcClock) where def = MkUtcClockTimeDiff $ fromInteger def instance Arbitrary (ClockTime UtcClock) where arbitrary = MkUtcClockTime <$> (UTCTime <$> (ModifiedJulianDay <$> arbitrary) <*> (fromInteger <$> arbitrary)) instance Arbitrary (ClockTimeDiff UtcClock) where arbitrary = MkUtcClockTimeDiff . fromInteger <$> arbitrary -- | An 'Iso' for converting a 'UtcClock' 'ClockTimeDiff' from/to a 'NominalDiffTime'. utcClockTimeDiff :: Iso' (ClockTimeDiff UtcClock) NominalDiffTime utcClockTimeDiff = iso _utcClockTimeDiff MkUtcClockTimeDiff instance LocalOrd (ClockTimeDiff UtcClock) where succeeds = succeeds `on` roundToSeconds where roundToSeconds = round . (/ 1000000000000) . _utcClockTimeDiff roundToSeconds :: ClockTimeDiff UtcClock -> Word64