{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | Provides a dynamic effect for "Data.Time".
--
-- @since 0.1
module Effectful.Time.Static
  ( -- * Effect
    Time,
    getSystemTime,
    getSystemZonedTime,
    getTimeZone,
    utcToLocalZonedTime,
    loadLocalTZ,
    getMonotonicTime,

    -- ** Handlers
    runTime,

    -- * Timing
    withTiming,
    withTiming_,

    -- ** TimeSpec
    TimeSpec (..),

    -- *** Creation
    TimeSpec.fromSeconds,
    TimeSpec.fromNanoSeconds,

    -- *** Elimination
    TimeSpec.toSeconds,
    TimeSpec.toNanoSeconds,

    -- *** Operations
    TimeSpec.diffTimeSpec,
    TimeSpec.normalizeTimeSpec,

    -- * Formatting
    Utils.formatLocalTime,
    Utils.formatZonedTime,

    -- * Parsing
    Utils.parseLocalTime,
    Utils.parseZonedTime,

    -- * Misc
    getSystemTimeString,
    getSystemZonedTimeString,

    -- * Re-exports
    LocalTime (..),
    ZonedTime (..),
  )
where

import Data.Time.Clock (UTCTime)
import Data.Time.LocalTime
  ( LocalTime (LocalTime, localDay, localTimeOfDay),
    TimeZone,
    ZonedTime (ZonedTime, zonedTimeToLocalTime, zonedTimeZone),
  )
import Data.Time.LocalTime qualified as Local
import Data.Time.Zones (TZ)
import Data.Time.Zones qualified as TZ
import Effectful
  ( Dispatch (Static),
    DispatchOf,
    Eff,
    Effect,
    IOE,
    type (:>),
  )
import Effectful.Dispatch.Static
  ( HasCallStack,
    SideEffects (WithSideEffects),
    StaticRep,
    evalStaticRep,
    unsafeEff_,
  )
import Effectful.Time.TimeSpec (TimeSpec (nsec, sec))
import Effectful.Time.TimeSpec qualified as TimeSpec
import Effectful.Time.Utils qualified as Utils
import GHC.Clock qualified as C

-- | Static time effect.
--
-- @since 0.1
data Time :: Effect

type instance DispatchOf Time = Static WithSideEffects

data instance StaticRep Time = MkTime

-- | Runs a 'Time' effect in IO.
--
-- @since 0.1
runTime :: (HasCallStack, IOE :> es) => Eff (Time : es) a -> Eff es a
runTime :: forall (es :: [Effect]) a.
(HasCallStack, IOE :> es) =>
Eff (Time : es) a -> Eff es a
runTime = StaticRep Time -> Eff (Time : es) a -> Eff es a
forall (e :: Effect) (sideEffects :: SideEffects) (es :: [Effect])
       a.
(HasCallStack, DispatchOf e ~ 'Static sideEffects,
 MaybeIOE sideEffects es) =>
StaticRep e -> Eff (e : es) a -> Eff es a
evalStaticRep StaticRep Time
MkTime

-- | Returns the local system time.
--
-- @since 0.1
getSystemTime :: (HasCallStack, Time :> es) => Eff es LocalTime
getSystemTime :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es LocalTime
getSystemTime = ZonedTime -> LocalTime
Local.zonedTimeToLocalTime (ZonedTime -> LocalTime) -> Eff es ZonedTime -> Eff es LocalTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es ZonedTime
forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es ZonedTime
getSystemZonedTime

-- | Returns the zoned system time.
--
-- @since 0.1
getSystemZonedTime :: (HasCallStack, Time :> es) => Eff es ZonedTime
getSystemZonedTime :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es ZonedTime
getSystemZonedTime = IO ZonedTime -> Eff es ZonedTime
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ IO ZonedTime
Local.getZonedTime

-- | Lifted 'Local.getTimeZone'.
--
-- @since 0.1
getTimeZone :: (HasCallStack, Time :> es) => UTCTime -> Eff es TimeZone
getTimeZone :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
UTCTime -> Eff es TimeZone
getTimeZone = IO TimeZone -> Eff es TimeZone
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ (IO TimeZone -> Eff es TimeZone)
-> (UTCTime -> IO TimeZone) -> UTCTime -> Eff es TimeZone
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> IO TimeZone
Local.getTimeZone

-- | Lifted 'Local.utcToLocalZonedTime'.
--
-- @since 0.1
utcToLocalZonedTime :: (HasCallStack, Time :> es) => UTCTime -> Eff es ZonedTime
utcToLocalZonedTime :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
UTCTime -> Eff es ZonedTime
utcToLocalZonedTime = IO ZonedTime -> Eff es ZonedTime
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ (IO ZonedTime -> Eff es ZonedTime)
-> (UTCTime -> IO ZonedTime) -> UTCTime -> Eff es ZonedTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> IO ZonedTime
Local.utcToLocalZonedTime

-- | Lifted 'TZ.loadLocalTZ'.
--
-- @since 0.1
loadLocalTZ :: (HasCallStack, Time :> es) => Eff es TZ
loadLocalTZ :: forall (es :: [Effect]). (HasCallStack, Time :> es) => Eff es TZ
loadLocalTZ = IO TZ -> Eff es TZ
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ IO TZ
TZ.loadLocalTZ

-- | Returns the zoned system time
--
-- @since 0.1
getMonotonicTime :: (HasCallStack, Time :> es) => Eff es Double
getMonotonicTime :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es Double
getMonotonicTime = IO Double -> Eff es Double
forall a (es :: [Effect]). IO a -> Eff es a
unsafeEff_ IO Double
C.getMonotonicTime

-- | Runs an action, returning the elapsed time.
--
-- @since 0.1
withTiming :: (HasCallStack, Time :> es) => Eff es a -> Eff es (TimeSpec, a)
withTiming :: forall (es :: [Effect]) a.
(HasCallStack, Time :> es) =>
Eff es a -> Eff es (TimeSpec, a)
withTiming Eff es a
m = do
  start <- Eff es Double
forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es Double
getMonotonicTime
  res <- m
  end <- getMonotonicTime
  pure (TimeSpec.fromSeconds (end - start), res)

-- | 'withTiming' but ignores the result value.
--
-- @since 0.1
withTiming_ :: (HasCallStack, Time :> es) => Eff es a -> Eff es TimeSpec
withTiming_ :: forall (es :: [Effect]) a.
(HasCallStack, Time :> es) =>
Eff es a -> Eff es TimeSpec
withTiming_ = ((TimeSpec, a) -> TimeSpec)
-> Eff es (TimeSpec, a) -> Eff es TimeSpec
forall a b. (a -> b) -> Eff es a -> Eff es b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (TimeSpec, a) -> TimeSpec
forall a b. (a, b) -> a
fst (Eff es (TimeSpec, a) -> Eff es TimeSpec)
-> (Eff es a -> Eff es (TimeSpec, a))
-> Eff es a
-> Eff es TimeSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Eff es a -> Eff es (TimeSpec, a)
forall (es :: [Effect]) a.
(HasCallStack, Time :> es) =>
Eff es a -> Eff es (TimeSpec, a)
withTiming

-- | Retrieves the formatted 'Data.Time.LocalTime'.
--
-- @since 0.1
getSystemTimeString :: (HasCallStack, Time :> es) => Eff es String
getSystemTimeString :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es String
getSystemTimeString = (LocalTime -> String) -> Eff es LocalTime -> Eff es String
forall a b. (a -> b) -> Eff es a -> Eff es b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LocalTime -> String
Utils.formatLocalTime Eff es LocalTime
forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es LocalTime
getSystemTime

-- | Retrieves the formatted 'Data.Time.ZonedTime'.
--
-- @since 0.1
getSystemZonedTimeString :: (HasCallStack, Time :> es) => Eff es String
getSystemZonedTimeString :: forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es String
getSystemZonedTimeString = (ZonedTime -> String) -> Eff es ZonedTime -> Eff es String
forall a b. (a -> b) -> Eff es a -> Eff es b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ZonedTime -> String
Utils.formatZonedTime Eff es ZonedTime
forall (es :: [Effect]).
(HasCallStack, Time :> es) =>
Eff es ZonedTime
getSystemZonedTime