on master help sign in
Fix integer overflow and optimize subsecond parsing

Previously we had this behavior: > decode "[\"2014-07-18 12:00:00.99999999999999Z\"]" :: Maybe [UTCTime] Just [2014-07-18 12:00:00.999999999999 UTC] > decode "[\"2014-07-18 12:00:00.9999999999999999999999Z\"]" :: Maybe [UTCTime] Just [2014-07-18 12:00:00.000186471204 UTC] Now the digits past picoseconds are truncated in all cases.

commitaf9d81aad85845e3cbf82c96854b0b0785171035 authorLeon P Smith <leon@melding-monads.com> dateSat 18 Jul 2015, 13:56 (11 years ago) parentebd408a
1 file changed+23−3
Data/Aeson/Parser/Time.hs+23−3
Data/Aeson/Parser/Time.hs+23−3
@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
1+{-# LANGUAGE BangPatterns, ScopedTypeVariables, CPP #-}
22
33-- |
44-- Module: Data.Aeson.Parser.Time
@@ -34,6 +34,17 @@ import Data.Time.Clock (UTCTime(..))
3434import qualified Data.Aeson.Types.Internal as Aeson
3535import qualified Data.Text as T
3636import qualified Data.Time.LocalTime as Local
37+#if !MIN_VERSION_base(4,7,0)
38+import Unsafe.Coerce
39+
40+mkPico :: Integer -> Pico
41+mkPico = unsafeCoerce
42+#else
43+import Data.Fixed (Fixed(MkFixed))
44+
45+mkPico :: Integer -> Pico
46+mkPico = MkFixed
47+#endif
3748
3849-- | Run an attoparsec parser as an aeson parser.
3950run :: Parser a -> Text -> Aeson.Parser a
@@ -75,10 +86,19 @@ seconds = do
7586 mc <- peekChar
7687 case mc of
7788 Just '.' -> do
78− (t,f :: Int64) <- anyChar *> match decimal
79− return $! fromIntegral w + fromIntegral f / 10 ^ T.length t
89+ t <- anyChar *> takeWhile1 (\c -> c >= '0' && c <= '9')
90+ return $! parsePicos w t
8091 _ -> return $! fromIntegral w
8192
93+parsePicos :: Int -> T.Text -> Pico
94+parsePicos a0 t =
95+ let (n,t') = T.foldl' step (12 :: Int, fromIntegral a0 :: Int64) t
96+ in mkPico (fromIntegral (t' * 10^n))
97+ where step na@(n,a) c
98+ | n <= 0 = na
99+ | otherwise = mkPair (n-1) (10 * a + fromIntegral (ord c) .&. 15)
100+ mkPair !x !y = (x,y)
101+
82102-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
83103-- zero. (This makes some speedups possible.)
84104timeZone :: Parser (Maybe Local.TimeZone)