diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Postdelay/Scan.hs | 27 | ||||
-rw-r--r-- | lib/Postdelay/TimeSpec.hs | 7 |
2 files changed, 21 insertions, 13 deletions
diff --git a/lib/Postdelay/Scan.hs b/lib/Postdelay/Scan.hs index fba9f35..2f0a78a 100644 --- a/lib/Postdelay/Scan.hs +++ b/lib/Postdelay/Scan.hs | |||
@@ -1,4 +1,4 @@ | |||
1 | {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, FlexibleContexts, RecordWildCards, ViewPatterns #-} | 1 | {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, FlexibleContexts, RecordWildCards, ViewPatterns, TupleSections #-} |
2 | 2 | ||
3 | module Postdelay.Scan | 3 | module Postdelay.Scan |
4 | ( scan | 4 | ( scan |
@@ -45,24 +45,29 @@ scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay) | |||
45 | scan = fmap getOption . extractDelay <=< either throwError return . parse message "" | 45 | scan = fmap getOption . extractDelay <=< either throwError return . parse message "" |
46 | 46 | ||
47 | extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) | 47 | extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) |
48 | extractDelay (Message headers _) | 48 | extractDelay (Message headers _) = do |
49 | = let latestCtx = maximumBy (comparing baseTime) dateHeaders | 49 | zones <- zoneHeaders |
50 | in foldMap pure <$> mapM (flip runReaderT latestCtx . parseDelay) delayHeaders | 50 | let (baseTime, dateTz) = maximumBy (comparing fst) dateHeaders |
51 | tz = foldr' (flip (<>)) (Left dateTz) zones | ||
52 | foldMap pure <$> mapM (flip runReaderT TimeCtx{..} . parseDelay) delayHeaders | ||
51 | where | 53 | where |
52 | delayHeaders :: [Field] | 54 | delayHeaders :: [Field] |
53 | delayHeaders = do | 55 | delayHeaders = do |
54 | (OptionalField field content) <- headers | 56 | (OptionalField field content) <- headers |
55 | guard $ CI.mk field == "X-Delay" | 57 | guard $ CI.mk field == "X-Delay" |
56 | return . OptionalField field $ decodeWords content | 58 | return . OptionalField field $ decodeWords content |
57 | dateHeaders :: [TimeCtx] | 59 | dateHeaders :: [(UTCTime, TimeZone)] |
58 | dateHeaders = do | 60 | dateHeaders = do |
59 | (Date CalendarTime{..}) <- headers | 61 | (Date CalendarTime{..}) <- headers |
60 | let tz = minutesToTimeZone . round $ ctTZ % 60 | 62 | let tz = minutesToTimeZone . round $ ctTZ % 60 |
61 | return $ TimeCtx | 63 | return . (, tz) . localTimeToUTC tz $ LocalTime |
62 | { baseTime = localTimeToUTC tz $ LocalTime | 64 | (fromGregorian (fromIntegral ctYear) (fromEnum ctMonth + 1) ctDay) |
63 | (fromGregorian (fromIntegral ctYear) (fromEnum ctMonth + 1) ctDay) | 65 | (TimeOfDay ctHour ctMin $ fromIntegral ctSec + fromIntegral ctPicosec * 1e-12) |
64 | (TimeOfDay ctHour ctMin $ fromIntegral ctSec + fromIntegral ctPicosec * 1e-12) | 66 | zoneHeaders :: m [Either TimeZone TZ] |
65 | , tz = Left tz | 67 | zoneHeaders = runListT $ do |
66 | } | 68 | (OptionalField field content) <- ListT $ return headers |
69 | guard $ CI.mk field == "X-Timezone" | ||
70 | Right r <- runParserT (spaces *> pTimeZone <* spaces <* eof) () field content | ||
71 | return r | ||
67 | parseDelay :: Field -> ReaderT TimeCtx m Delay | 72 | parseDelay :: Field -> ReaderT TimeCtx m Delay |
68 | parseDelay (OptionalField field content) = either throwError return =<< runParserT (Until <$> pTimeSpec) () field content | 73 | parseDelay (OptionalField field content) = either throwError return =<< runParserT (Until <$> pTimeSpec) () field content |
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs index af8d801..676dabf 100644 --- a/lib/Postdelay/TimeSpec.hs +++ b/lib/Postdelay/TimeSpec.hs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | module Postdelay.TimeSpec | 3 | module Postdelay.TimeSpec |
4 | ( pTimeSpec | 4 | ( pTimeSpec |
5 | , pTimeZone | ||
5 | , TimeCtx(..) | 6 | , TimeCtx(..) |
6 | ) where | 7 | ) where |
7 | 8 | ||
@@ -308,7 +309,7 @@ pTimeBase = choice' [ do | |||
308 | pMinute = ensure (< 60) =<< natural | 309 | pMinute = ensure (< 60) =<< natural |
309 | pSecond = decimal | 310 | pSecond = decimal |
310 | 311 | ||
311 | pTimeZone :: MonadTP m => ParsecT String () m (Either TimeZone TZ) | 312 | pTimeZone :: MonadIO m => ParsecT String () m (Either TimeZone TZ) |
312 | pTimeZone = choice' [ do | 313 | pTimeZone = choice' [ do |
313 | sgn <- choice [ id <$ char '+' | 314 | sgn <- choice [ id <$ char '+' |
314 | , negate <$ char '-' | 315 | , negate <$ char '-' |
@@ -317,7 +318,9 @@ pTimeZone = choice' [ do | |||
317 | ms <- option 0 $ (\d u -> 10 * d + u) <$> digit <*> digit | 318 | ms <- option 0 $ (\d u -> 10 * d + u) <$> digit <*> digit |
318 | return . Left . minutesToTimeZone $ hs * 60 + ms | 319 | return . Left . minutesToTimeZone $ hs * 60 + ms |
319 | , do | 320 | , do |
320 | n <- many1 $ letter <|> char '/' | 321 | let |
322 | ident = (++) <$> many1 alphaNum <*> option "" ((:) <$> oneOf "_-/.+" <*> ident) | ||
323 | n <- ident | ||
321 | tz <- liftIO $ do | 324 | tz <- liftIO $ do |
322 | let | 325 | let |
323 | fbHandler :: IO a -> (IOException -> IO a) | 326 | fbHandler :: IO a -> (IOException -> IO a) |