module Data.DateTime.Parser( Format(..), expr, time, weekday, month, duration, date, parseDate, repetition, exprs, limExpr) where import Text.Parsec import Data.DateTime import Data.Time.Clock (utctDayTime) import Data.Duration import Control.Concatenative import Control.Monad import Control.Monad.Reader data Format = US | World deriving (Eq, Show, Read) type Parser = ParsecT String () (Reader (Format,DateTime)) a <<| b = a >> (return b) exprs :: Parser [DateTime] exprs = liftM2 (++) expr (liftM concat $ many (char ',' >> expr)) expr :: Parser [DateTime] expr = do t <- option 0 (try time) optional spaces d <- optionMaybe (try date) d' <- case d of Nothing -> asks (\x-> (snd x){utctDayTime=fromInteger 0}) (Just a) -> return a t' <- option t (optional spaces >> try time) let dt = addMinutes t' d' optional spaces r <- optionMaybe (repetition dt) return $ case r of Nothing -> [dt] (Just a) -> a limExpr :: Parser [DateTime] limExpr = do t <- option 0 (try time) d <- asks (\x-> (snd x){utctDayTime=fromInteger 0}) let dt = addMinutes t d optional spaces r <- optionMaybe (repetition dt) return $ case r of Nothing -> [dt] (Just a) -> a time :: Parser Integer time = do a <- digit b <- optionMaybe digit let hours = ifte (==12) (const 0) id (case b of {(Just i)-> read [a,i]; Nothing -> read [a]}) *60 char ':' c <- digit d <- digit let minutes = read [c,d] amPm <- optionMaybe . try $ spaces >> (((string "AM" <|> string "am") <<| 0) <|> ((string "PM" <|> string "pm") <<|720)) return $ case amPm of (Just a) -> hours + minutes + a Nothing -> ifte (<= 360) (+720) id (hours + minutes) weekday :: Parser Int weekday = (string "Monday" <<|1) <|> try (string "Tuesday" <<|2) <|> (string "Wednesday" <<|3) <|> (string "Thursday" <<|4) <|> (string "Friday" <<|5) <|> try (string "Saturday" <<|6) <|> (string "Sunday" <<|0) month :: Parser Int month = try (string "January" <<|1) <|> (string "February" <<|2) <|> try (string "March" <<|3) <|> try (string "April" <<|4) <|> (string "May" <<|5) <|> try (string "June" <<|6) <|> (string "July" <<|7) <|> (string "August" <<|8) <|> (string "September" <<|9) <|> (string "October" <<|10) <|> (string "November" <<|11) <|> (string "December" <<|12) duration :: Parser Days duration = (string "day" <<|Day) <|> (string "week" <<|Week) <|> (string "month" <<|Month) <|> (string "year" <<|Year) date :: Parser DateTime date = do c <- asks snd let dur = duration >>= (return . toDuration 1) >>= (\x-> return $ x c) let day = liftM (fixTime (addWeeks 1 c)) weekday (string "next " >> (day <|> dur)) <|> parseDate parseDate = slashed <|> long where digits :: (Read a, Integral a) => Parser a digits = liftM read (many1 digit) slashed = do a <- digits :: Parser Int b <- char '/' >> digits :: Parser Int c <- char '/' >> digits :: Parser Integer (y,_,_) <- asks (toGregorian' . snd) f <- asks ((==US) . fst) return $ uncurry (fromGregorian' (y - (y `mod` 1000) + c)) $ if f then (a,b) else (b,a) long = do optional $ try (weekday >> spaces) m <- month d <- spaces >> digits y <- optionMaybe (char ',' >> spaces >> digits) (c,_,_) <- asks (toGregorian' . snd) return $ fromGregorian' (maybe c id y) m d repetition :: DateTime -> Parser [DateTime] repetition t = do (c,t') <- weekRep <|> everyRep l <- optionMaybe $ (string " for " >> numbered) <|> (string " until " >> date >>= (\x-> return (const x,t))) l' <- return $ case l of {Nothing -> Nothing; (Just (a,_)) -> Just a} return (assemble t' c l') where weekRep = do {a <- nDay 1; char 's' >> return a} everyRep = string "every " >> (numbered <|> othDur <|> nDay 1 <|> nDur 1) nDur n = duration >>= (\x-> return (toDuration n x,t)) nDay n = weekday >>= (\x->return (toDuration n Week, fixTime t x)) othDur = string "other " >> (nDay 2 <|> nDur 2) numbered = do a <- many1 digit a' <- return $ read a b <- spaces >> (nDur a' <|> nDay a') optional (char 's') return b