diff options
author | Gregor Kleen <aethoago@141.li> | 2017-02-15 19:04:57 +0100 |
---|---|---|
committer | Gregor Kleen <aethoago@141.li> | 2017-02-15 19:04:57 +0100 |
commit | 127c1212d7704392363e3614f339627bf514cfcf (patch) | |
tree | 10e9a0a1f48ec8aef518c3c33a6e34a7043d9738 /lib/Postdelay/TimeSpec.hs | |
parent | 5b09b096e38ed231b62df57736e87c989b481b5d (diff) | |
download | postdelay-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.hs | 49 |
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 | ||
3 | module Postdelay.TimeSpec | 3 | module 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 | |||
15 | import Control.Monad.IO.Class | 15 | import Control.Monad.IO.Class |
16 | import Control.Applicative | 16 | import Control.Applicative |
17 | import Control.Lens hiding ((#)) | 17 | import Control.Lens hiding ((#)) |
18 | import Control.Exception (IOException) | ||
19 | import Control.Monad.Catch | ||
18 | 20 | ||
19 | import Data.Bool | 21 | import Data.Bool |
20 | import Data.Semigroup hiding (option) | 22 | import Data.Semigroup hiding (option) |
@@ -30,22 +32,49 @@ import Data.Time.Zones | |||
30 | seconds' :: Timeable t => Lens' t Time | 32 | seconds' :: Timeable t => Lens' t Time |
31 | seconds' = seconds . iso (% Second) (# Second) | 33 | seconds' = seconds . iso (% Second) (# Second) |
32 | 34 | ||
35 | utcOffset :: Iso' TimeZone Time | ||
36 | utcOffset = iso ((% Minute) . fromIntegral . timeZoneMinutes) (minutesToTimeZone . round . (# Minute)) | ||
33 | 37 | ||
34 | pTimeSpec :: StringParser s m => m (Endo LocalTime) | 38 | |
35 | pTimeSpec = label "Relative time specification" $ | 39 | timeSpec :: StringParser s m => m (Endo LocalTime) |
36 | choice [ pOffsets False | 40 | timeSpec = label "Relative time specification" $ |
41 | choice [ offsets False | ||
37 | ] | 42 | ] |
38 | 43 | ||
39 | pOffsets :: forall s m. StringParser s m | 44 | offsets :: 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) |
42 | pOffsets (bool optSigned signed -> reqSgn) = fmap fold $ (:) <$> offset reqSgn <*> many (offset optSigned) | 47 | offsets (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 | ||
49 | pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) | 54 | timeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) |
50 | pTimeZone = label "Timezone" $ | 55 | timeZone = label "Timezone" $ (Left <$> numericTimezone) <|> (Right <$> namedTimezone) |
51 | empty | 56 | |
57 | numericTimezone :: StringParser s m => m TimeZone | ||
58 | numericTimezone = 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 | |||
64 | namedTimezone :: (StringParser s m, MonadIO m) => m TZ | ||
65 | namedTimezone = 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" | ||