on master help sign in
Spruce up the time parser module, make it public
commit92227a37cec179613589526cbc19c4faad6ab6ea authorBryan O'Sullivan <bos@serpentine.com> dateSat 18 Jul 2015, 00:19 (11 years ago) parentf88416b
2 files changed+39−9
Data/Aeson/Parser/Time.hs+38−8 aeson.cabal+1−1
Data/Aeson/Parser/Time.hs+38−8
@@ -1,5 +1,15 @@
11{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
22
3+-- |
4+-- Module: Data.Aeson.Parser.Time
5+-- Copyright: (c) 2015 Bryan O'Sullivan
6+-- License: Apache
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
8+-- Stability: experimental
9+-- Portability: portable
10+--
11+-- Parsers for parsing dates and times.
12+
313module Data.Aeson.Parser.Time
414 (
515 run
@@ -25,11 +35,13 @@ import qualified Data.Aeson.Types.Internal as Aeson
2535import qualified Data.Text as T
2636import qualified Data.Time.LocalTime as Local
2737
38+-- | Run an attoparsec parser as an aeson parser.
2839run :: Parser a -> Text -> Aeson.Parser a
2940run p t = case A.parseOnly (p <* endOfInput) t of
30− Left err -> fail $ "could not parse date: " ++ err
31− Right r -> return r
41+ Left err -> fail $ "could not parse date: " ++ err
42+ Right r -> return r
3243
44+-- | Parse a date of the form @YYYY-MM-DD@.
3345day :: Parser Day
3446day = do
3547 y <- decimal <* char '-'
@@ -37,26 +49,28 @@ day = do
3749 d <- twoDigits
3850 maybe (fail "invalid date") return (fromGregorianValid y m d)
3951
40−c2d :: Char -> Int
41−c2d c = ord c .&. 15
42−
52+-- | Parse a two-digit integer (e.g. day of month, hour).
4353twoDigits :: Parser Int
4454twoDigits = do
4555 a <- digit
4656 b <- digit
57+ let c2d c = ord c .&. 15
4758 return $! c2d a * 10 + c2d b
4859
60+-- | Parse a time of the form @HH:MM:SS[.SSS]@.
4961timeOfDay :: Parser Local.TimeOfDay
5062timeOfDay = do
5163 h <- twoDigits <* char ':'
5264 m <- twoDigits <* char ':'
53− s <- pico
65+ s <- seconds
5466 if h < 24 && m < 60 && s < 61
5567 then return (Local.TimeOfDay h m s)
5668 else fail "invalid time"
5769
58−pico :: Parser Pico
59−pico = do
70+-- | Parse a count of seconds., with the integer part being two digits
71+-- long.
72+seconds :: Parser Pico
73+seconds = do
6074 w <- twoDigits
6175 mc <- peekChar
6276 case mc of
@@ -65,6 +79,8 @@ pico = do
6579 return $! fromIntegral w + fromIntegral f / 10 ^ T.length t
6680 _ -> return $! fromIntegral w
6781
82+-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
83+-- zero. (This makes some speedups possible.)
6884timeZone :: Parser (Maybe Local.TimeZone)
6985timeZone = do
7086 let maybeSkip c = do ch <- peekChar'; when (ch == c) (void anyChar)
@@ -86,10 +102,15 @@ timeZone = do
86102 let !tz = Local.minutesToTimeZone off
87103 in return (Just tz)
88104
105+-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM:SS@.
106+-- The space may be replaced with a @T@. The number of seconds may be
107+-- followed by a fractional component.
89108localTime :: Parser Local.LocalTime
90109localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay
91110 where daySep = satisfy (\c -> c == 'T' || c == ' ')
92111
112+-- | Behaves as 'zonedTime', but converts any time zone offset into a
113+-- UTC time.
93114utcTime :: Parser UTCTime
94115utcTime = do
95116 lt@(Local.LocalTime d t) <- localTime
@@ -99,6 +120,15 @@ utcTime = do
99120 in return (UTCTime d tt)
100121 Just tz -> return $! Local.localTimeToUTC tz lt
101122
123+-- | Parse a date with time zone info. Acceptable formats:
124+--
125+-- @YYYY-MM-DD HH:MM:SS Z@
126+--
127+-- The first space may instead be a @T@, and the second space is
128+-- optional. The @Z@ represents UTC. The @Z@ may be replaced with a
129+-- time zone offset of the form @+0000@ or @-08:00@, where the first
130+-- two digits are hours, the @:@ is optional and the second two digits
131+-- are minutes.
102132zonedTime :: Parser Local.ZonedTime
103133zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)
104134
aeson.cabal+1−1
@@ -90,6 +90,7 @@ library
9090 Data.Aeson.Encode
9191 Data.Aeson.Internal
9292 Data.Aeson.Parser
93+ Data.Aeson.Parser.Time
9394 Data.Aeson.Types
9495 Data.Aeson.TH
9596
@@ -98,7 +99,6 @@ library
9899 Data.Aeson.Encode.Functions
99100 Data.Aeson.Functions
100101 Data.Aeson.Parser.Internal
101− Data.Aeson.Parser.Time
102102 Data.Aeson.Types.Class
103103 Data.Aeson.Types.Generic
104104 Data.Aeson.Types.Instances