import Control.Applicative( (<$>), (<*>) ) import Data.DateTime( diffSeconds ) import Data.List.Split( splitOn ) import Data.List( zipWith4, transpose ) import qualified Data.Map as Map import Data.Maybe( fromJust, fromMaybe ) import Data.Time.Clock( UTCTime(..), secondsToDiffTime ) import Data.Time.Format( parseTime, formatTime ) import Data.Time.LocalTime( LocalTime(..), ZonedTime(..), zonedTimeToUTC, midnight, localDay ) import Diddo.Entry( DiddoEntry(..) ) import Diddo.Log( DiddoLogline(..) ) import HMSTime( HMSTime(..), secondsToHMS ) import System.Console.GetOpt import System.Environment( getArgs ) import System.Exit import System.IO( stderr, hPutStr, hPutStrLn ) import System.Locale data Flag = Verbose | Version | Help | InputFile String | OutputFile String | InputFormat String | OutputFormat String deriving Show options :: [OptDescr Flag] options = [ Option "v" ["verbose"] (NoArg Verbose) "More detailed output" , Option "V" ["version"] (NoArg Version) "Display program version" , Option "h" ["help"] (NoArg Help) "Display program version" , Option "f" ["file"] (ReqArg InputFile "FILE") "Read from FILE" , Option "w" ["output"] (ReqArg OutputFile "FILE") "Write to FILE" , Option "i" ["informat"] (ReqArg InputFormat "FORMAT") "Parse dates in the given FORMAT" , Option "o" ["outformat"] (ReqArg OutputFormat "FORMAT") "Output dates in the given FORMAT" ] parseToZonedTime :: String -> String -> ZonedTime parseToZonedTime format = fromMaybe (error "Input data broken.") . parseTime defaultTimeLocale format parseISOsecondsTime :: String -> ZonedTime parseISOsecondsTime = parseToZonedTime $ iso8601DateFormat $ Just "%T%z" parseRFC822Time :: String -> ZonedTime parseRFC822Time = parseToZonedTime rfc822DateFormat formatZonedTime :: String -> ZonedTime -> String formatZonedTime = formatTime defaultTimeLocale utcTimesDeltas :: ZonedTime -> [UTCTime] -> [Integer] utcTimesDeltas startTime timestamps = let startTimeUTC = zonedTimeToUTC startTime relevantTimestamps = dropWhile (< startTimeUTC) timestamps in zipWith diffSeconds relevantTimestamps $ startTimeUTC : init relevantTimestamps startOfDay :: ZonedTime -> ZonedTime startOfDay time = ZonedTime (LocalTime day midnight) $ zonedTimeZone time where day = localDay $ zonedTimeToLocalTime time parseToUTCFromZonedString :: String -> String -> UTCTime parseToUTCFromZonedString fmt time = zonedTimeToUTC $ parseToZonedTime fmt time splitToMapOn :: String -> [String] -> Map.Map String [String] splitToMapOn sep lines = Map.fromList $ map (listToTuple . splitOn sep) lines where listToTuple (x:xs) = (x, xs) logLinesToDiddohs :: String -> [String] -> [DiddoEntry] logLinesToDiddohs inDateFmt logLines = let loglineMap = splitToMapOn ";" logLines zonedtimeEntryMap = Map.mapKeysMonotonic (parseToZonedTime inDateFmt) loglineMap utctimeEntryMap = Map.mapKeys zonedTimeToUTC zonedtimeEntryMap timeStamps = Map.keys loglineMap entryTexts = map head $ Map.elems loglineMap parsedTimes = Map.keys zonedtimeEntryMap deltasHMS = map secondsToHMS $ utcTimesDeltas (startOfDay $ head parsedTimes) $ Map.keys utctimeEntryMap in zipWith4 DiddoEntry (formatZonedTime inDateFmt (startOfDay $ head parsedTimes) : init timeStamps) timeStamps deltasHMS entryTexts readFilesToLines :: [String] -> IO [String] readFilesToLines filenames = lines . concat <$> mapM readFile filenames main :: IO () main = do argv <- getArgs case getOpt Permute options argv of (opts,args,[]) -> do let logFileNames = [file | InputFile file <- opts] inDateFmt = head [fmt | InputFormat fmt <- opts] logLines <- readFilesToLines logFileNames mapM_ print $ logLinesToDiddohs inDateFmt logLines (_,_,errs) -> do hPutStr stderr $ usageInfo header options ioError (userError ('\n' : concat errs)) where header = "Usage: diddohs [OPTION...]"