on master help sign in
Make ZonedTime encoding 3x faster
commit298f0a0b216cd9b6ac7745cf8ffde09878898927 authorBryan O'Sullivan <bos@serpentine.com> dateMon 20 Jul 2015, 21:03 (11 years ago) parentfa4dfb6
2 files changed+37−21
Data/Aeson/Encode/Builder.hs+27−1 Data/Aeson/Types/Instances.hs+10−20
Data/Aeson/Encode/Builder.hs+27−1
@@ -26,6 +26,9 @@ module Data.Aeson.Encode.Builder
2626 , number
2727 , day
2828 , timeOfDay
29+ , timeZone
30+ , utcTime
31+ , zonedTime
2932 , ascii2
3033 , ascii4
3134 , ascii5
@@ -40,8 +43,9 @@ import Data.Char (chr, ord)
4043import Data.Foldable (foldMap)
4144import Data.Monoid ((<>))
4245import Data.Scientific (Scientific, base10Exponent, coefficient)
46+import Data.Time (UTCTime(..))
4347import Data.Time.Calendar (Day(..), toGregorian)
44−import Data.Time.LocalTime (TimeOfDay(..))
48+import Data.Time.LocalTime
4549import Data.Word (Word8)
4650import qualified Data.HashMap.Strict as HMS
4751import qualified Data.Text as T
@@ -195,6 +199,28 @@ timeOfDay (TimeOfDay h m s)
195199 !c = digit (cc `quot` 10)
196200{-# INLINE timeOfDay #-}
197201
202+timeZone :: TimeZone -> Builder
203+timeZone (TimeZone off _ _)
204+ | off == 0 = B.char7 'Z'
205+ | otherwise = BP.primBounded (ascii5 (s,(hh,(hl,(mh,ml))))) ()
206+ where !s = if off < 0 then '-' else '+'
207+ !(T hh hl) = twoDigits h
208+ !(T mh ml) = twoDigits m
209+ (h,m) = abs off `quotRem` 60
210+{-# INLINE timeZone #-}
211+
212+dayTime :: Day -> TimeOfDay -> Builder
213+dayTime d t = day d <> B.char7 'T' <> timeOfDay t
214+{-# INLINE dayTime #-}
215+
216+utcTime :: UTCTime -> B.Builder
217+utcTime (UTCTime d s) = dayTime d (timeToTimeOfDay s) <> B.char7 'Z'
218+{-# INLINE utcTime #-}
219+
220+zonedTime :: ZonedTime -> Builder
221+zonedTime (ZonedTime (LocalTime d t) z) = dayTime d t <> timeZone z
222+{-# INLINE zonedTime #-}
223+
198224data T = T {-# UNPACK #-} !Char {-# UNPACK #-} !Char
199225
200226twoDigits :: Int -> T
Data/Aeson/Types/Instances.hs+10−20
@@ -61,7 +61,7 @@ import qualified Data.ByteString.Builder as B
6161import qualified Data.ByteString.Lazy as L
6262import Data.Aeson.Functions
6363import Data.Monoid ((<>), mempty)
64−import Data.Aeson.Encode.Functions (brackets, builder, foldable, list)
64+import Data.Aeson.Encode.Functions (brackets, builder, encode, foldable, list)
6565import qualified Data.Aeson.Parser.Time as Time
6666import Data.Aeson.Types.Class
6767import Data.Aeson.Types.Internal
@@ -79,7 +79,6 @@ import Data.Ratio (Ratio, (%), numerator, denominator)
7979import Data.Text (Text, pack, unpack)
8080import Data.Time (UTCTime(..), ZonedTime(..), TimeZone(..))
8181import Data.Time.Format (FormatTime, formatTime, parseTime)
82−import Data.Time.LocalTime (timeToTimeOfDay)
8382import Data.Traversable as Tr (sequence, traverse)
8483import Data.Vector (Vector)
8584import Data.Word (Word, Word8, Word16, Word32, Word64)
@@ -713,34 +712,25 @@ instance FromJSON DotNetTime where
713712 {-# INLINE parseJSON #-}
714713
715714instance ToJSON ZonedTime where
716− toJSON = toJSON . zonedTime
715+ toJSON = stringEncoding
717716
718− toEncoding = toEncoding . zonedTime
719−
720−zonedTime :: ZonedTime -> String
721−zonedTime t = formatTime defaultTimeLocale format t
722− where
723− format = "%FT%T." ++ formatMillis t ++ tzFormat
724− tzFormat | timeZoneMinutes (zonedTimeZone t) == 0 = "Z"
725− | otherwise = "%z"
717+ toEncoding z = Encoding (E.zonedTime z)
726718
727719formatMillis :: (FormatTime t) => t -> String
728−formatMillis = take 3 . formatSubseconds
729−
730−formatSubseconds :: (FormatTime t) => t -> String
731−formatSubseconds = formatTime defaultTimeLocale "%q"
720+formatMillis = take 3 . formatTime defaultTimeLocale "%q"
732721
733722instance FromJSON ZonedTime where
734723 parseJSON = withText "ZonedTime" (Time.run Time.zonedTime)
735724
736725instance ToJSON UTCTime where
737− toJSON = String . T.decodeLatin1 . L.toStrict . B.toLazyByteString . utcTime
726+ toJSON = stringEncoding
738727
739− toEncoding t = Encoding (utcTime t)
728+ toEncoding t = Encoding (E.utcTime t)
740729
741−utcTime :: UTCTime -> B.Builder
742−utcTime (UTCTime day secs) =
743− E.day day <> B.char7 'T' <> E.timeOfDay (timeToTimeOfDay secs) <> B.char7 'Z'
730+-- | Encode something to a JSON string.
731+stringEncoding :: (ToJSON a) => a -> Value
732+stringEncoding = String . T.decodeLatin1 . L.toStrict . encode
733+{-# INLINE stringEncoding #-}
744734
745735instance FromJSON UTCTime where
746736 parseJSON = withText "UTCTime" (Time.run Time.utcTime)