{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE DerivingStrategies    #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns        #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TemplateHaskell       #-}

{-# OPTIONS_GHC -Wno-orphans        #-}

module Cardano.Node.Emulator.TimeSlot(
  SlotConfig(..)
, SlotConversionError(..)
, slotRangeToPOSIXTimeRange
, slotToPOSIXTimeRange
, slotToBeginPOSIXTime
, slotToEndPOSIXTime
, posixTimeRangeToContainedSlotRange
, posixTimeToEnclosingSlot
, currentSlot
, utcTimeToPOSIXTime
, posixTimeToUTCTime
, nominalDiffTimeToPOSIXTime
, posixTimeToNominalDiffTime
) where

import Codec.Serialise (Serialise)
import Data.Aeson (FromJSON, ToJSON)
import Data.Default (Default (def))
import Data.Time.Clock qualified as Time
import Data.Time.Clock.POSIX qualified as Time
import GHC.Generics (Generic)
import Ledger.Orphans ()
import Ledger.Slot (Slot (Slot), SlotRange)
import Plutus.V1.Ledger.Interval (Extended (..), Interval (Interval), LowerBound (..), UpperBound (..), interval,
                                  member)
import Plutus.V1.Ledger.Time (POSIXTime (POSIXTime, getPOSIXTime), POSIXTimeRange)
import PlutusTx.Lift (makeLift)
import PlutusTx.Prelude hiding (Eq, (<$>))
import Prelude (Eq, IO, Show, (<$>))
import Prelude qualified as Haskell
import Prettyprinter (Pretty (pretty), (<+>))

-- | Datatype to configure the length (ms) of one slot and the beginning of the
-- first slot.
data SlotConfig =
    SlotConfig
        { SlotConfig -> Integer
scSlotLength   :: Integer -- ^ Length (number of milliseconds) of one slot
        , SlotConfig -> POSIXTime
scSlotZeroTime :: POSIXTime -- ^ Beginning of slot 0 (in milliseconds)
        }
    deriving stock (SlotConfig -> SlotConfig -> Bool
(SlotConfig -> SlotConfig -> Bool)
-> (SlotConfig -> SlotConfig -> Bool) -> Eq SlotConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SlotConfig -> SlotConfig -> Bool
$c/= :: SlotConfig -> SlotConfig -> Bool
== :: SlotConfig -> SlotConfig -> Bool
$c== :: SlotConfig -> SlotConfig -> Bool
Eq, Int -> SlotConfig -> ShowS
[SlotConfig] -> ShowS
SlotConfig -> String
(Int -> SlotConfig -> ShowS)
-> (SlotConfig -> String)
-> ([SlotConfig] -> ShowS)
-> Show SlotConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SlotConfig] -> ShowS
$cshowList :: [SlotConfig] -> ShowS
show :: SlotConfig -> String
$cshow :: SlotConfig -> String
showsPrec :: Int -> SlotConfig -> ShowS
$cshowsPrec :: Int -> SlotConfig -> ShowS
Show, (forall x. SlotConfig -> Rep SlotConfig x)
-> (forall x. Rep SlotConfig x -> SlotConfig) -> Generic SlotConfig
forall x. Rep SlotConfig x -> SlotConfig
forall x. SlotConfig -> Rep SlotConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SlotConfig x -> SlotConfig
$cfrom :: forall x. SlotConfig -> Rep SlotConfig x
Generic)
    deriving anyclass ([SlotConfig] -> Encoding
[SlotConfig] -> Value
SlotConfig -> Encoding
SlotConfig -> Value
(SlotConfig -> Value)
-> (SlotConfig -> Encoding)
-> ([SlotConfig] -> Value)
-> ([SlotConfig] -> Encoding)
-> ToJSON SlotConfig
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [SlotConfig] -> Encoding
$ctoEncodingList :: [SlotConfig] -> Encoding
toJSONList :: [SlotConfig] -> Value
$ctoJSONList :: [SlotConfig] -> Value
toEncoding :: SlotConfig -> Encoding
$ctoEncoding :: SlotConfig -> Encoding
toJSON :: SlotConfig -> Value
$ctoJSON :: SlotConfig -> Value
ToJSON, Value -> Parser [SlotConfig]
Value -> Parser SlotConfig
(Value -> Parser SlotConfig)
-> (Value -> Parser [SlotConfig]) -> FromJSON SlotConfig
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [SlotConfig]
$cparseJSONList :: Value -> Parser [SlotConfig]
parseJSON :: Value -> Parser SlotConfig
$cparseJSON :: Value -> Parser SlotConfig
FromJSON, [SlotConfig] -> Encoding
SlotConfig -> Encoding
(SlotConfig -> Encoding)
-> (forall s. Decoder s SlotConfig)
-> ([SlotConfig] -> Encoding)
-> (forall s. Decoder s [SlotConfig])
-> Serialise SlotConfig
forall s. Decoder s [SlotConfig]
forall s. Decoder s SlotConfig
forall a.
(a -> Encoding)
-> (forall s. Decoder s a)
-> ([a] -> Encoding)
-> (forall s. Decoder s [a])
-> Serialise a
decodeList :: Decoder s [SlotConfig]
$cdecodeList :: forall s. Decoder s [SlotConfig]
encodeList :: [SlotConfig] -> Encoding
$cencodeList :: [SlotConfig] -> Encoding
decode :: Decoder s SlotConfig
$cdecode :: forall s. Decoder s SlotConfig
encode :: SlotConfig -> Encoding
$cencode :: SlotConfig -> Encoding
Serialise)

makeLift ''SlotConfig

instance Default SlotConfig where
    {-# INLINABLE def #-}
    def :: SlotConfig
def = SlotConfig :: Integer -> POSIXTime -> SlotConfig
SlotConfig{ scSlotLength :: Integer
scSlotLength = Integer
1000, scSlotZeroTime :: POSIXTime
scSlotZeroTime = Integer -> POSIXTime
POSIXTime Integer
beginningOfTime }

instance Pretty SlotConfig where
    pretty :: SlotConfig -> Doc ann
pretty SlotConfig {Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} =
            Doc ann
"Slot 0 starts at"
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> POSIXTime -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty POSIXTime
scSlotZeroTime
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"and one slot has length of"
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Integer -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Integer
scSlotLength
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"ms"

data SlotConversionError =
    SlotOutOfRange
        { SlotConversionError -> Slot
requestedSlot :: Slot
        , SlotConversionError -> (Slot, POSIXTime)
horizon       :: (Slot, POSIXTime)
        }
    deriving stock (SlotConversionError -> SlotConversionError -> Bool
(SlotConversionError -> SlotConversionError -> Bool)
-> (SlotConversionError -> SlotConversionError -> Bool)
-> Eq SlotConversionError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SlotConversionError -> SlotConversionError -> Bool
$c/= :: SlotConversionError -> SlotConversionError -> Bool
== :: SlotConversionError -> SlotConversionError -> Bool
$c== :: SlotConversionError -> SlotConversionError -> Bool
Eq, Int -> SlotConversionError -> ShowS
[SlotConversionError] -> ShowS
SlotConversionError -> String
(Int -> SlotConversionError -> ShowS)
-> (SlotConversionError -> String)
-> ([SlotConversionError] -> ShowS)
-> Show SlotConversionError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SlotConversionError] -> ShowS
$cshowList :: [SlotConversionError] -> ShowS
show :: SlotConversionError -> String
$cshow :: SlotConversionError -> String
showsPrec :: Int -> SlotConversionError -> ShowS
$cshowsPrec :: Int -> SlotConversionError -> ShowS
Show, (forall x. SlotConversionError -> Rep SlotConversionError x)
-> (forall x. Rep SlotConversionError x -> SlotConversionError)
-> Generic SlotConversionError
forall x. Rep SlotConversionError x -> SlotConversionError
forall x. SlotConversionError -> Rep SlotConversionError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SlotConversionError x -> SlotConversionError
$cfrom :: forall x. SlotConversionError -> Rep SlotConversionError x
Generic)
    deriving anyclass ([SlotConversionError] -> Encoding
[SlotConversionError] -> Value
SlotConversionError -> Encoding
SlotConversionError -> Value
(SlotConversionError -> Value)
-> (SlotConversionError -> Encoding)
-> ([SlotConversionError] -> Value)
-> ([SlotConversionError] -> Encoding)
-> ToJSON SlotConversionError
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [SlotConversionError] -> Encoding
$ctoEncodingList :: [SlotConversionError] -> Encoding
toJSONList :: [SlotConversionError] -> Value
$ctoJSONList :: [SlotConversionError] -> Value
toEncoding :: SlotConversionError -> Encoding
$ctoEncoding :: SlotConversionError -> Encoding
toJSON :: SlotConversionError -> Value
$ctoJSON :: SlotConversionError -> Value
ToJSON, Value -> Parser [SlotConversionError]
Value -> Parser SlotConversionError
(Value -> Parser SlotConversionError)
-> (Value -> Parser [SlotConversionError])
-> FromJSON SlotConversionError
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [SlotConversionError]
$cparseJSONList :: Value -> Parser [SlotConversionError]
parseJSON :: Value -> Parser SlotConversionError
$cparseJSON :: Value -> Parser SlotConversionError
FromJSON)

instance Pretty SlotConversionError where
    pretty :: SlotConversionError -> Doc ann
pretty SlotOutOfRange { Slot
requestedSlot :: Slot
requestedSlot :: SlotConversionError -> Slot
requestedSlot, (Slot, POSIXTime)
horizon :: (Slot, POSIXTime)
horizon :: SlotConversionError -> (Slot, POSIXTime)
horizon } =
            Doc ann
"Slot out of range:"
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Slot -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Slot
requestedSlot
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"Horizon:"
        Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> (Slot, POSIXTime) -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty (Slot, POSIXTime)
horizon

{-# INLINABLE beginningOfTime #-}
-- | 'beginningOfTime' corresponds to the Shelley launch date
-- (2020-07-29T21:44:51Z) which is 1596059091000 in POSIX time
-- (number of milliseconds since 1970-01-01T00:00:00Z).
beginningOfTime :: Integer
beginningOfTime :: Integer
beginningOfTime = Integer
1596059091000

{-# INLINABLE slotRangeToPOSIXTimeRange #-}
-- | Convert a 'SlotRange' to a 'POSIXTimeRange' given a 'SlotConfig'. The
-- resulting 'POSIXTimeRange' refers to the starting time of the lower bound of
-- the 'SlotRange' and the ending time of the upper bound of the 'SlotRange'.
slotRangeToPOSIXTimeRange :: SlotConfig -> SlotRange -> POSIXTimeRange
slotRangeToPOSIXTimeRange :: SlotConfig -> SlotRange -> POSIXTimeRange
slotRangeToPOSIXTimeRange SlotConfig
sc (Interval (LowerBound Extended Slot
start Bool
startIncl) (UpperBound Extended Slot
end Bool
endIncl)) =
  let lbound :: Extended POSIXTime
lbound = (Slot -> POSIXTime) -> Extended Slot -> Extended POSIXTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (if Bool
startIncl then SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc else SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc) Extended Slot
start
      ubound :: Extended POSIXTime
ubound = (Slot -> POSIXTime) -> Extended Slot -> Extended POSIXTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (if Bool
endIncl   then SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc   else SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc) Extended Slot
end
   in LowerBound POSIXTime -> UpperBound POSIXTime -> POSIXTimeRange
forall a. LowerBound a -> UpperBound a -> Interval a
Interval (Extended POSIXTime -> Bool -> LowerBound POSIXTime
forall a. Extended a -> Bool -> LowerBound a
LowerBound Extended POSIXTime
lbound Bool
startIncl) (Extended POSIXTime -> Bool -> UpperBound POSIXTime
forall a. Extended a -> Bool -> UpperBound a
UpperBound Extended POSIXTime
ubound Bool
endIncl)

{-# INLINABLE slotToPOSIXTimeRange #-}
-- | Convert a 'Slot' to a 'POSIXTimeRange' given a 'SlotConfig'. Each 'Slot'
-- can be represented by an interval of time.
slotToPOSIXTimeRange :: SlotConfig -> Slot -> POSIXTimeRange
slotToPOSIXTimeRange :: SlotConfig -> Slot -> POSIXTimeRange
slotToPOSIXTimeRange SlotConfig
sc Slot
slot =
  POSIXTime -> POSIXTime -> POSIXTimeRange
forall a. a -> a -> Interval a
interval (SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
slot) (SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc Slot
slot)

{-# INLINABLE slotToBeginPOSIXTime #-}
-- | Get the starting 'POSIXTime' of a 'Slot' given a 'SlotConfig'.
slotToBeginPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} (Slot Integer
n) =
  let msAfterBegin :: Integer
msAfterBegin = Integer
n Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
scSlotLength
   in Integer -> POSIXTime
POSIXTime (Integer -> POSIXTime) -> Integer -> POSIXTime
forall a b. (a -> b) -> a -> b
$ POSIXTime -> Integer
getPOSIXTime POSIXTime
scSlotZeroTime Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
msAfterBegin

{-# INLINABLE slotToEndPOSIXTime #-}
-- | Get the ending 'POSIXTime' of a 'Slot' given a 'SlotConfig'.
slotToEndPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime sc :: SlotConfig
sc@SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength} Slot
slot =
  SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
slot POSIXTime -> POSIXTime -> POSIXTime
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer -> POSIXTime
POSIXTime (Integer
scSlotLength Integer -> Integer -> Integer
forall a. AdditiveGroup a => a -> a -> a
- Integer
1)

{-# INLINABLE posixTimeRangeToContainedSlotRange #-}
-- | Convert a 'POSIXTimeRange' to 'SlotRange' given a 'SlotConfig'. This gives
-- the biggest slot range that is entirely contained by the given time range.
posixTimeRangeToContainedSlotRange :: SlotConfig -> POSIXTimeRange -> SlotRange
posixTimeRangeToContainedSlotRange :: SlotConfig -> POSIXTimeRange -> SlotRange
posixTimeRangeToContainedSlotRange SlotConfig
sc POSIXTimeRange
ptr = case (POSIXTime -> Slot) -> POSIXTimeRange -> SlotRange
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig
sc) POSIXTimeRange
ptr of
  Interval (LowerBound Extended Slot
start Bool
startIncl) (UpperBound Extended Slot
end Bool
endIncl) ->
    LowerBound Slot -> UpperBound Slot -> SlotRange
forall a. LowerBound a -> UpperBound a -> Interval a
Interval
      (Extended Slot -> Bool -> LowerBound Slot
forall a. Extended a -> Bool -> LowerBound a
LowerBound Extended Slot
start (case Extended Slot
start of Finite Slot
s -> SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
s POSIXTime -> POSIXTimeRange -> Bool
forall a. Ord a => a -> Interval a -> Bool
`member` POSIXTimeRange
ptr; Extended Slot
_ -> Bool
startIncl))
      (Extended Slot -> Bool -> UpperBound Slot
forall a. Extended a -> Bool -> UpperBound a
UpperBound Extended Slot
end (case Extended Slot
end of Finite Slot
e -> SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc Slot
e POSIXTime -> POSIXTimeRange -> Bool
forall a. Ord a => a -> Interval a -> Bool
`member` POSIXTimeRange
ptr; Extended Slot
_ -> Bool
endIncl))

{-# INLINABLE posixTimeToEnclosingSlot #-}
-- | Convert a 'POSIXTime' to 'Slot' given a 'SlotConfig'.
posixTimeToEnclosingSlot :: SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot :: SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} (POSIXTime Integer
t) =
  let timePassed :: Integer
timePassed = Integer
t Integer -> Integer -> Integer
forall a. AdditiveGroup a => a -> a -> a
- POSIXTime -> Integer
getPOSIXTime POSIXTime
scSlotZeroTime
      slotsPassed :: Integer
slotsPassed = Integer -> Integer -> Integer
divide Integer
timePassed Integer
scSlotLength
  in Integer -> Slot
Slot Integer
slotsPassed

-- | Get the current slot number
currentSlot :: SlotConfig -> IO Slot
currentSlot :: SlotConfig -> IO Slot
currentSlot SlotConfig
sc = NominalDiffTime -> Slot
timeToSlot (NominalDiffTime -> Slot) -> IO NominalDiffTime -> IO Slot
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO NominalDiffTime
Time.getPOSIXTime
    where
      timeToSlot :: NominalDiffTime -> Slot
timeToSlot = SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig
sc
                 (POSIXTime -> Slot)
-> (NominalDiffTime -> POSIXTime) -> NominalDiffTime -> Slot
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime

utcTimeToPOSIXTime :: Time.UTCTime -> POSIXTime
utcTimeToPOSIXTime :: UTCTime -> POSIXTime
utcTimeToPOSIXTime = NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime (NominalDiffTime -> POSIXTime)
-> (UTCTime -> NominalDiffTime) -> UTCTime -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> NominalDiffTime
Time.utcTimeToPOSIXSeconds

posixTimeToUTCTime :: POSIXTime -> Time.UTCTime
posixTimeToUTCTime :: POSIXTime -> UTCTime
posixTimeToUTCTime = NominalDiffTime -> UTCTime
Time.posixSecondsToUTCTime (NominalDiffTime -> UTCTime)
-> (POSIXTime -> NominalDiffTime) -> POSIXTime -> UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. POSIXTime -> NominalDiffTime
posixTimeToNominalDiffTime

nominalDiffTimeToPOSIXTime :: Time.NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime :: NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime
  = Integer -> POSIXTime
POSIXTime
  (Integer -> POSIXTime)
-> (NominalDiffTime -> Integer) -> NominalDiffTime -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pico -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
Haskell.truncate
  (Pico -> Integer)
-> (NominalDiffTime -> Pico) -> NominalDiffTime -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pico -> Pico -> Pico
forall a. Num a => a -> a -> a
Haskell.* Pico
1000) -- Convert to ms
  (Pico -> Pico)
-> (NominalDiffTime -> Pico) -> NominalDiffTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NominalDiffTime -> Pico
Time.nominalDiffTimeToSeconds

posixTimeToNominalDiffTime :: POSIXTime -> Time.NominalDiffTime
posixTimeToNominalDiffTime :: POSIXTime -> NominalDiffTime
posixTimeToNominalDiffTime
  = Pico -> NominalDiffTime
Time.secondsToNominalDiffTime
  (Pico -> NominalDiffTime)
-> (POSIXTime -> Pico) -> POSIXTime -> NominalDiffTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pico -> Pico -> Pico
forall a. Fractional a => a -> a -> a
Haskell./ Pico
1000)
  (Pico -> Pico) -> (POSIXTime -> Pico) -> POSIXTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Pico
forall a. Num a => Integer -> a
Haskell.fromInteger
  (Integer -> Pico) -> (POSIXTime -> Integer) -> POSIXTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. POSIXTime -> Integer
getPOSIXTime