summaryrefslogtreecommitdiff
path: root/lib/Postdelay/TimeSpec.hs
diff options
context:
space:
mode:
authorGregor Kleen <aethoago@141.li>2017-02-15 19:04:57 +0100
committerGregor Kleen <aethoago@141.li>2017-02-15 19:04:57 +0100
commit127c1212d7704392363e3614f339627bf514cfcf (patch)
tree10e9a0a1f48ec8aef518c3c33a6e34a7043d9738 /lib/Postdelay/TimeSpec.hs
parent5b09b096e38ed231b62df57736e87c989b481b5d (diff)
downloadpostdelay-127c1212d7704392363e3614f339627bf514cfcf.tar
postdelay-127c1212d7704392363e3614f339627bf514cfcf.tar.gz
postdelay-127c1212d7704392363e3614f339627bf514cfcf.tar.bz2
postdelay-127c1212d7704392363e3614f339627bf514cfcf.tar.xz
postdelay-127c1212d7704392363e3614f339627bf514cfcf.zip
Timezones
Diffstat (limited to 'lib/Postdelay/TimeSpec.hs')
-rw-r--r--lib/Postdelay/TimeSpec.hs49
1 files changed, 39 insertions, 10 deletions
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs
index 03baf85..fbbce6b 100644
--- a/lib/Postdelay/TimeSpec.hs
+++ b/lib/Postdelay/TimeSpec.hs
@@ -1,8 +1,8 @@
1{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, ViewPatterns, ConstraintKinds, TupleSections, RecordWildCards, TypeFamilies #-} 1{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, ViewPatterns, ConstraintKinds, TupleSections, RecordWildCards, TypeFamilies #-}
2 2
3module Postdelay.TimeSpec 3module Postdelay.TimeSpec
4 ( pTimeSpec 4 ( timeSpec
5 , pTimeZone 5 , timeZone
6 6
7 , spaceConsumer, lexeme 7 , spaceConsumer, lexeme
8 ) where 8 ) where
@@ -15,6 +15,8 @@ import Text.Megaparsec
15import Control.Monad.IO.Class 15import Control.Monad.IO.Class
16import Control.Applicative 16import Control.Applicative
17import Control.Lens hiding ((#)) 17import Control.Lens hiding ((#))
18import Control.Exception (IOException)
19import Control.Monad.Catch
18 20
19import Data.Bool 21import Data.Bool
20import Data.Semigroup hiding (option) 22import Data.Semigroup hiding (option)
@@ -30,22 +32,49 @@ import Data.Time.Zones
30seconds' :: Timeable t => Lens' t Time 32seconds' :: Timeable t => Lens' t Time
31seconds' = seconds . iso (% Second) (# Second) 33seconds' = seconds . iso (% Second) (# Second)
32 34
35utcOffset :: Iso' TimeZone Time
36utcOffset = iso ((% Minute) . fromIntegral . timeZoneMinutes) (minutesToTimeZone . round . (# Minute))
33 37
34pTimeSpec :: StringParser s m => m (Endo LocalTime) 38
35pTimeSpec = label "Relative time specification" $ 39timeSpec :: StringParser s m => m (Endo LocalTime)
36 choice [ pOffsets False 40timeSpec = label "Relative time specification" $
41 choice [ offsets False
37 ] 42 ]
38 43
39pOffsets :: forall s m. StringParser s m 44offsets :: forall s m. StringParser s m
40 => Bool -- ^ Require sign on first offset? 45 => Bool -- ^ Require sign on first offset?
41 -> m (Endo LocalTime) 46 -> m (Endo LocalTime)
42pOffsets (bool optSigned signed -> reqSgn) = fmap fold $ (:) <$> offset reqSgn <*> many (offset optSigned) 47offsets (bool optSigned signed -> reqSgn) = fmap fold $ (:) <$> offset reqSgn <*> many (offset optSigned)
43 where 48 where
44 asOffset :: Time -> Endo LocalTime 49 asOffset :: Time -> Endo LocalTime
45 asOffset by = Endo $ flexDT.seconds' %~ (^+^ by) 50 asOffset by = Endo $ flexDT.seconds' %~ (^+^ by)
46 offset :: (m Time -> m Time) -> m (Endo LocalTime) 51 offset :: (m Time -> m Time) -> m (Endo LocalTime)
47 offset sgn = asOffset <$> lexeme (sgn timeLength) <?> "Time offset" 52 offset sgn = asOffset <$> lexeme (sgn timeLength) <?> "Time offset"
48 53
49pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) 54timeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ)
50pTimeZone = label "Timezone" $ 55timeZone = label "Timezone" $ (Left <$> numericTimezone) <|> (Right <$> namedTimezone)
51 empty 56
57numericTimezone :: StringParser s m => m TimeZone
58numericTimezone = review utcOffset <$> (sign <*> ((^+^) <$> hour <*> minute)) <?> "Numeric timezone"
59 where
60 hour = (% Hour) <$> twoDigit
61 minute = (% Minute) <$> twoDigit
62 twoDigit = (\n1 n2 -> fromDigit n1 * 10 + fromDigit n2) <$> digitChar <*> digitChar
63
64namedTimezone :: (StringParser s m, MonadIO m) => m TZ
65namedTimezone = do
66 n <- ident
67 tz <- liftIO $ do
68 let
69 fbHandler :: IO a -> (IOException -> IO a)
70 fbHandler fb _ = fb
71 foldl (\fb a -> a `catch` fbHandler fb) (return Nothing)
72 [ Just <$> loadSystemTZ n
73 , Just <$> loadTZFromDB n
74 ]
75 case tz of
76 Nothing -> fail $ "Could not resolve timezone: " ++ show n
77 (Just tz) -> return tz
78 where
79 asciiAlphaNum = oneOf $ ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z']
80 ident = (++) <$> some asciiAlphaNum <*> option [] ((:) <$> oneOf "_-/.+" <*> ident) <?> "Named timezone identifier"