{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveDataTypeable #-} -- | -- Module : Data.Hourglass.Epoch -- License : BSD-style -- Maintainer : Vincent Hanquez -- Stability : experimental -- Portability : unknown -- -- Epoch tracking -- module Data.Hourglass.Epoch ( -- * computer time tracking with various epoch ElapsedSince(..) , ElapsedSinceP(..) -- * Epoch , Epoch(..) -- ** standard and usual epochs , UnixEpoch(..) , WindowsEpoch(..) ) where import Data.Data import Control.DeepSeq import Data.Hourglass.Types import Data.Hourglass.Time -- | A number of seconds elapsed since an epoch. newtype ElapsedSince epoch = ElapsedSince Seconds deriving (Show,Read,Eq,Ord,Num,Data,Typeable,NFData) -- | A number of seconds and nanoseconds elapsed since an epoch. data ElapsedSinceP epoch = ElapsedSinceP {-# UNPACK #-} !(ElapsedSince epoch) {-# UNPACK #-} !NanoSeconds deriving (Show,Read,Eq,Ord,Data,Typeable) instance NFData (ElapsedSinceP e) where rnf e = e `seq` () instance Num (ElapsedSinceP e) where (ElapsedSinceP e1 ns1) + (ElapsedSinceP e2 ns2) = ElapsedSinceP (e1+e2) (ns1+ns2) (ElapsedSinceP e1 ns1) - (ElapsedSinceP e2 ns2) = ElapsedSinceP (e1-e2) (ns1-ns2) (ElapsedSinceP e1 ns1) * (ElapsedSinceP e2 ns2) = ElapsedSinceP (e1*e2) (ns1*ns2) negate (ElapsedSinceP e ns) = ElapsedSinceP (negate e) ns abs (ElapsedSinceP e ns) = ElapsedSinceP (abs e) ns signum (ElapsedSinceP e ns) = ElapsedSinceP (signum e) ns fromInteger i = ElapsedSinceP (ElapsedSince (fromIntegral i)) 0 -- FIXME instance Real (ElapsedSinceP e) -- | epoch related. -- -- We use the well known Unix epoch as the -- reference timezone for doing conversion between epochs. -- -- Each methods of this typeclass should not use the actual value, -- but only get the information needed from the type itself. class Epoch epoch where -- | The name of this epoch epochName :: epoch -> String -- | number of seconds of difference with 1st January 1970. -- -- a negative number means that this epoch start before -- the unix epoch. epochDiffToUnix :: epoch -> Seconds -- | Unix Epoch, starting 1st January 1970 data UnixEpoch = UnixEpoch deriving (Show,Eq) instance Epoch UnixEpoch where epochName _ = "unix" epochDiffToUnix _ = 0 -- | Windows Epoch, starting 1st January 1601 data WindowsEpoch = WindowsEpoch deriving (Show,Eq) instance Epoch WindowsEpoch where epochName _ = "windows" epochDiffToUnix _ = -11644473600 instance Epoch epoch => Timeable (ElapsedSince epoch) where timeGetElapsedP es = ElapsedP (Elapsed e) 0 where ElapsedSince e = convertEpoch es :: ElapsedSince UnixEpoch timeGetElapsed es = Elapsed e where ElapsedSince e = convertEpoch es :: ElapsedSince UnixEpoch timeGetNanoSeconds _ = 0 instance Epoch epoch => Time (ElapsedSince epoch) where timeFromElapsedP (ElapsedP (Elapsed e) _) = convertEpoch (ElapsedSince e :: ElapsedSince UnixEpoch) instance Epoch epoch => Timeable (ElapsedSinceP epoch) where timeGetElapsedP es = ElapsedP (Elapsed e) ns where ElapsedSinceP (ElapsedSince e) ns = convertEpochP es :: ElapsedSinceP UnixEpoch timeGetNanoSeconds (ElapsedSinceP _ ns) = ns instance Epoch epoch => Time (ElapsedSinceP epoch) where timeFromElapsedP (ElapsedP (Elapsed e) ns) = convertEpochP (ElapsedSinceP (ElapsedSince e) ns :: ElapsedSinceP UnixEpoch) -- | Convert Elapsed seconds to another epoch with explicit epochs specified convertEpochWith :: (Epoch e1, Epoch e2) => (e1,e2) -> ElapsedSince e1 -> ElapsedSince e2 convertEpochWith (e1,e2) (ElapsedSince s1) = ElapsedSince (s1 + diff) where diff = d1 - d2 d1 = epochDiffToUnix e1 d2 = epochDiffToUnix e2 -- | Convert Elapsed seconds to another epoch. -- -- the actual epochs need to be known somehow by the context, otherwise this function -- will yield a compilation errors as the epoch are not chosen. -- -- If you want to force specific epoch conversion, use convertEpochWith convertEpoch :: (Epoch e1, Epoch e2) => ElapsedSince e1 -> ElapsedSince e2 convertEpoch = convertEpochWith (undefined, undefined) -- | Convert Precise Elapsed seconds to another epoch with explicit epochs specified convertEpochPWith :: (Epoch e1, Epoch e2) => (e1,e2) -> ElapsedSinceP e1 -> ElapsedSinceP e2 convertEpochPWith es (ElapsedSinceP e1 n1) = ElapsedSinceP (convertEpochWith es e1) n1 -- | Convert Elapsed seconds to another epoch. -- -- the actual epochs need to be known somehow by the context, otherwise this function -- will yield a compilation errors as the epoch are not chosen. -- -- If you want to force specific epoch conversion, use convertEpochWith convertEpochP :: (Epoch e1, Epoch e2) => ElapsedSinceP e1 -> ElapsedSinceP e2 convertEpochP = convertEpochPWith (undefined, undefined)