diff --git a/Data/Attoparsec/Time.hs b/Data/Attoparsec/Time.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attoparsec/Time.hs
@@ -0,0 +1,149 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+-- |
+-- Module:      Data.Aeson.Parser.Time
+-- Copyright:   (c) 2015-2016 Bryan O'Sullivan
+-- License:     BSD3
+-- Maintainer:  Bryan O'Sullivan <bos@serpentine.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Parsers for parsing dates and times.
+
+module Data.Attoparsec.Time
+    (
+      day
+    , localTime
+    , timeOfDay
+    , timeZone
+    , utcTime
+    , zonedTime
+    ) where
+
+import Prelude ()
+import Prelude.Compat
+
+import Control.Applicative ((<|>))
+import Control.Monad (void, when)
+import Data.Attoparsec.Text as A
+import Data.Attoparsec.Time.Internal (toPico)
+import Data.Bits ((.&.))
+import Data.Char (isDigit, ord)
+import Data.Fixed (Pico)
+import Data.Int (Int64)
+import Data.Maybe (fromMaybe)
+import Data.Time.Calendar (Day, fromGregorianValid)
+import Data.Time.Clock (UTCTime(..))
+import qualified Data.Text as T
+import qualified Data.Time.LocalTime as Local
+
+-- | Parse a date of the form @[+,-]YYYY-MM-DD@.
+day :: Parser Day
+day = do
+  absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
+  y <- decimal <* char '-'
+  m <- twoDigits <* char '-'
+  d <- twoDigits
+  maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
+
+-- | Parse a two-digit integer (e.g. day of month, hour).
+twoDigits :: Parser Int
+twoDigits = do
+  a <- digit
+  b <- digit
+  let c2d c = ord c .&. 15
+  return $! c2d a * 10 + c2d b
+
+-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
+timeOfDay :: Parser Local.TimeOfDay
+timeOfDay = do
+  h <- twoDigits
+  m <- char ':' *> twoDigits
+  s <- option 0 (char ':' *> seconds)
+  if h < 24 && m < 60 && s < 61
+    then return (Local.TimeOfDay h m s)
+    else fail "invalid time"
+
+data T = T {-# UNPACK #-} !Int {-# UNPACK #-} !Int64
+
+-- | Parse a count of seconds, with the integer part being two digits
+-- long.
+seconds :: Parser Pico
+seconds = do
+  real <- twoDigits
+  mc <- peekChar
+  case mc of
+    Just '.' -> do
+      t <- anyChar *> takeWhile1 isDigit
+      return $! parsePicos real t
+    _ -> return $! fromIntegral real
+ where
+  parsePicos a0 t = toPico (fromIntegral (t' * 10^n))
+    where T n t'  = T.foldl' step (T 12 (fromIntegral a0)) t
+          step ma@(T m a) c
+              | m <= 0    = ma
+              | otherwise = T (m-1) (10 * a + fromIntegral (ord c) .&. 15)
+
+-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
+-- zero. (This makes some speedups possible.)
+timeZone :: Parser (Maybe Local.TimeZone)
+timeZone = do
+  let maybeSkip c = do ch <- peekChar'; when (ch == c) (void anyChar)
+  maybeSkip ' '
+  ch <- satisfy $ \c -> c == 'Z' || c == '+' || c == '-'
+  if ch == 'Z'
+    then return Nothing
+    else do
+      h <- twoDigits
+      mm <- peekChar
+      m <- case mm of
+             Just ':'           -> anyChar *> twoDigits
+             Just d | isDigit d -> twoDigits
+             _                  -> return 0
+      let off | ch == '-' = negate off0
+              | otherwise = off0
+          off0 = h * 60 + m
+      case undefined of
+        _   | off == 0 ->
+              return Nothing
+            | off < -720 || off > 840 || m > 59 ->
+              fail "invalid time zone offset"
+            | otherwise ->
+              let !tz = Local.minutesToTimeZone off
+              in return (Just tz)
+
+-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM[:SS[.SSS]]@.
+-- The space may be replaced with a @T@.  The number of seconds is optional
+-- and may be followed by a fractional component.
+localTime :: Parser Local.LocalTime
+localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay
+  where daySep = satisfy (\c -> c == 'T' || c == ' ')
+
+-- | Behaves as 'zonedTime', but converts any time zone offset into a
+-- UTC time.
+utcTime :: Parser UTCTime
+utcTime = do
+  lt@(Local.LocalTime d t) <- localTime
+  mtz <- timeZone
+  case mtz of
+    Nothing -> let !tt = Local.timeOfDayToTime t
+               in return (UTCTime d tt)
+    Just tz -> return $! Local.localTimeToUTC tz lt
+
+-- | Parse a date with time zone info. Acceptable formats:
+--
+-- @YYYY-MM-DD HH:MM Z@
+-- @YYYY-MM-DD HH:MM:SS Z@
+-- @YYYY-MM-DD HH:MM:SS.SSS Z@
+--
+-- The first space may instead be a @T@, and the second space is
+-- optional.  The @Z@ represents UTC.  The @Z@ may be replaced with a
+-- time zone offset of the form @+0000@ or @-08:00@, where the first
+-- two digits are hours, the @:@ is optional and the second two digits
+-- (also optional) are minutes.
+zonedTime :: Parser Local.ZonedTime
+zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)
+
+utc :: Local.TimeZone
+utc = Local.TimeZone 0 False ""
diff --git a/Data/Attoparsec/Time/Internal.hs b/Data/Attoparsec/Time/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attoparsec/Time/Internal.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE CPP #-}
+
+-- |
+-- Module:      Data.Aeson.Internal.Time
+-- Copyright:   (c) 2015-2016 Bryan O'Sullivan
+-- License:     BSD3
+-- Maintainer:  Bryan O'Sullivan <bos@serpentine.com>
+-- Stability:   experimental
+-- Portability: portable
+
+module Data.Attoparsec.Time.Internal
+    (
+      TimeOfDay64(..)
+    , fromPico
+    , toPico
+    , diffTimeOfDay64
+    , toTimeOfDay64
+    ) where
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Int (Int64)
+import Data.Time
+import Unsafe.Coerce (unsafeCoerce)
+
+#if MIN_VERSION_base(4,7,0)
+
+import Data.Fixed (Pico, Fixed(MkFixed))
+
+toPico :: Integer -> Pico
+toPico = MkFixed
+
+fromPico :: Pico -> Integer
+fromPico (MkFixed i) = i
+
+#else
+
+import Data.Fixed (Pico)
+
+toPico :: Integer -> Pico
+toPico = unsafeCoerce
+
+fromPico :: Pico -> Integer
+fromPico = unsafeCoerce
+
+#endif
+
+-- | Like TimeOfDay, but using a fixed-width integer for seconds.
+data TimeOfDay64 = TOD {-# UNPACK #-} !Int
+                       {-# UNPACK #-} !Int
+                       {-# UNPACK #-} !Int64
+
+diffTimeOfDay64 :: DiffTime -> TimeOfDay64
+diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
+  where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
+        (m,s)  = mp `quotRem` 60000000000000
+        pico   = unsafeCoerce t :: Integer
+
+toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
+toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011, MailRank, Inc.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+Parsing of ISO 8601 dates.
+
+This package is used to parse dates in aeson. It is split into a
+separate package to be shared by other projects that want to parse
+dates like aeson does.
+
+For now, this project is located in the aeson repository and aeson
+itself uses the source of this package without pulling in the package
+as a dependency.
+
+## Stability
+
+Since aeson depends on this package we want to be very careful about
+changing the format.
+
+There may be breaking changes if we find that the format is
+incorrectly too lenient. We consider widening of the allowed input a
+non-breaking addition since all previously valid input will still
+parse correctly.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/attoparsec-iso8601.cabal b/attoparsec-iso8601.cabal
new file mode 100644
--- /dev/null
+++ b/attoparsec-iso8601.cabal
@@ -0,0 +1,55 @@
+name:            attoparsec-iso8601
+version:         1.0.0.0
+synopsis:        Parsing of ISO 8601 dates, originally from aeson.
+description:     Parsing of ISO 8601 dates, originally from aeson.
+license:         BSD3
+license-file:    LICENSE
+category:        Parsing
+copyright:       (c) 2011-2016 Bryan O'Sullivan
+                 (c) 2011 MailRank, Inc.
+author:          Bryan O'Sullivan <bos@serpentine.com>
+maintainer:      Adam Bergmark <adam@bergmark.nl>
+stability:       experimental
+cabal-version:   >=1.10
+homepage:        https://github.com/bos/aeson
+bug-reports:     https://github.com/bos/aeson/issues
+build-type:      Simple
+extra-source-files:
+  README.md
+
+flag developer
+  description: operate in developer mode
+  default: False
+  manual: True
+
+flag fast
+  description: compile without optimizations
+  default: False
+  manual: True
+
+library
+  hs-source-dirs:      .
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  exposed-modules:
+    Data.Attoparsec.Time.Internal
+    Data.Attoparsec.Time
+  build-depends:
+    attoparsec >= 0.13.0.1,
+    base >= 4.5 && < 5,
+    base-compat >= 0.9.1 && < 0.10,
+    text >= 1.1.1.0,
+    time >= 1.1.1.4
+
+  if flag(fast)
+    ghc-options: -O0
+  else
+    ghc-options: -O2
+
+  if flag(developer)
+    ghc-options: -Werror
+    ghc-prof-options: -auto-all
+
+source-repository head
+  type:     git
+  location: git://github.com/bos/aeson.git
