@@ -104,39 +104,48 @@ getDay = do
104104getDate :: A.Parser Date
105105getDate = getUnbounded getDay
106106
107−decimal :: Fractional a => B.ByteString -> a
108−decimal str = toNum str / 10^(B.length str)
109−{-# INLINE decimal #-}
110−
111107getTimeOfDay :: A.Parser TimeOfDay
112108getTimeOfDay = do
113109 !hour <- digits "hours"
114110 _ <- A.char ':'
115111 minute <- digits "minutes"
116− _ <- A.char ':'
117− second <- digits "seconds"
118− subsec <- (A.char '.' *> (decimal <$> A.takeWhile1 A.isDigit)) <|> return 0
112+ sec <- (A.char ':' *> getSeconds) <|> pure 0
119113
120− let !picos' = second + subsec
121−
122− case makeTimeOfDayValid hour minute picos' of
114+ case makeTimeOfDayValid hour minute sec of
123115 Nothing -> fail "invalid time of day"
124116 Just x -> return $! x
117+ where
118+ getSeconds :: A.Parser Pico
119+ getSeconds = do
120+ secs <- digits "seconds"
121+ decimal secs <|> (return $! fromInteger secs)
122+
123+ decimal :: Integer -> A.Parser Pico
124+ decimal secs = do
125+ _ <- A.satisfy (\c -> c == '.' || c == ',')
126+ digits <- B.take 12 <$> A.takeWhile1 A.isDigit
127+ -- A kludge to work around the fact that Data.Fixed isn't very fast and
128+ -- doesn't give me access to the MkFixed constructor.
129+ return $! unsafeCoerce (toNum_ secs digits * 10^(12 - B.length digits))
125130
126131getLocalTime :: A.Parser LocalTime
127−getLocalTime = LocalTime <$> getDay <*> (A.char ' ' *> getTimeOfDay)
132+getLocalTime = LocalTime <$> getDay <*> (todSeparator *> getTimeOfDay)
133+ where todSeparator = A.satisfy (\c -> c == ' ' || c == 'T')
128134
129135getLocalTimestamp :: A.Parser LocalTimestamp
130136getLocalTimestamp = getUnbounded getLocalTime
131137
132138getTimeZone :: A.Parser TimeZone
133139getTimeZone = do
134− !sign <- A.satisfy (\c -> c == '+' || c == '-')
135− !hours <- digits "timezone"
136− !mins <- (A.char ':' *> digits "timezone minutes") <|> pure 0
137− let !absset = 60 * hours + mins
138− !offset = if sign == '+' then absset else -absset
139− return $! minutesToTimeZone offset
140+ !sign <- A.satisfy (\c -> c == '+' || c == '-' || c == 'Z')
141+ if sign == 'Z'
142+ then return utc
143+ else do
144+ !hours <- digits "timezone"
145+ !mins <- (A.char ':' *> digits "timezone minutes") <|> pure 0
146+ let !absset = 60 * hours + mins
147+ !offset = if sign == '+' then absset else -absset
148+ return $! minutesToTimeZone offset
140149
141150type TimeZoneHMS = (Int,Int,Int)
142151
@@ -189,9 +198,12 @@ getUTCTimestamp :: A.Parser UTCTimestamp
189198getUTCTimestamp = getUnbounded getUTCTime
190199
191200toNum :: Num n => B.ByteString -> n
192−toNum = B.foldl' (\a c -> 10*a + digit c) 0
201+toNum = toNum_ 0
193202{-# INLINE toNum #-}
194203
204+toNum_ :: Num n => n -> B.ByteString -> n
205+toNum_ = B.foldl' (\a c -> 10*a + digit c)
206+
195207digit :: Num n => Word8 -> n
196208digit c = fromIntegral (c .&. 0x0f)
197209{-# INLINE digit #-}