diff --git a/Data/Aeson/Encode.hs b/Data/Aeson/Encode.hs
--- a/Data/Aeson/Encode.hs
+++ b/Data/Aeson/Encode.hs
@@ -29,10 +29,9 @@
 
 import Data.Aeson.Types (Value(..))
 import Data.Monoid (mappend)
-import Data.Scientific (Scientific, coefficient, base10Exponent)
+import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
 import Data.Text.Lazy.Builder
-import Data.Text.Lazy.Builder.Int (decimal)
-import Data.Text.Lazy.Builder.Scientific (scientificBuilder)
+import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
 import Numeric (showHex)
 import qualified Data.HashMap.Strict as H
 import qualified Data.Text as T
@@ -77,8 +76,6 @@
         where (h,t) = {-# SCC "break" #-} T.break isEscape q
     isEscape c = c == '\"' ||
                  c == '\\' ||
-                 c == '<'  ||
-                 c == '>'  ||
                  c < '\x20'
     escape '\"' = "\\\""
     escape '\\' = "\\\\"
@@ -86,22 +83,17 @@
     escape '\r' = "\\r"
     escape '\t' = "\\t"
 
-    -- The following prevents untrusted JSON strings containing </script> or -->
-    -- from causing an XSS vulnerability:
-    escape '<'  = "\\u003c"
-    escape '>'  = "\\u003e"
-
     escape c
         | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
         | otherwise  = singleton c
         where h = showHex (fromEnum c) ""
 
 fromScientific :: Scientific -> Builder
-fromScientific s
-    | e < 0     = scientificBuilder s
-    | otherwise = decimal (coefficient s * 10 ^ e)
+fromScientific s = formatScientificBuilder format prec s
   where
-    e = base10Exponent s
+    (format, prec)
+      | base10Exponent s < 0 = (Generic, Nothing)
+      | otherwise            = (Fixed,   Just 0)
 
 (<>) :: Builder -> Builder -> Builder
 (<>) = mappend
diff --git a/Data/Aeson/Parser/Internal.hs b/Data/Aeson/Parser/Internal.hs
--- a/Data/Aeson/Parser/Internal.hs
+++ b/Data/Aeson/Parser/Internal.hs
@@ -28,26 +28,31 @@
     , eitherDecodeStrictWith
     ) where
 
-import Data.ByteString.Builder
-  (Builder, byteString, toLazyByteString, charUtf8, word8)
-
 import Control.Applicative ((*>), (<$>), (<*), liftA2, pure)
+import Control.Monad.IO.Class (liftIO)
 import Data.Aeson.Types (Result(..), Value(..))
 import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific,
                                          skipSpace, string)
 import Data.Bits ((.|.), shiftL)
-import Data.ByteString (ByteString)
+import Data.ByteString.Internal (ByteString(..))
 import Data.Char (chr)
-import Data.Monoid (mappend, mempty)
 import Data.Text (Text)
 import Data.Text.Encoding (decodeUtf8')
+import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4)
+import Data.Text.Internal.Unsafe.Char (ord)
 import Data.Vector as Vector (Vector, fromList)
 import Data.Word (Word8)
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Ptr (minusPtr)
+import Foreign.Ptr (Ptr, plusPtr)
+import Foreign.Storable (poke)
+import System.IO.Unsafe (unsafePerformIO)
 import qualified Data.Attoparsec.ByteString as A
 import qualified Data.Attoparsec.Lazy as L
 import qualified Data.Attoparsec.Zepto as Z
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Unsafe as B
 import qualified Data.HashMap.Strict as H
 
@@ -109,7 +114,7 @@
 objectValues :: Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
 objectValues str val = do
   skipSpace
