-- SPDX-FileCopyrightText: 2019 Serokell <https://serokell.io>
--
-- SPDX-License-Identifier: MPL-2.0

{-# LANGUAGE AllowAmbiguousTypes        #-}
{-# LANGUAGE CPP                        #-}
{-# LANGUAGE ConstraintKinds            #-}
{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE ExplicitForAll             #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs               #-}
{-# LANGUAGE NoStarIsType               #-}
{-# LANGUAGE TypeOperators              #-}
{-# LANGUAGE UndecidableInstances       #-}

-- | This module contains time unit data structures
-- and functions to work with time.

module Time.Units
       ( -- * Time
         Time (..)

         -- ** Time data types
       , Second
       , Millisecond
       , Microsecond
       , Nanosecond
       , Picosecond
       , Minute
       , Hour
       , Day
       , Week
       , Fortnight

       , UnitName
       , KnownUnitName
       , KnownRatName
       , unitNameVal

        -- ** Creation helpers
       , time
       , floorUnit
       , floorRat
       , ceilingUnit
       , ceilingRat
       , toNum
       , toFractional

       , sec
       , ms
       , mcs
       , ns
       , ps

       , minute
       , hour
       , day
       , week
       , fortnight

        -- ** Functions
       , toUnit
       , threadDelay
       , getCPUTime
       , timeout
       ) where

import Control.Monad (unless)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Char (isDigit, isLetter)
import Data.Coerce (coerce)
import Data.Foldable (foldl')
import Data.Proxy (Proxy (..))
import Data.Semigroup (Semigroup (..))
import GHC.Generics (Generic)
import GHC.Natural (Natural)
import GHC.Read (Read (readPrec))
import GHC.Real (denominator, numerator, (%))
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import Text.ParserCombinators.ReadP (ReadP, char, munch1, option, pfail, (+++))
import Text.ParserCombinators.ReadPrec (ReadPrec, lift)

#ifdef HAS_aeson
import Data.Aeson (FromJSON (..), ToJSON (..), withText)
import qualified Data.Text as Text
import Text.Read (readMaybe)
#endif

import Time.Rational (KnownDivRat, KnownRat, Rat, RatioNat, ratVal, type (*), type (/), type (:%))

import qualified Control.Concurrent as Concurrent
import qualified System.CPUTime as CPUTime
import qualified System.Timeout as Timeout

----------------------------------------------------------------------------
-- Units
----------------------------------------------------------------------------

type Second      = 1 / 1
type Millisecond = Second      / 1000
type Microsecond = Millisecond / 1000
type Nanosecond  = Microsecond / 1000
type Picosecond  = Nanosecond  / 1000

type Minute      = 60 * Second
type Hour        = 60 * Minute
type Day         = 24 * Hour
type Week        = 7  * Day
type Fortnight   = 2  * Week

----------------------------------------------------------------------------
-- Time data type
----------------------------------------------------------------------------

-- | Time unit is represented as type level rational multiplier with kind 'Rat'.
newtype Time (rat :: Rat) = Time { forall (rat :: Rat). Time rat -> RatioNat
unTime :: RatioNat }
    deriving (Time rat -> Time rat -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall (rat :: Rat). Time rat -> Time rat -> Bool
/= :: Time rat -> Time rat -> Bool
$c/= :: forall (rat :: Rat). Time rat -> Time rat -> Bool
== :: Time rat -> Time rat -> Bool
$c== :: forall (rat :: Rat). Time rat -> Time rat -> Bool
Eq, Time rat -> Time rat -> Bool
Time rat -> Time rat -> Ordering
Time rat -> Time rat -> Time rat
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall (rat :: Rat). Eq (Time rat)
forall (rat :: Rat). Time rat -> Time rat -> Bool
forall (rat :: Rat). Time rat -> Time rat -> Ordering
forall (rat :: Rat). Time rat -> Time rat -> Time rat
min :: Time rat -> Time rat -> Time rat
$cmin :: forall (rat :: Rat). Time rat -> Time rat -> Time rat
max :: Time rat -> Time rat -> Time rat
$cmax :: forall (rat :: Rat). Time rat -> Time rat -> Time rat
>= :: Time rat -> Time rat -> Bool
$c>= :: forall (rat :: Rat). Time rat -> Time rat -> Bool
> :: Time rat -> Time rat -> Bool
$c> :: forall (rat :: Rat). Time rat -> Time rat -> Bool
<= :: Time rat -> Time rat -> Bool
$c<= :: forall (rat :: Rat). Time rat -> Time rat -> Bool
< :: Time rat -> Time rat -> Bool
$c< :: forall (rat :: Rat). Time rat -> Time rat -> Bool
compare :: Time rat -> Time rat -> Ordering
$ccompare :: forall (rat :: Rat). Time rat -> Time rat -> Ordering
Ord, Int -> Time rat
Time rat -> Int
Time rat -> [Time rat]
Time rat -> Time rat
Time rat -> Time rat -> [Time rat]
Time rat -> Time rat -> Time rat -> [Time rat]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
forall (rat :: Rat). Int -> Time rat
forall (rat :: Rat). Time rat -> Int
forall (rat :: Rat). Time rat -> [Time rat]
forall (rat :: Rat). Time rat -> Time rat
forall (rat :: Rat). Time rat -> Time rat -> [Time rat]
forall (rat :: Rat). Time rat -> Time rat -> Time rat -> [Time rat]
enumFromThenTo :: Time rat -> Time rat -> Time rat -> [Time rat]
$cenumFromThenTo :: forall (rat :: Rat). Time rat -> Time rat -> Time rat -> [Time rat]
enumFromTo :: Time rat -> Time rat -> [Time rat]
$cenumFromTo :: forall (rat :: Rat). Time rat -> Time rat -> [Time rat]
enumFromThen :: Time rat -> Time rat -> [Time rat]
$cenumFromThen :: forall (rat :: Rat). Time rat -> Time rat -> [Time rat]
enumFrom :: Time rat -> [Time rat]
$cenumFrom :: forall (rat :: Rat). Time rat -> [Time rat]
fromEnum :: Time rat -> Int
$cfromEnum :: forall (rat :: Rat). Time rat -> Int
toEnum :: Int -> Time rat
$ctoEnum :: forall (rat :: Rat). Int -> Time rat
pred :: Time rat -> Time rat
$cpred :: forall (rat :: Rat). Time rat -> Time rat
succ :: Time rat -> Time rat
$csucc :: forall (rat :: Rat). Time rat -> Time rat
Enum, forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (rat :: Rat) x. Rep (Time rat) x -> Time rat
forall (rat :: Rat) x. Time rat -> Rep (Time rat) x
$cto :: forall (rat :: Rat) x. Rep (Time rat) x -> Time rat
$cfrom :: forall (rat :: Rat) x. Time rat -> Rep (Time rat) x
Generic)

-- | Addition is associative binary operation for 'Semigroup' of 'Time'.
instance Semigroup (Time (rat :: Rat)) where
    <> :: Time rat -> Time rat -> Time rat
(<>) = coerce :: forall a b. Coercible a b => a -> b
coerce (forall a. Num a => a -> a -> a
(+) :: RatioNat -> RatioNat -> RatioNat)
    {-# INLINE (<>) #-}
    sconcat :: NonEmpty (Time rat) -> Time rat
sconcat = forall (t :: Type -> Type) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall a. Semigroup a => a -> a -> a
(<>) forall a. Monoid a => a
mempty
    {-# INLINE sconcat #-}
    stimes :: forall b. Integral b => b -> Time rat -> Time rat
stimes b
n (Time RatioNat
t) = forall (rat :: Rat). RatioNat -> Time rat
Time (forall a b. (Integral a, Num b) => a -> b
fromIntegral b
n forall a. Num a => a -> a -> a
* RatioNat
t)
    {-# INLINE stimes #-}

instance Monoid (Time (rat :: Rat)) where
    mempty :: Time rat
mempty  = forall (rat :: Rat). RatioNat -> Time rat
Time RatioNat
0
    {-# INLINE mempty #-}
    mappend :: Time rat -> Time rat -> Time rat
mappend = forall a. Semigroup a => a -> a -> a
(<>)
    {-# INLINE mappend #-}
    mconcat :: [Time rat] -> Time rat
mconcat = forall (t :: Type -> Type) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall a. Semigroup a => a -> a -> a
(<>) forall a. Monoid a => a
mempty
    {-# INLINE mconcat #-}

#ifdef HAS_aeson
instance (KnownUnitName unit) => ToJSON (Time (unit :: Rat)) where
    toJSON = toJSON . show

instance (KnownUnitName unit) => FromJSON (Time (unit :: Rat)) where
    parseJSON = withText "time" $ maybe parseFail pure . maybeTime
      where
        parseFail = fail $ "Can not parse Time. Expected unit: " ++ unitNameVal @unit
        maybeTime = readMaybe @(Time unit) . Text.unpack
#endif

-- | Type family for prettier 'show' of time units.
type family UnitName (unit :: Rat) :: Symbol

type instance UnitName (1 :% 1)             = "s"   -- second unit
type instance UnitName (1 :% 1000)          = "ms"  -- millisecond unit
type instance UnitName (1 :% 1000000)       = "mcs" -- microsecond unit
type instance UnitName (1 :% 1000000000)    = "ns"  -- nanosecond unit
type instance UnitName (1 :% 1000000000000) = "ps"  -- picosecond unit

type instance UnitName (60      :% 1) = "m"  -- minute unit
type instance UnitName (3600    :% 1) = "h"  -- hour unit
type instance UnitName (86400   :% 1) = "d"  -- day unit
type instance UnitName (604800  :% 1) = "w"  -- week unit
type instance UnitName (1209600 :% 1) = "fn" -- fortnight unit

-- | Constraint alias for 'KnownSymbol' 'UnitName'.
type KnownUnitName unit = KnownSymbol (UnitName unit)

-- | Constraint alias for 'KnownUnitName' and 'KnownRat' for time unit.
type KnownRatName unit = (KnownUnitName unit, KnownRat unit)

-- | Returns type-level 'Symbol' of the time unit converted to 'String'.
unitNameVal :: forall (unit :: Rat) . (KnownUnitName unit) => String
unitNameVal :: forall (unit :: Rat). KnownUnitName unit => String
unitNameVal = forall (n :: Symbol) (proxy :: Symbol -> Type).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @(UnitName unit))

instance KnownUnitName unit => Show (Time unit) where
    showsPrec :: Int -> Time unit -> ShowS
showsPrec Int
p (Time RatioNat
t) = Bool -> ShowS -> ShowS
showParen (Int
p forall a. Ord a => a -> a -> Bool
> Int
6)
                              forall a b. (a -> b) -> a -> b
$ forall {a}. (Integral a, Show a) => Ratio a -> ShowS
showsMixed RatioNat
t
                              forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString (forall (unit :: Rat). KnownUnitName unit => String
unitNameVal @unit)
      where
        showsMixed :: Ratio a -> ShowS
showsMixed Ratio a
0 = String -> ShowS
showString String
"0"
        showsMixed Ratio a
rat =
          let (a
n,a
d) = (forall a. Ratio a -> a
numerator Ratio a
rat, forall a. Ratio a -> a
denominator Ratio a
rat)
              (a
q,a
r) = a
n forall a. Integral a => a -> a -> (a, a)
`quotRem` a
d
              op :: String
op = if a
q forall a. Eq a => a -> a -> Bool
== a
0 Bool -> Bool -> Bool
|| a
r forall a. Eq a => a -> a -> Bool
== a
0 then String
"" else String
"+"
              quotStr :: ShowS
quotStr = if a
q forall a. Eq a => a -> a -> Bool
== a
0
                          then forall a. a -> a
id -- NB id === showString ""
                          else forall a. Show a => a -> ShowS
shows a
q
              remStr :: ShowS
remStr = if a
r forall a. Eq a => a -> a -> Bool
== a
0
                         then forall a. a -> a
id
                         else forall a. Show a => a -> ShowS
shows a
r
                            forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
"/"
                            forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> ShowS
shows a
d
          in
              ShowS
quotStr forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
op forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
remStr

instance KnownUnitName unit => Read (Time unit) where
    readPrec :: ReadPrec (Time unit)
    readPrec :: ReadPrec (Time unit)
readPrec = forall a. ReadP a -> ReadPrec a
lift ReadP (Time unit)
readP
      where
        readP :: ReadP (Time unit)
        readP :: ReadP (Time unit)
readP = do
            let naturalP :: ReadP Natural
naturalP = forall a. Read a => String -> a
read forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> (Char -> Bool) -> ReadP String
munch1 Char -> Bool
isDigit
            -- If a '+' is parsed as part of a mixed fraction, the other parts
            -- are no longer optional.  This separation is required to prevent
            -- e.g. "3+2" successfully parsing.
            let fullMixedExpr :: ReadP (Natural, Natural, Natural)
fullMixedExpr = (,,) forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> (ReadP Natural
naturalP forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f a
<* Char -> ReadP Char
char Char
'+')
                                     forall (f :: Type -> Type) a b.
Applicative f =>
f (a -> b) -> f a -> f b
<*> (ReadP Natural
naturalP forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f a
<* Char -> ReadP Char
char Char
'/')
                                     forall (f :: Type -> Type) a b.
Applicative f =>
f (a -> b) -> f a -> f b
<*> ReadP Natural
naturalP
            let improperExpr :: ReadP (Natural, Natural, Natural)
improperExpr = (,,) Natural
0 forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadP Natural
naturalP
                                      forall (f :: Type -> Type) a b.
Applicative f =>
f (a -> b) -> f a -> f b
<*> forall a. a -> ReadP a -> ReadP a
option Natural
1 (Char -> ReadP Char
char Char
'/' forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f b
*> ReadP Natural
naturalP)
            (Natural
q,Natural
r,Natural
d) <- ReadP (Natural, Natural, Natural)
fullMixedExpr forall a. ReadP a -> ReadP a -> ReadP a
+++ ReadP (Natural, Natural, Natural)
improperExpr
            let n :: Natural
n = (Natural
q forall a. Num a => a -> a -> a
* Natural
d forall a. Num a => a -> a -> a
+ Natural
r)
            String
timeUnitStr <- (Char -> Bool) -> ReadP String
munch1 Char -> Bool
isLetter
            forall (f :: Type -> Type). Applicative f => Bool -> f () -> f ()
unless (String
timeUnitStr forall a. Eq a => a -> a -> Bool
== forall (unit :: Rat). KnownUnitName unit => String
unitNameVal @unit) forall a. ReadP a
pfail
            forall (f :: Type -> Type) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (rat :: Rat). RatioNat -> Time rat
Time (Natural
n forall a. Integral a => a -> a -> Ratio a
% Natural
d)

----------------------------------------------------------------------------
-- Creation helpers
----------------------------------------------------------------------------

-- | Creates 'Time' of some type from given 'Natural'.
time :: RatioNat -> Time unit
time :: forall (rat :: Rat). RatioNat -> Time rat
time RatioNat
n = forall (rat :: Rat). RatioNat -> Time rat
Time RatioNat
n
{-# INLINE time #-}

-- | Creates 'Second' from given 'Natural'.
--
-- >>> sec 42
-- 42s
sec :: RatioNat -> Time Second
sec :: RatioNat -> Time Second
sec = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE sec #-}

-- | Creates 'Millisecond' from given 'Natural'.
--
-- >>> ms 42
-- 42ms
ms :: RatioNat -> Time Millisecond
ms :: RatioNat -> Time Millisecond
ms = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE ms #-}

-- | Creates 'Microsecond' from given 'Natural'.
--
-- >>> mcs 42
-- 42mcs
mcs :: RatioNat -> Time Microsecond
mcs :: RatioNat -> Time Microsecond
mcs = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE mcs #-}

-- | Creates 'Nanosecond' from given 'Natural'.
--
-- >>> ns 42
-- 42ns
ns :: RatioNat -> Time Nanosecond
ns :: RatioNat -> Time Nanosecond
ns = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE ns #-}

-- | Creates 'Picosecond' from given 'Natural'.
--
-- >>> ps 42
-- 42ps
ps :: RatioNat -> Time Picosecond
ps :: RatioNat -> Time Picosecond
ps = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE ps #-}

-- | Creates 'Minute' from given 'Natural'.
--
-- >>> minute 42
-- 42m
minute :: RatioNat -> Time Minute
minute :: RatioNat -> Time Minute
minute = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE minute #-}

-- | Creates 'Hour' from given 'Natural'.
--
-- >>> hour 42
-- 42h
hour :: RatioNat -> Time Hour
hour :: RatioNat -> Time Hour
hour = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE hour #-}

-- | Creates 'Day' from given 'Natural'.
--
-- >>> day 42
-- 42d
day :: RatioNat -> Time Day
day :: RatioNat -> Time Day
day = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE day #-}

-- | Creates 'Week' from given 'Natural'.
--
-- >>> week 42
-- 42w
week :: RatioNat -> Time Week
week :: RatioNat -> Time Week
week = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE week #-}

-- | Creates 'Fortnight' from given 'Natural'.
--
-- >>> fortnight 42
-- 42fn
fortnight :: RatioNat -> Time Fortnight
fortnight :: RatioNat -> Time Fortnight
fortnight = forall (rat :: Rat). RatioNat -> Time rat
time
{-# INLINE fortnight #-}

-- | Returns the greatest integer not greater than given 'Time'.
floorRat :: forall b (unit :: Rat) . Integral b => Time unit -> b
floorRat :: forall b (unit :: Rat). Integral b => Time unit -> b
floorRat = forall a b. (RealFrac a, Integral b) => a -> b
floor forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (rat :: Rat). Time rat -> RatioNat
unTime

{- | Similar to 'floor', but works with 'Time' units.

>>> floorUnit @Day (Time $ 5 % 2)
2d

>>> floorUnit (Time @Second $ 2 % 3)
0s

>>> floorUnit $ ps 42
42ps

-}
floorUnit :: forall (unit :: Rat) . Time unit -> Time unit
floorUnit :: forall (rat :: Rat). Time rat -> Time rat
floorUnit = forall (rat :: Rat). RatioNat -> Time rat
time forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral @Natural forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b (unit :: Rat). Integral b => Time unit -> b
floorRat

-- | Returns the smallest integer greater than or equal to the given 'Time'.
--
-- @since 1.3.0
ceilingRat :: forall b (unit :: Rat) . (Integral b) => Time unit -> b
ceilingRat :: forall b (unit :: Rat). Integral b => Time unit -> b
ceilingRat = forall a b. (RealFrac a, Integral b) => a -> b
ceiling forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (rat :: Rat). Time rat -> RatioNat
unTime

{- | Similar to 'ceiling', but works with 'Time' units.

>>> ceilingUnit @Day (Time $ 5 % 2)
3d

>>> ceilingUnit (Time @Second $ 2 % 3)
1s

>>> ceilingUnit $ ps 42
42ps

@since 1.3.0
-}
ceilingUnit :: forall (unit :: Rat) . Time unit -> Time unit
ceilingUnit :: forall (rat :: Rat). Time rat -> Time rat
ceilingUnit = forall (rat :: Rat). RatioNat -> Time rat
time forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral @Natural forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b (unit :: Rat). Integral b => Time unit -> b
ceilingRat

{- | Convert time to the 'Num' in given units.

For example, instead of writing

@
foo :: POSIXTime
foo = 10800  -- 3 hours
@

one can write more safe implementation:

@
foo = toNum @Second $ hour 3
@

__Examples:__

>>> toNum @Second @Natural $ hour 3
10800

>>> toNum @Minute @Int $ hour 3
180

>>> toNum @Hour @Natural $ hour 3
3

-}
toNum :: forall (unitTo :: Rat) n (unit :: Rat) . (KnownDivRat unit unitTo, Num n)
      => Time unit -> n
toNum :: forall (unitTo :: Rat) n (unit :: Rat).
(KnownDivRat unit unitTo, Num n) =>
Time unit -> n
toNum = forall a b. (Integral a, Num b) => a -> b
fromIntegral @Natural forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b (unit :: Rat). Integral b => Time unit -> b
floorRat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (unitTo :: Rat) (unitFrom :: Rat).
KnownDivRat unitFrom unitTo =>
Time unitFrom -> Time unitTo
toUnit @unitTo
{-# DEPRECATED toNum
  [ "May lead to unexpected flooring of the fractional time."
  , "Use 'toFractional' to avoid rounding or 'floorRat' to keep the flooring behaviour."
  ] #-}

{- | Convert the 'Time' object to the 'Fractional' value.

__Examples:__

>>> toFractional @Rational $ hour (1 % 8)
1 % 8

>>> toFractional @Double $ hour (1 % 8)
0.125

@since 1.3.0
-}
toFractional :: forall r (unit :: Rat) . Fractional r => Time unit -> r
toFractional :: forall r (unit :: Rat). Fractional r => Time unit -> r
toFractional = forall a. Fractional a => Rational -> a
fromRational forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Real a => a -> Rational
toRational forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (rat :: Rat). Time rat -> RatioNat
unTime

----------------------------------------------------------------------------
-- Functional
----------------------------------------------------------------------------

{- | Converts from one time unit to another time unit.

>>> toUnit @Hour (minute 120)
2h

>>> toUnit @Second (ms 7)
7/1000s

>>> toUnit @Week (Time @Day 45)
6+3/7w

>>> toUnit @Second @Minute (Time 3)
180s

>>> toUnit (day 42000000) :: Time Second
3628800000000s

-}
toUnit :: forall (unitTo :: Rat) (unitFrom :: Rat) . KnownDivRat unitFrom unitTo
       => Time unitFrom
       -> Time unitTo
toUnit :: forall (unitTo :: Rat) (unitFrom :: Rat).
KnownDivRat unitFrom unitTo =>
Time unitFrom -> Time unitTo
toUnit Time{RatioNat
unTime :: RatioNat
unTime :: forall (rat :: Rat). Time rat -> RatioNat
..} = forall (rat :: Rat). RatioNat -> Time rat
Time forall a b. (a -> b) -> a -> b
$ RatioNat
unTime forall a. Num a => a -> a -> a
* forall (r :: Rat). KnownRat r => RatioNat
ratVal @(unitFrom / unitTo)
{-# INLINE toUnit #-}

{- | Convenient version of 'Control.Concurrent.threadDelay' which takes
 any time-unit and operates in any MonadIO.


@
__>>> threadDelay $ sec 2__
__>>> threadDelay (2 :: Time Second)__
__>>> threadDelay @Second 2__
@

-}
threadDelay :: forall (unit :: Rat) m . (KnownDivRat unit Microsecond, MonadIO m)
            => Time unit
            -> m ()
threadDelay :: forall (unit :: Rat) (m :: Type -> Type).
(KnownDivRat unit Microsecond, MonadIO m) =>
Time unit -> m ()
threadDelay = forall (m :: Type -> Type) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> IO ()
Concurrent.threadDelay forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b (unit :: Rat). Integral b => Time unit -> b
floorRat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (unitTo :: Rat) (unitFrom :: Rat).
KnownDivRat unitFrom unitTo =>
Time unitFrom -> Time unitTo
toUnit @Microsecond
{-# INLINE threadDelay #-}

-- | Similar to 'CPUTime.getCPUTime' but returns the CPU time used by the current
-- program in the given time unit.
-- The precision of this result is implementation-dependent.
--
-- @
-- __>>> getCPUTime @Second__
-- 1064046949/1000000000s
-- @
--
getCPUTime :: forall (unit :: Rat) m . (KnownDivRat Picosecond unit, MonadIO m)
           => m (Time unit)
getCPUTime :: forall (unit :: Rat) (m :: Type -> Type).
(KnownDivRat Picosecond unit, MonadIO m) =>
m (Time unit)
getCPUTime = forall (unitTo :: Rat) (unitFrom :: Rat).
KnownDivRat unitFrom unitTo =>
Time unitFrom -> Time unitTo
toUnit forall b c a. (b -> c) -> (a -> b) -> a -> c
. RatioNat -> Time Picosecond
ps forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => Integer -> a
fromInteger forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: Type -> Type) a. MonadIO m => IO a -> m a
liftIO IO Integer
CPUTime.getCPUTime
{-# INLINE getCPUTime #-}

{- | Similar to 'Timeout.timeout' but receiving any time unit
instead of number of microseconds.

@
__>>> timeout (sec 1) (putStrLn "Hello O'Clock")__
Hello O'Clock
Just ()
@

@
__>>> timeout (ps 1) (putStrLn "Hello O'Clock")__
Nothing
@

@
__>>> timeout (mcs 1) (putStrLn "Hello O'Clock")__
HellNothing
@

-}
timeout :: forall (unit :: Rat) m a . (MonadIO m, KnownDivRat unit Microsecond)
        => Time unit   -- ^ time
        -> IO a        -- ^ 'IO' action
        -> m (Maybe a) -- ^ returns 'Nothing' if no result is available within the given time
timeout :: forall (unit :: Rat) (m :: Type -> Type) a.
(MonadIO m, KnownDivRat unit Microsecond) =>
Time unit -> IO a -> m (Maybe a)
timeout Time unit
t = forall (m :: Type -> Type) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Int -> IO a -> IO (Maybe a)
Timeout.timeout (forall b (unit :: Rat). Integral b => Time unit -> b
floorRat forall a b. (a -> b) -> a -> b
$ forall (unitTo :: Rat) (unitFrom :: Rat).
KnownDivRat unitFrom unitTo =>
Time unitFrom -> Time unitTo
toUnit @Microsecond Time unit
t)
{-# INLINE timeout #-}