content: module Data.Time.TAI64 (
      TAI64(..)
    , TAI64N(..)
    , TAI64NA(..)
    , posixToTAI64
    , posixToTAI64N
    , posixToTAI64NA
    , getCurrentTAI64
    , getCurrentTAI64N
    , getCurrentTAI64NA
    , tAI64ToPosix
    , tAI64NToPosix
    , tAI64NAToPosix
  ) where
  
  import Data.Serialize
  import Control.Monad
  import Data.Word
  
  import Data.Time.Clock
  import Data.Time.Clock.POSIX
  
  import Numeric
  
  data TAI64 = TAI64
    {-# UNPACK #-} !Word64
    deriving (Eq, Ord)
  
  data TAI64N = TAI64N
    {-# UNPACK #-} !TAI64
    {-# UNPACK #-} !Word32
    deriving (Eq, Ord, Show)
  
  data TAI64NA = TAI64NA
    {-# UNPACK #-} !TAI64N
    {-# UNPACK #-} !Word32
    deriving (Eq, Ord, Show)
  
  instance Show TAI64   where
    show (TAI64 t) = "TAI64 0x" ++ showHex t ""
  
  instance Serialize TAI64 where
    put (TAI64 t) = putWord64be t
    get = liftM TAI64 get
  
  instance Serialize TAI64N where
    put (TAI64N  t' nt) = put t' >> putWord32be nt
    get = liftM2 TAI64N  get get
  
  instance Serialize TAI64NA where
    put (TAI64NA t' at) = put t' >> putWord32be at
    get = liftM2 TAI64NA get get
  
  
  posixToTAI64 :: POSIXTime -> TAI64
  posixToTAI64 = TAI64 . (2^62 +) . truncate . realToFrac
  
  posixToTAI64N :: POSIXTime -> TAI64N
  posixToTAI64N pt = TAI64N t' ns where
    t' = posixToTAI64 pt
    ns = (`mod` 10^9) $ truncate (pts * 10**9)
    pts = realToFrac pt
  
  posixToTAI64NA :: POSIXTime -> TAI64NA -- | PICOsecond precision
  posixToTAI64NA pt = TAI64NA t' as where
    t' = posixToTAI64N pt
    as = (`mod` 10^9) $ truncate (pts * 10**18)
    pts = realToFrac pt
  
  getCurrentTAI64   :: IO TAI64
  getCurrentTAI64N  :: IO TAI64N
  getCurrentTAI64NA :: IO TAI64NA
  getCurrentTAI64   = liftM posixToTAI64   getPOSIXTime
  getCurrentTAI64N  = liftM posixToTAI64N  getPOSIXTime
  getCurrentTAI64NA = liftM posixToTAI64NA getPOSIXTime
  
  tAI64ToPosix :: TAI64 -> POSIXTime
  tAI64ToPosix (TAI64 s) = fromRational . fromIntegral $ s - 2^62
  
  tAI64NToPosix :: TAI64N -> POSIXTime
  tAI64NToPosix (TAI64N t' n) = tAI64ToPosix t' + nanopart where
    nanopart = fromRational $ (toRational $ 10**(-9)) * toRational n -- TODO: optimize?
  
  tAI64NAToPosix :: TAI64NA -> POSIXTime
  tAI64NAToPosix (TAI64NA t' a) = tAI64NToPosix t' + attopart where
    attopart = fromRational $ (toRational $ 10**(-18)) * toRational a
  