module Data.Time.Clock.TAI
(
AbsoluteTime,taiEpoch,addAbsoluteTime,diffAbsoluteTime,
LeapSecondTable,
utcDayLength,utcToTAITime,taiToUTCTime,
parseTAIUTCDATFile
) where
import Data.Time.LocalTime
import Data.Time.Calendar.Days
import Data.Time.Clock
import Data.Fixed
newtype AbsoluteTime = MkAbsoluteTime {unAbsoluteTime :: DiffTime} deriving (Eq,Ord)
instance Show AbsoluteTime where
show t = show (utcToLocalTime utc (taiToUTCTime (const 0) t)) ++ " TAI"
taiEpoch :: AbsoluteTime
taiEpoch = MkAbsoluteTime 0
addAbsoluteTime :: DiffTime -> AbsoluteTime -> AbsoluteTime
addAbsoluteTime t (MkAbsoluteTime a) = MkAbsoluteTime (a + t)
diffAbsoluteTime :: AbsoluteTime -> AbsoluteTime -> DiffTime
diffAbsoluteTime (MkAbsoluteTime a) (MkAbsoluteTime b) = a b
type LeapSecondTable = Day -> Integer
utcDayLength :: LeapSecondTable -> Day -> DiffTime
utcDayLength table day = realToFrac (86400 + (table (addDays 1 day)) (table day))
dayStart :: LeapSecondTable -> Day -> AbsoluteTime
dayStart table day = MkAbsoluteTime (realToFrac ((toModifiedJulianDay day) * 86400 + (table day)))
utcToTAITime :: LeapSecondTable -> UTCTime -> AbsoluteTime
utcToTAITime table (UTCTime day dtime) = MkAbsoluteTime (t + dtime) where
MkAbsoluteTime t = dayStart table day
taiToUTCTime :: LeapSecondTable -> AbsoluteTime -> UTCTime
taiToUTCTime table abstime = stable (ModifiedJulianDay (div' (unAbsoluteTime abstime) 86400)) where
stable day = if (day == day') then UTCTime day dtime else stable day' where
dayt = dayStart table day
dtime = diffAbsoluteTime abstime dayt
day' = addDays (div' dtime (utcDayLength table day)) day
parseTAIUTCDATFile :: String -> LeapSecondTable
parseTAIUTCDATFile ss = offsetlist 0 (parse (lines ss)) where
offsetlist :: Integer -> [(Day,Integer)] -> LeapSecondTable
offsetlist i [] _ = i
offsetlist i ((d0,_):_) d | d < d0 = i
offsetlist _ ((_,i0):xx) d = offsetlist i0 xx d
parse :: [String] -> [(Day,Integer)]
parse [] = []
parse (a:as) = let
ps = parse as
in case matchLine a of
Just di -> di:ps
Nothing -> ps
matchLine :: String -> Maybe (Day,Integer)
matchLine s = do
check0S s
(d,s') <- findJD s
i <- findOffset s'
return (d,i)
check0S :: String -> Maybe ()
check0S "X 0.0 S" = Just ()
check0S [] = Nothing
check0S (_:cs) = check0S cs
findJD :: String -> Maybe (Day,String)
findJD ('=':'J':'D':s) = do
d <- getInteger '5' s
return (ModifiedJulianDay (d 2400000),s)
findJD [] = Nothing
findJD (_:cs) = findJD cs
findOffset :: String -> Maybe Integer
findOffset ('T':'A':'I':'-':'U':'T':'C':'=':s) = getInteger '0' s
findOffset [] = Nothing
findOffset (_:cs) = findOffset cs
getInteger :: Char -> String -> Maybe Integer
getInteger p s = do
digits <- getDigits p s
fromDigits 0 digits
getDigits :: Char -> String -> Maybe String
getDigits p (' ':s) = getDigits p s
getDigits p (c:cs) | c >= '0' && c <= '9' = do
s <- getDigits p cs
return (c:s)
getDigits p ('.':p1:_) = if p == p1 then Just [] else Nothing
getDigits _ _ = Nothing
fromDigits :: Integer -> String -> Maybe Integer
fromDigits i [] = Just i
fromDigits i (c:cs) | c >= '0' && c <= '9' = fromDigits ((i * 10) + (fromIntegral ((fromEnum c) (fromEnum '0')))) cs
fromDigits _ _ = Nothing