tslib-0.1.4: src/Data/UTime.hs
-- | 'UTime' is a minimalistic efficient alternative to 'UTCTime'.
--
-- The 'UTime' representation is intentionally not opaque to allow
-- efficient low-level access. But, you should consider this module
-- \"somewhat internal\".
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.UTime (
UTime(..),
fromUTime,
toUTime,
toUTimeUp, toUTimeUp',
toUTimeDown, toUTimeDown',
utime,
-- * For testing only:
dtPico,
) where
import Control.Lens
import Data.Time
import Data.Int
import Foreign.Storable
import Unsafe.Coerce
newtype UTime = UTime Int64
deriving (Eq, Ord, Enum, Bounded, Storable)
-- 'DiffTime' is currently implemented as a newtype around
-- 'Integer'. As long as it stays so, this function is safe; and if it
-- changes it will blow up noticeably.
--
-- Because the appropriate accessors/constructors are not exported,
-- all other conversion mechanisms involve unnecessary 'Rational'
-- arithmetics, which is probably the biggest source of inefficiency
-- in 'UTCTime'.
dtPico :: Iso' DiffTime Integer
dtPico = iso unsafeCoerce unsafeCoerce
{-# INLINE dtPico #-}
fromUTime :: UTime -> UTCTime
fromUTime (UTime micros) = UTCTime (ModifiedJulianDay day) dayTime
where
(d, dt) = micros `divMod` 86400000000
day = fromIntegral $ 40587 + d
dayTime = (fromIntegral $ 1000000 * dt) ^. from dtPico
{-# INLINE fromUTime #-}
toUTimeGeneric :: Int64 -> UTCTime -> UTime
toUTimeGeneric shift (UTCTime (ModifiedJulianDay day) dayTime) = UTime micros
where
micros = d * 86400000000 + dt
d = fromIntegral day - 40587
dt = (fromIntegral (dayTime ^. dtPico) + shift) `div` 1000000
{-# INLINE toUTimeGeneric #-}
-- | Converts 'UTCTime' to 'UTime' by rounding.
toUTime :: UTCTime -> UTime
toUTime = toUTimeGeneric 500000
-- TODO(klao): rounding is probably the right thing to do
-- here. But, truncating can make things cleaner and easier to
-- reason about...
{-# INLINE toUTime #-}
-- | Returns the smallest 'UTime' value representing a time not
-- earlier than the input 'UTCTime'.
toUTimeUp :: UTCTime -> UTime
toUTimeUp = toUTimeGeneric 999999
{-# INLINE toUTimeUp #-}
-- | Returns the smallest 'UTime' value representing a time strictly
-- later than the input 'UTCTime'.
toUTimeUp' :: UTCTime -> UTime
toUTimeUp' = toUTimeGeneric 1000000
{-# INLINE toUTimeUp' #-}
-- | Returns the largest 'UTime' value representing a time not
-- later than the input 'UTCTime'.
toUTimeDown :: UTCTime -> UTime
toUTimeDown = toUTimeGeneric 0
{-# INLINE toUTimeDown #-}
-- | Returns the largest 'UTime' value representing a time strictly
-- earlier than the input 'UTCTime'.
toUTimeDown' :: UTCTime -> UTime
toUTimeDown' = toUTimeGeneric (-1)
{-# INLINE toUTimeDown' #-}
utime :: Iso' UTCTime UTime
utime = iso toUTime fromUTime
{-# INLINE utime #-}
instance Show UTime where
showsPrec p = showsPrec p . fromUTime
show = show . fromUTime