@@ -1,6 +1,8 @@
11{-# LANGUAGE CPP, DeriveDataTypeable, FlexibleContexts, FlexibleInstances,
22 GeneralizedNewtypeDeriving, IncoherentInstances, OverlappingInstances,
3− OverloadedStrings, UndecidableInstances, ViewPatterns #-}
3+ OverloadedStrings, UndecidableInstances, ViewPatterns,
4+ StandaloneDeriving #-}
5+{-# OPTIONS_GHC -fno-warn-orphans #-}
46
57#ifdef GENERICS
68{-# LANGUAGE DefaultSignatures #-}
@@ -39,7 +41,7 @@ module Data.Aeson.Types.Class
3941 , typeMismatch
4042 ) where
4143
42−import Control.Applicative ((<$>), (<*>), pure)
44+import Control.Applicative ((<$>), (<*>), (<|>), pure, empty)
4345import Data.Aeson.Functions
4446import Data.Aeson.Types.Internal
4547import Data.Attoparsec.Char8 (Number(..))
@@ -47,11 +49,11 @@ import Data.Fixed
4749import Data.Hashable (Hashable(..))
4850import Data.Int (Int8, Int16, Int32, Int64)
4951import Data.Maybe (fromMaybe)
50−import Data.Monoid (Dual(..), First(..), Last(..))
52+import Data.Monoid (Dual(..), First(..), Last(..), mappend)
5153import Data.Ratio (Ratio)
5254import Data.Text (Text, pack, unpack)
5355import Data.Text.Encoding (encodeUtf8)
54−import Data.Time.Clock (UTCTime)
56+import Data.Time (UTCTime, ZonedTime(..), TimeZone(..))
5557import Data.Time.Format (FormatTime, formatTime, parseTime)
5658import Data.Traversable (traverse)
5759import Data.Typeable (Typeable)
@@ -623,6 +625,37 @@ instance FromJSON DotNetTime where
623625 parseJSON v = typeMismatch "DotNetTime" v
624626 {-# INLINE parseJSON #-}
625627
628+deriving instance Eq ZonedTime
629+
630+instance ToJSON ZonedTime where
631+ toJSON t = String $ pack $ formatTime defaultTimeLocale format t
632+ where
633+ format = "%FT%T" ++ milliseconds ++ tzFormat
634+ milliseconds = take 4 $ formatTime defaultTimeLocale "%Q" t
635+ tzFormat
636+ | 0 == timeZoneMinutes (zonedTimeZone t) = "Z"
637+ | otherwise = "%z"
638+
639+instance FromJSON ZonedTime where
640+ parseJSON (String t) =
641+ tryFormats alternateFormats
642+ <|> fail "could not parse ECMA-262 ISO-8601 date"
643+ where
644+ tryFormat f =
645+ case parseTime defaultTimeLocale f (unpack t) of
646+ Just d -> pure d
647+ Nothing -> empty
648+ tryFormats = foldr1 (<|>) . map tryFormat
649+ alternateFormats =
650+ distributeList ["%Y", "%Y-%m", "%F"]
651+ ["T%R", "T%T", "T%T%Q", "T%T%QZ", "T%T%Q%z"]
652+
653+ distributeList xs ys =
654+ foldr (\x acc -> acc ++ distribute x ys) [] xs
655+ distribute x = map (mappend x)
656+
657+ parseJSON v = typeMismatch "ZonedTime" v
658+
626659instance ToJSON UTCTime where
627660 toJSON t = String (pack (take 23 str ++ "Z"))
628661 where str = formatTime defaultTimeLocale "%FT%T%Q" t