chronologique-0.3.0.0: lib/Chrono/TimeStamp.hs
--
-- Time to manipulate time
--
-- Copyright © 2013-2017 Operational Dynamics Consulting, Pty Ltd and Others
--
-- The code in this file, and the program it is a part of, is
-- made available to you by its authors as open source software:
-- you can redistribute it and/or modify it under the terms of
-- the 3-clause BSD licence.
--
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module Chrono.TimeStamp
(
TimeStamp(..)
, getCurrentTimeNanoseconds
, ISO8601_Precise(..)
) where
import Control.Applicative
import Data.Maybe
import Data.Int (Int64)
import Data.Hourglass
import qualified Data.Vector.Generic as G
import qualified Data.Vector.Generic.Mutable as M
import Data.Vector.Unboxed
import Time.System
import Chrono.Formats
--
-- | Number of nanoseconds since the Unix epoch.
--
-- The Show instance displays the TimeStamp as seconds with the nanosecond
-- precision expressed as a decimal amount after the interger, ie:
--
-- >>> t <- getCurrentTimeNanoseconds
-- >>> show t
-- 2014-07-31T23:09:35.274387031Z
--
-- However this doesn't change the fact the underlying representation counts
-- nanoseconds since epoch:
--
-- >>> show $ unTimeStamp t
-- 1406848175274387031
--
-- There is a Read instance that is reasonably accommodating.
--
-- >>> read "2014-07-31T13:05:04.942089001Z" :: TimeStamp
-- 2014-07-31T13:05:04.942089001Z
--
-- >>> read "1406811904.942089001" :: TimeStamp
-- 2014-07-31T13:05:04.942089001Z
--
-- >>> read "1406811904" :: TimeStamp
-- 2014-07-31T13:05:04.000000000Z
--
-- In case you're wondering, the valid range of nanoseconds that fits into the
-- underlying Int64 is:
--
-- >>> show $ minBound :: TimeStamp
-- 1677-09-21T00:12:43.145224192Z
--
-- >>> show $ maxBound :: TimeStamp
-- 2262-04-11T23:47:16.854775807Z
--
-- so in a quarter millenium's time, yes, you'll have the Y2262 Problem.
-- Haskell code from today will, of course, still be running, so in the mid
-- Twenty-Third century you will need to replace this implementation with
-- something else.
--
newtype TimeStamp = TimeStamp {
unTimeStamp :: Int64
} deriving (Eq, Ord, Enum, Num, Real, Integral, Bounded)
{-
Hourglass works by sending types in and out of the Timeable and Time
typeclasses. They're not particularly easy to work with, but they're a
prerequisite for using timePrint
-}
instance Timeable TimeStamp where
timeGetElapsedP :: TimeStamp -> ElapsedP
timeGetElapsedP (TimeStamp ticks) =
let
(s,ns) = divMod ticks 1000000000
in
ElapsedP (Elapsed (Seconds (s))) (NanoSeconds (ns))
instance Time TimeStamp where
timeFromElapsedP :: ElapsedP -> TimeStamp
timeFromElapsedP (ElapsedP (Elapsed (Seconds seconds)) (NanoSeconds nanoseconds)) =
let
s = fromIntegral seconds :: Int64
ns = fromIntegral nanoseconds
in
TimeStamp $! (s * 1000000000) + ns
instance Show TimeStamp where
show t =
timePrint ISO8601_Precise t
instance Read TimeStamp where
readsPrec _ s = maybeToList $ (,"") <$> reduceDateTime <$> parse s
where
parse :: String -> Maybe DateTime
parse x = timeParse ISO8601_Precise x
<|> timeParse ISO8601_Seconds x
<|> timeParse ISO8601_DateAndTime x -- from hourglass
<|> timeParse ISO8601_Date x -- from hourglass
<|> timeParse Posix_Precise x
<|> timeParse Posix_Micro x
<|> timeParse Posix_Milli x
<|> timeParse Posix_Seconds x
reduceDateTime :: DateTime -> TimeStamp
reduceDateTime = timeFromElapsedP . timeGetElapsedP
--
-- | Get the current system time, expressed as a 'TimeStamp' (which is to
-- say, number of nanoseconds since the Unix epoch).
--
getCurrentTimeNanoseconds :: IO TimeStamp
getCurrentTimeNanoseconds = do
p <- timeCurrentP
return $! convertToTimeStamp p
convertToTimeStamp :: ElapsedP -> TimeStamp
convertToTimeStamp = timeFromElapsedP
{-
Ideally both the existing G.Vector Vector Int64 and M.MVector MVector
Int64 instances would be sufficient to support automatically deriving
those things for TimeStamp. But, *gaboom* somewhere down in GHC.Prim;
writing this gumpf out manually did the trick, and satisifies the
superclass requirements to derive an Unbox instance.
-}
newtype instance MVector s TimeStamp = MV_TimeStamp (MVector s Int64)
newtype instance Vector TimeStamp = V_TimeStamp (Vector Int64)
instance G.Vector Vector TimeStamp where
basicUnsafeFreeze (MV_TimeStamp v) = V_TimeStamp <$> G.basicUnsafeFreeze v
basicUnsafeThaw (V_TimeStamp v) = MV_TimeStamp <$> G.basicUnsafeThaw v
basicLength (V_TimeStamp v) = G.basicLength v
basicUnsafeSlice j k (V_TimeStamp v) = V_TimeStamp $ G.basicUnsafeSlice j k v
basicUnsafeIndexM (V_TimeStamp v) i = TimeStamp <$> G.basicUnsafeIndexM v i
{-# INLINE basicUnsafeFreeze #-}
{-# INLINE basicUnsafeThaw #-}
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicUnsafeIndexM #-}
instance M.MVector MVector TimeStamp where
basicLength (MV_TimeStamp v) = M.basicLength v
basicUnsafeSlice j k (MV_TimeStamp v) = MV_TimeStamp $ M.basicUnsafeSlice j k v
basicOverlaps (MV_TimeStamp a) (MV_TimeStamp b) = M.basicOverlaps a b
basicUnsafeNew n = MV_TimeStamp <$> M.basicUnsafeNew n
basicInitialize (MV_TimeStamp v) = M.basicInitialize v
basicUnsafeRead (MV_TimeStamp v) i = TimeStamp <$> M.basicUnsafeRead v i
basicUnsafeWrite (MV_TimeStamp v) i (TimeStamp a) = M.basicUnsafeWrite v i a
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicOverlaps #-}
{-# INLINE basicUnsafeNew #-}
{-# INLINE basicInitialize #-}
{-# INLINE basicUnsafeRead #-}
{-# INLINE basicUnsafeWrite #-}
deriving instance Unbox TimeStamp