mirror of
https://github.com/urbit/shrub.git
synced 2024-12-21 01:41:37 +03:00
150 lines
4.4 KiB
Haskell
150 lines
4.4 KiB
Haskell
|
{-# LANGUAGE NumericUnderscores, GeneralizedNewtypeDeriving #-}
|
||
|
|
||
|
module Urbit.Time where
|
||
|
|
||
|
import Prelude
|
||
|
import Control.Lens
|
||
|
|
||
|
import Data.Coerce (coerce)
|
||
|
import Control.Concurrent (threadDelay)
|
||
|
import Control.Exception (throw, ArithException(Underflow))
|
||
|
import Data.Bits (shiftL, shiftR)
|
||
|
import Data.LargeWord (Word128, LargeKey(..))
|
||
|
import Data.Time.Clock (DiffTime, UTCTime, picosecondsToDiffTime,
|
||
|
diffTimeToPicoseconds)
|
||
|
import Data.Time.Clock.System (SystemTime(..), getSystemTime, utcToSystemTime,
|
||
|
systemToUTCTime)
|
||
|
|
||
|
|
||
|
-- Types -----------------------------------------------------------------------
|
||
|
|
||
|
newtype Gap = Gap { unGap :: Word128 }
|
||
|
deriving (Eq, Ord, Show)
|
||
|
|
||
|
newtype Wen = Wen { unWen :: Word128 }
|
||
|
deriving (Eq, Ord, Show)
|
||
|
|
||
|
newtype Unix = Unix { unUnix :: Word128 }
|
||
|
deriving (Eq, Ord, Show)
|
||
|
|
||
|
|
||
|
-- Basic Lenses ----------------------------------------------------------------
|
||
|
|
||
|
fractoSecs :: Iso' Gap Word128
|
||
|
fractoSecs = iso unGap Gap
|
||
|
|
||
|
sinceUrbitEpoch :: Iso' Wen Gap
|
||
|
sinceUrbitEpoch = iso (Gap . unWen) (Wen . unGap)
|
||
|
|
||
|
sinceUnixEpoch :: Iso' Unix Gap
|
||
|
sinceUnixEpoch = iso (Gap . unUnix) (Unix . unGap)
|
||
|
|
||
|
|
||
|
-- Instances -------------------------------------------------------------------
|
||
|
|
||
|
instance Num Gap where
|
||
|
x + y = Gap (coerce x + coerce y)
|
||
|
x * y = Gap (coerce x * coerce y)
|
||
|
fromInteger = Gap . fromInteger
|
||
|
abs = over fractoSecs abs
|
||
|
signum = over fractoSecs signum
|
||
|
negate = over fractoSecs negate
|
||
|
|
||
|
|
||
|
-- Conversions -----------------------------------------------------------------
|
||
|
|
||
|
diffTime :: Iso' Gap DiffTime
|
||
|
diffTime = iso fromGap toGap
|
||
|
where
|
||
|
fromGap = picosecondsToDiffTime . view picoSecs
|
||
|
toGap = view (from picoSecs) . diffTimeToPicoseconds
|
||
|
|
||
|
utcTime :: Iso' Unix UTCTime
|
||
|
utcTime = iso fromUnix toUnix
|
||
|
where
|
||
|
fromUnix = systemToUTCTime . view systemTime
|
||
|
toUnix = view (from systemTime) . utcToSystemTime
|
||
|
|
||
|
wenUtcTime :: Iso' Wen UTCTime
|
||
|
wenUtcTime = unix . utcTime
|
||
|
|
||
|
systemTime :: Iso' Unix SystemTime
|
||
|
systemTime = iso toSys fromSys
|
||
|
where
|
||
|
toSys :: Unix -> SystemTime
|
||
|
toSys (sinceUnixEpoch -> gap) =
|
||
|
MkSystemTime (gap ^. secs) (gap ^. nanoSecs `mod` 1_000_000_000)
|
||
|
|
||
|
fromSys :: SystemTime -> Unix
|
||
|
fromSys (MkSystemTime numSecs ns) =
|
||
|
fromUnixEpoch $ (numSecs ^. from secs) + (ns ^. from nanoSecs)
|
||
|
|
||
|
fromUnixEpoch :: Gap -> Unix
|
||
|
fromUnixEpoch (Gap g) = Unix g
|
||
|
|
||
|
sinceUnixEpoch :: Unix -> Gap
|
||
|
sinceUnixEpoch (Unix u) = Gap u
|
||
|
|
||
|
unixEpoch :: Wen
|
||
|
unixEpoch = Wen (LargeKey 0x8000_000c_ce9e_0d80 0)
|
||
|
|
||
|
unix :: Iso' Wen Unix
|
||
|
unix = iso toUnix fromUnix
|
||
|
where
|
||
|
fromUnix (Unix u) = Wen (u + epoch)
|
||
|
toUnix (Wen w) | w >= epoch = Unix (w - epoch)
|
||
|
| otherwise = throw Underflow
|
||
|
epoch = view (sinceUrbitEpoch . fractoSecs) unixEpoch
|
||
|
|
||
|
picoSecs :: (Integral a, Num a) => Iso' Gap a
|
||
|
picoSecs = iso fromGap toGap
|
||
|
where
|
||
|
fromGap (Gap x) = fromIntegral (shiftR (x * 1_000_000_000_000) 64)
|
||
|
toGap x = Gap (shiftL (fromIntegral x) 64 `div` 1_000_000_000_000)
|
||
|
|
||
|
nanoSecs :: (Integral a, Num a) => Iso' Gap a
|
||
|
nanoSecs = iso fromGap toGap
|
||
|
where
|
||
|
fromGap (Gap x) = fromIntegral (shiftR (x * 1_000_000_000) 64)
|
||
|
toGap x = Gap (shiftL (fromIntegral x) 64 `div` 1_000_000_000)
|
||
|
|
||
|
microSecs :: (Integral a, Num a) => Iso' Gap a
|
||
|
microSecs = iso fromGap toGap
|
||
|
where
|
||
|
fromGap (Gap x) = fromIntegral (shiftR (x * 1_000_000) 64)
|
||
|
toGap x = Gap (shiftL (fromIntegral x) 64 `div` 1_000_000)
|
||
|
|
||
|
milliSecs :: (Integral a, Num a) => Iso' Gap a
|
||
|
milliSecs = iso fromGap toGap
|
||
|
where
|
||
|
fromGap (Gap x) = fromIntegral (shiftR (x * 1_000) 64)
|
||
|
toGap x = Gap (shiftL (fromIntegral x) 64 `div` 1_000)
|
||
|
|
||
|
secs :: (Integral a, Num a) => Iso' Gap a
|
||
|
secs = iso fromGap toGap
|
||
|
where
|
||
|
fromGap (Gap x) = fromIntegral (shiftR x 64)
|
||
|
toGap x = Gap (shiftL (fromIntegral x) 64)
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
now :: IO Wen
|
||
|
now = view (from systemTime . from unix) <$> getSystemTime
|
||
|
|
||
|
gap :: Wen -> Wen -> Gap
|
||
|
gap (Wen x) (Wen y) | x > y = Gap (x - y)
|
||
|
| otherwise = Gap (y - x)
|
||
|
|
||
|
addGap :: Wen -> Gap -> Wen
|
||
|
addGap (Wen fs) (Gap g) = Wen (fs + g)
|
||
|
|
||
|
sleep :: Gap -> IO ()
|
||
|
sleep gap = threadDelay (gap ^. microSecs)
|
||
|
|
||
|
sleepUntil :: Wen -> IO ()
|
||
|
sleepUntil end = do
|
||
|
now >>= \case
|
||
|
start | start >= end -> pure ()
|
||
|
| otherwise -> sleep (gap start end)
|