-  let pair = liftA2 (,) (str <* skipSpace) (char ':' *> skipSpace *> val)
+  let pair = liftA2 (,) (str <* skipSpace) (char ':' *> val)
   H.fromList <$> commaSeparated pair CLOSE_CURLY
 {-# INLINE objectValues #-}
 
@@ -154,6 +159,7 @@
 -- to preserve interoperability and security.
 value :: Parser Value
 value = do
+  skipSpace
   w <- A.peekWord8'
   case w of
     DOUBLE_QUOTE  -> A.anyWord8 *> (String <$> jstring_)
@@ -169,6 +175,7 @@
 -- | Strict version of 'value'. See also 'json''.
 value' :: Parser Value
 value' = do
+  skipSpace
   w <- A.peekWord8'
   case w of
     DOUBLE_QUOTE  -> do
@@ -198,7 +205,7 @@
                                         else Just (c == BACKSLASH)
   _ <- A.word8 DOUBLE_QUOTE
   s1 <- if BACKSLASH `B.elem` s
-        then case Z.parse unescape s of
+        then case unescape s of
             Right r  -> return r
             Left err -> fail err
          else return s
@@ -209,42 +216,55 @@
 
 {-# INLINE jstring_ #-}
 
-unescape :: Z.Parser ByteString
-unescape = toByteString <$> go mempty where
-  go acc = do
+unescape :: ByteString -> Either String ByteString
+unescape s = unsafePerformIO $ do
+  let len = B.length s
+  fp <- B.mallocByteString len
+  -- We perform no bounds checking when writing to the destination
+  -- string, as unescaping always makes it shorter than the source.
+  withForeignPtr fp $ \ptr -> do
+    ret <- Z.parseT (go ptr) s
+    case ret of
+      Left err -> return (Left err)
+      Right p -> do
+        let newlen = p `minusPtr` ptr
+            slop = len - newlen
+        Right <$> if slop >= 128 && slop >= len `quot` 4
+                  then B.create newlen $ \np -> B.memcpy np ptr newlen
+                  else return (PS fp 0 newlen)
+ where
+  go ptr = do
     h <- Z.takeWhile (/=BACKSLASH)
     let rest = do
           start <- Z.take 2
           let !slash = B.unsafeHead start
               !t = B.unsafeIndex start 1
-              escape = case B.findIndex (==t) "\"\\/ntbrfu" of
+              escape = case B.elemIndex t "\"\\/ntbrfu" of
                          Just i -> i
                          _      -> 255
           if slash /= BACKSLASH || escape == 255
             then fail "invalid JSON escape sequence"
-            else do
-            let cont m = go (acc `mappend` byteString h `mappend` m)
-                {-# INLINE cont #-}
+            else
             if t /= 117 -- 'u'
-              then cont (word8 (B.unsafeIndex mapping escape))
+              then copy h ptr >>= word8 (B.unsafeIndex mapping escape) >>= go
               else do
                    a <- hexQuad
                    if a < 0xd800 || a > 0xdfff
-                     then cont (charUtf8 (chr a))
+                     then copy h ptr >>= charUtf8 (chr a) >>= go
                      else do
                        b <- Z.string "\\u" *> hexQuad
                        if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
                          then let !c = ((a - 0xd800) `shiftL` 10) +
                                        (b - 0xdc00) + 0x10000
-                              in cont (charUtf8 (chr c))
+                              in copy h ptr >>= charUtf8 (chr c) >>= go
                          else fail "invalid UTF-16 surrogates"
     done <- Z.atEnd
     if done
-      then return (acc `mappend` byteString h)
+      then copy h ptr
       else rest
   mapping = "\"\\/\n\t\b\r\f"
 
-hexQuad :: Z.Parser Int
+hexQuad :: Z.ZeptoT IO Int
 hexQuad = do
   s <- Z.take 4
   let hex n | w >= C_0 && w <= C_9 = w - C_0
@@ -321,6 +341,37 @@
 jsonEOF' :: Parser Value
 jsonEOF' = json' <* skipSpace <* endOfInput
 
-toByteString :: Builder -> ByteString
-toByteString = L.toStrict . toLazyByteString
-{-# INLINE toByteString #-}
+word8 :: Word8 -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+word8 w ptr = do
+  liftIO $ poke ptr w
+  return $! ptr `plusPtr` 1
+
+copy :: ByteString -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+copy (PS fp off len) ptr =
+  liftIO . withForeignPtr fp $ \src -> do
+    B.memcpy ptr (src `plusPtr` off) len
+    return $! ptr `plusPtr` len
+
+charUtf8 :: Char -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+charUtf8 ch ptr
+  | ch < '\x80'   = liftIO $ do
+                       poke ptr (fromIntegral (ord ch))
+                       return $! ptr `plusPtr` 1
+  | ch < '\x800'  = liftIO $ do
+                       let (a,b) = ord2 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       return $! ptr `plusPtr` 2
+  | ch < '\xffff' = liftIO $ do
+                       let (a,b,c) = ord3 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       poke (ptr `plusPtr` 2) c
+                       return $! ptr `plusPtr` 3
+  | otherwise     = liftIO $ do
+                       let (a,b,c,d) = ord4 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       poke (ptr `plusPtr` 2) c
+                       poke (ptr `plusPtr` 3) d
+                       return $! ptr `plusPtr` 4
diff --git a/Data/Aeson/Types/Class.hs b/Data/Aeson/Types/Class.hs
--- a/Data/Aeson/Types/Class.hs
+++ b/Data/Aeson/Types/Class.hs
@@ -129,7 +129,8 @@
 --
 -- An example type and instance:
 --
--- @{-\# LANGUAGE OverloadedStrings #-}
+-- @
+-- {-\# LANGUAGE OverloadedStrings #-}
 --
 -- data Coord = Coord { x :: Double, y :: Double }
 --
@@ -165,7 +166,8 @@
 --
 -- For example the previous example can be simplified to just:
 --
--- @{-\# LANGUAGE DeriveGeneric \#-}
+-- @
+-- {-\# LANGUAGE DeriveGeneric \#-}
 --
 -- import GHC.Generics
 --
diff --git a/Data/Aeson/Types/Instances.hs b/Data/Aeson/Types/Instances.hs
--- a/Data/Aeson/Types/Instances.hs
+++ b/Data/Aeson/Types/Instances.hs
@@ -64,10 +64,12 @@
 import qualified Data.Scientific as Scientific (coefficient, base10Exponent, fromFloatDigits, toRealFloat)
 import Data.Attoparsec.Number (Number(..))
 import Data.Fixed
+import Data.Foldable (toList)
+import Data.Functor.Identity (Identity(..))
 import Data.Hashable (Hashable(..))
 import Data.Int (Int8, Int16, Int32, Int64)
 import Data.Maybe (fromMaybe)
-import Data.Monoid (Dual(..), First(..), Last(..), mappend)
+import Data.Monoid (Dual(..), First(..), Last(..))
 import Data.Ratio (Ratio, (%), numerator, denominator)
 import Data.Text (Text, pack, unpack)
 import Data.Time (UTCTime, ZonedTime(..), TimeZone(..))
@@ -87,6 +89,7 @@
 import qualified Data.IntSet as IntSet
 import qualified Data.Map as M
 import qualified Data.Set as Set
+import qualified Data.Sequence as Seq
 import qualified Data.Tree as Tree
 import qualified Data.Text as T
 import qualified Data.Text.Lazy as LT
@@ -97,6 +100,14 @@
 import qualified Data.Vector.Unboxed as VU
 import qualified Data.Vector.Mutable as VM ( unsafeNew, unsafeWrite )
 
+instance (ToJSON a) => ToJSON (Identity a) where
+    toJSON (Identity a) = toJSON a
+    {-# INLINE toJSON #-}
+
+instance (FromJSON a) => FromJSON (Identity a) where
+    parseJSON a      = Identity <$> parseJSON a
+    {-# INLINE parseJSON #-}
+
 instance (ToJSON a) => ToJSON (Maybe a) where
     toJSON (Just a) = toJSON a
     toJSON Nothing  = Null
@@ -339,6 +350,14 @@
     parseJSON = withArray "[a]" $ mapM parseJSON . V.toList
     {-# INLINE parseJSON #-}
 
+instance (ToJSON a) => ToJSON (Seq.Seq a) where
+    toJSON = toJSON . toList
+    {-# INLINE toJSON #-}
+
+instance (FromJSON a) => FromJSON (Seq.Seq a) where
+    parseJSON = withArray "Seq a" $ traverse parseJSON . Seq.fromList . V.toList
+    {-# INLINE parseJSON #-}
+
 instance (ToJSON a) => ToJSON (Vector a) where
     toJSON = Array . V.map toJSON
     {-# INLINE toJSON #-}
@@ -482,8 +501,11 @@
           | otherwise = "%z"
 
 formatMillis :: (FormatTime t) => t -> String
-formatMillis t = take 3 . formatTime defaultTimeLocale "%q" $ t
+formatMillis = take 3 . formatSubseconds
 
+formatSubseconds :: (FormatTime t) => t -> String
+formatSubseconds = formatTime defaultTimeLocale "%q"
+
 instance FromJSON ZonedTime where
     parseJSON (String t) =
       tryFormats alternateFormats
@@ -495,20 +517,32 @@
             Nothing -> empty
         tryFormats = foldr1 (<|>) . map tryFormat
         alternateFormats =
-          dateTimeFmt defaultTimeLocale :
-          distributeList ["%Y", "%Y-%m", "%F"]
-                         ["T%R", "T%T", "T%T%Q", "T%T%QZ", "T%T%Q%z"]
-
-        distributeList xs ys =
-          foldr (\x acc -> acc ++ distribute x ys) [] xs
-        distribute x = map (mappend x)
+            "%FT%T%QZ" :  -- (javascript new Date().toISOString())
+            "%F %T%Q%z" :   -- (postgres)
+            "%F %T%Q %Z" :   -- (time's Show format)
+            "%FT%T%Q%z" :
+            "%Y-%mT%T%Q" :
+            "%Y-%mT%R" :
+            "%Y-%mT%T" :
+            "%Y-%mT%T%QZ" :
+            "%Y-%mT%T%Q%z" :
+            "%YT%T%Q" :
+            "%YT%R" :
+            "%YT%T" :
+            "%YT%T%QZ" :
+            "%YT%T%Q%z" :
+            "%FT%T%Q" :
+            "%FT%R" :
+            "%FT%T" :
+            dateTimeFmt defaultTimeLocale :
+            []
 
     parseJSON v = typeMismatch "ZonedTime" v
 
 instance ToJSON UTCTime where
     toJSON t = String $ pack $ formatTime defaultTimeLocale format t
       where
-        format = "%FT%T." ++ formatMillis t ++ "Z"
+        format = "%FT%T." ++ formatSubseconds t ++ "Z"
     {-# INLINE toJSON #-}
 
 instance FromJSON UTCTime where
diff --git a/Data/Aeson/Types/Internal.hs b/Data/Aeson/Types/Internal.hs
--- a/Data/Aeson/Types/Internal.hs
+++ b/Data/Aeson/Types/Internal.hs
@@ -180,7 +180,7 @@
            | Number !Scientific
            | Bool !Bool
            | Null
-             deriving (Eq, Show, Typeable, Data)
+             deriving (Eq, Read, Show, Typeable, Data)
 
 -- | A newtype wrapper for 'UTCTime' that uses the same non-standard
 -- serialization format as Microsoft .NET, whose @System.DateTime@
diff --git a/aeson.cabal b/aeson.cabal
--- a/aeson.cabal
+++ b/aeson.cabal
@@ -1,9 +1,9 @@
 name:            aeson
-version:         0.8.0.2
+version:         0.8.1.0
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Web, JSON
-copyright:       (c) 2011-2014 Bryan O'Sullivan
+copyright:       (c) 2011-2015 Bryan O'Sullivan
                  (c) 2011 MailRank, Inc.
 author:          Bryan O'Sullivan <bos@serpentine.com>
 maintainer:      Bryan O'Sullivan <bos@serpentine.com>
@@ -99,7 +99,7 @@
     Data.Aeson.Types.Internal
 
   build-depends:
-    attoparsec >= 0.11.3.4,
+    attoparsec >= 0.13.0.0,
     base == 4.*,
     bytestring >= 0.10.4.0,
     containers,
@@ -112,6 +112,7 @@
     syb,
     template-haskell >= 2.4,
     text >= 1.1.1.0,
+    transformers,
     unordered-containers >= 0.2.3.0,
     vector >= 0.7.1
 
@@ -121,7 +122,6 @@
     build-depends: time >= 1.5
 
   if flag(developer)
-    ghc-options: -Werror
     ghc-prof-options: -auto-all
 
   ghc-options: -O2 -Wall
@@ -151,6 +151,7 @@
     bytestring,
     containers,
     ghc-prim >= 0.2,
+    old-locale,
     template-haskell,
     test-framework,
     test-framework-hunit,
diff --git a/benchmarks/aeson-benchmarks.cabal b/benchmarks/aeson-benchmarks.cabal
--- a/benchmarks/aeson-benchmarks.cabal
+++ b/benchmarks/aeson-benchmarks.cabal
@@ -2,17 +2,70 @@
 version:             0
 build-type:          Simple
 
-cabal-version:       >=1.2
+cabal-version:       >=1.8
 
+flag old-locale
+  description: If false then depend on time >= 1.5.
+               .
+               If true then depend on time < 1.5 together with old-locale.
+  default: False
+
+library
+  hs-source-dirs: ..
+
+  exposed-modules:
+    Data.Aeson
+    Data.Aeson.Encode
+    Data.Aeson.Generic
+    Data.Aeson.Parser
+    Data.Aeson.Types
+    Data.Aeson.TH
+
+  other-modules:
+    Data.Aeson.Encode.ByteString
+    Data.Aeson.Functions
+    Data.Aeson.Parser.Internal
+    Data.Aeson.Types.Class
+    Data.Aeson.Types.Generic
+    Data.Aeson.Types.Instances
+    Data.Aeson.Types.Internal
+
+  build-depends:
+    attoparsec >= 0.13.0.0,
+    base == 4.*,
+    bytestring >= 0.10.4.0,
+    containers,
+    deepseq,
+    dlist >= 0.2,
+    ghc-prim >= 0.2,
+    hashable >= 1.1.2.0,
+    mtl,
+    scientific >= 0.3.1 && < 0.4,
+    syb,
+    template-haskell >= 2.4,
+    text >= 1.1.1.0,
+    transformers,
+    unordered-containers >= 0.2.3.0,
+    vector >= 0.7.1
+
+  if flag(old-locale)
+    build-depends: time < 1.5, old-locale
+  else
+    build-depends: time >= 1.5
+
+  ghc-options: -O2 -Wall
+  cpp-options: -DGENERICS
+
 executable aeson-benchmark-compare-with-json
   main-is: CompareWithJSON.hs
   ghc-options: -Wall -O2 -rtsopts
   build-depends:
-    aeson,
+    aeson-benchmarks,
     base,
     blaze-builder,
     bytestring,
     criterion,
+    deepseq,
     json,
     text
 
@@ -20,16 +73,22 @@
   main-is: AesonEncode.hs
   ghc-options: -Wall -O2 -rtsopts
   build-depends:
-    aeson,
-    base
+    aeson-benchmarks,
+    attoparsec,
+    base,
+    bytestring,
+    deepseq,
+    time
 
 executable aeson-benchmark-aeson-parse
   main-is: AesonParse.hs
   ghc-options: -Wall -O2 -rtsopts
   build-depends:
-    aeson,
+    aeson-benchmarks,
     attoparsec,
-    base
+    base,
+    bytestring,
+    time
 
 executable aeson-benchmark-json-parse
   main-is: JsonParse.hs
diff --git a/benchmarks/json-data/sigma.json b/benchmarks/json-data/sigma.json
new file mode 100644
# file too large to diff: benchmarks/json-data/sigma.json
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,29 @@
+0.8.1.0
+
+* Encoding a Scientific value with a huge exponent is now handled
+  efficiently.  (This would previously allocate a huge
+  arbitrary-precision integer, potentially leading to a denial of
+  service.)
+
+* Handling of strings that contain backslash escape sequences is
+  greatly improved.  For a pathological string containing almost a
+  megabyte of consecutive backslashes, the new implementation is 27x
+  faster and uses 42x less memory.
+
+* The ToJSON instance for UTCTime is rendered with higher (picosecond)
+  resolution.
+
+* The value parser now correctly handles leading whitespace.
+
+* New instances of ToJSON and FromJSON for Data.Sequence and
+  Data.Functor.Identity.  The Value type now has a Read instance.
+
+* ZonedTime parser ordering now favours the standard JSON format,
+  increasing efficiency in the common case.
+
+* Encoding to a Text.Builder now escapes '<' and '>' characters, to
+  reduce XSS risk.
+
 0.8.0.2
 
 * Fix ToJSON instance for 15-tuples (see #223).
diff --git a/examples/TemplateHaskell.hs b/examples/TemplateHaskell.hs
--- a/examples/TemplateHaskell.hs
+++ b/examples/TemplateHaskell.hs
@@ -14,10 +14,6 @@
              deriving (Show)
 
 -- This splice will derive instances of ToJSON and FromJSON for us.
---
--- The use of "id" below is a placeholder function to transform the
--- names of the type's fields.  We don't want to transform them, so we
--- use the identity function.
 
 $(deriveJSON defaultOptions ''Coord)
 
diff --git a/tests/Properties.hs b/tests/Properties.hs
--- a/tests/Properties.hs
+++ b/tests/Properties.hs
@@ -18,8 +18,7 @@
 import qualified Data.Text.Lazy.Builder as TLB
 import qualified Data.Text.Lazy.Encoding as TLE
 import qualified Data.HashMap.Strict as H
-import Data.Time.Clock (UTCTime(..))
-import Data.Time (ZonedTime(..))
+import Data.Time
 import Instances ()
 import Types
 import Encoders
@@ -29,9 +28,14 @@
 import qualified Data.Map as Map
 #endif
 
+import System.IO
+import System.IO.Unsafe (unsafePerformIO)
+import System.Locale
 
 roundTripCamel :: String -> Assertion
 roundTripCamel name = assertEqual "" name (camelFrom '_' $ camelTo '_' name)
+
+
   where
     camelFrom c s = let (p:ps) = split c s
                     in concat $ p : map capitalize ps
@@ -67,6 +71,71 @@
                 Error _ -> False
                 Success x' -> x == x'
 
+zonedTimeToJSON :: ZonedTime -> Bool
+zonedTimeToJSON t = and $
+    toFromJSON' "%FT%T%QZ" t (clearTimeZone t) :  -- (javascript new Date().toISOString())
+    toFromJSON' "%F %T%Q%z" t t :   -- (postgres)
+    toFromJSON' "%F %T%Q %Z" t t :   -- (time's Show format)
+    toFromJSON' "%FT%T%Q%z" t t :
+    toFromJSON' "%Y-%mT%T%Q" t (clearTimeZone . clearDay $ t) :
+    toFromJSON' "%Y-%mT%R" t (clearTimeZone . clearDay . clearSeconds $ t) :
+    toFromJSON' "%Y-%mT%T" t (clearTimeZone . clearDay $ t) :
+    toFromJSON' "%Y-%mT%T%QZ" t (clearTimeZone . clearDay $ t) :
+    toFromJSON' "%Y-%mT%T%Q%z" t (clearDay t) :
+    toFromJSON' "%YT%T%Q" t (clearTimeZone . clearMonth . clearDay $ t) :
+    toFromJSON' "%YT%R" t (clearTimeZone . clearMonth . clearDay . clearSeconds $ t) :
+    toFromJSON' "%YT%T" t (clearTimeZone . clearMonth . clearDay $ t) :
+    toFromJSON' "%YT%T%QZ" t (clearTimeZone . clearMonth . clearDay $ t) :
+    toFromJSON' "%YT%T%Q%z" t (clearMonth . clearDay $ t) :
+    toFromJSON' "%FT%T%Q" t (clearTimeZone t) :
+    toFromJSON' "%FT%R" t (clearTimeZone . clearSeconds $ t) :
+    toFromJSON' "%FT%T" t (clearTimeZone t) :
+    toFromJSON' (dateTimeFmt defaultTimeLocale) t t :
+    []
+  where
+    toJSON' :: String -> ZonedTime -> Value
+    toJSON' format = toJSON . formatTime defaultTimeLocale format
+
+    toFromJSON' :: String -> ZonedTime -> ZonedTime -> Bool
+    toFromJSON' format t_ t_' = case fromJSON . toJSON' format $ t_ of
+                                  Error msg -> error' msg
+                                  Success t_'' -> if t_'' == t_' then True else error' (show t_'')
+      where
+        error' :: String -> Bool
+        error' msg = unsafePerformIO (hPutStrLn stderr . ("zonedTimeToJSON: " ++) $ show
+                                       (format, t_, toJSON' format t_, t_', msg))
+            `seq` False
+
+clearTimeZone :: ZonedTime -> ZonedTime
+clearTimeZone t = t { zonedTimeZone = TimeZone 0 False "" }
+
+clearMonth :: ZonedTime -> ZonedTime
+clearMonth = f
+  where
+    f zt = zt { zonedTimeToLocalTime = g $ zonedTimeToLocalTime zt }
+    g lt = lt { localDay = h $ localDay lt }
+
+    h :: Day -> Day
+    h = maybe (error "clearMonth") id . parseTime defaultTimeLocale "%Y-%m-%d" . formatTime defaultTimeLocale "%Y-01-%d"
+
+clearDay :: ZonedTime -> ZonedTime
+clearDay = f
+  where
+    f zt = zt { zonedTimeToLocalTime = g $ zonedTimeToLocalTime zt }
+    g lt = lt { localDay = h $ localDay lt }
+
+    h :: Day -> Day
+    h = maybe (error "clearDay") id . parseTime defaultTimeLocale "%Y-%m-%d" . formatTime defaultTimeLocale "%Y-%m-01"
+
+clearSeconds :: ZonedTime -> ZonedTime
+clearSeconds = f
+  where
+    f zt = zt { zonedTimeToLocalTime = g $ zonedTimeToLocalTime zt }
+    g lt = lt { localTimeOfDay = h $ localTimeOfDay lt }
+
+    h :: TimeOfDay -> TimeOfDay
+    h = maybe (error "clearSeconds") id . parseTime defaultTimeLocale "%H:%M:%S" . formatTime defaultTimeLocale "%H:%M:00"
+
 modifyFailureProp :: String -> String -> Bool
 modifyFailureProp orig added =
     result == Error (added ++ orig)
@@ -126,14 +195,14 @@
   testGroup "roundTrip" [
       testProperty "Bool" $ roundTripEq True
     , testProperty "Double" $ roundTripEq (1 :: Approx Double)
-    , testProperty "Int" $ roundTripEq (1::Int)
-    , testProperty "Integer" $ roundTripEq (1::Integer)
-    , testProperty "String" $ roundTripEq (""::String)
+    , testProperty "Int" $ roundTripEq (1 :: Int)
+    , testProperty "Integer" $ roundTripEq (1 :: Integer)
+    , testProperty "String" $ roundTripEq ("" :: String)
     , testProperty "Text" $ roundTripEq T.empty
-    , testProperty "Foo" $ roundTripEq (undefined::Foo)
+    , testProperty "Foo" $ roundTripEq (undefined :: Foo)
     , testProperty "DotNetTime" $ roundTripEq (undefined :: DotNetTime)
     , testProperty "UTCTime" $ roundTripEq (undefined :: UTCTime)
-    , testProperty "ZonedTime" $ roundTripEq (undefined::ZonedTime)
+    , testProperty "ZonedTime" $ roundTripEq (undefined :: ZonedTime)
 #ifdef GHC_GENERICS
     , testGroup "ghcGenerics" [
         testProperty "OneConstructor" $ roundTripEq OneConstructor
@@ -149,6 +218,7 @@
     , testProperty "Maybe Integer" (toFromJSON :: Maybe Integer -> Bool)
     , testProperty "Either Integer Double" (toFromJSON :: Either Integer Double -> Bool)
     , testProperty "Either Integer Integer" (toFromJSON :: Either Integer Integer -> Bool)
+    , testProperty "ZonedTime" $ zonedTimeToJSON
     ],
   testGroup "deprecated" deprecatedTests,
   testGroup "failure messages" [
