diff --git a/Data/UTC.hs b/Data/UTC.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC.hs
@@ -0,0 +1,121 @@
+module Data.UTC
+  ( 
+  -- * Introduction
+  -- ** Quick Start
+  -- $quickstart
+
+  -- ** General Concepts
+
+  -- *** Handling Failure
+  -- $failure
+
+  -- *** Integer vs. Int
+  -- $integerint
+
+  -- *** Leap Seconds
+  -- $leapseconds
+
+  -- * Interfaces
+  -- ** Date
+    IsDate(..)
+  -- ** Time
+  , IsTime (..)
+  -- ** Unix Time
+  , IsUnixTime(..)
+  -- ** Epoch / Midnight
+  , Epoch(..)
+  , Midnight(..)
+    -- ** Getting (Current) Timestamps
+  , HasUnixTime (..)
+  -- * Generic Date/Time Types
+  -- ** Date
+  , Date
+  -- ** Time
+  , Time
+  -- ** DateTime
+  , DateTime (..)
+  -- ** Local Time
+  , Local (..)
+
+  -- * Formatting
+  -- ** RFC 3339
+  -- *** Parsing
+  , Rfc3339Parser(..)
+  -- *** Rendering
+  , Rfc3339Renderer(..)
+  ) where
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.Midnight
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+import Data.UTC.Class.IsUnixTime
+import Data.UTC.Class.HasUnixTime
+import Data.UTC.Type.Date
+import Data.UTC.Type.Time
+import Data.UTC.Type.DateTime
+import Data.UTC.Type.Local
+import Data.UTC.Format.Rfc3339
+
+-- $quickstart
+--
+-- Just import the main module and use the 'DateTime' type! 
+-- It supports all functions you'll find below.
+-- Use 'Maybe' for all occurences of 'm'.
+--
+-- > Prelude> :m +Data.UTC
+-- > Prelude Data.UTC> type MT = Maybe (Local DateTime)
+-- > Prelude Data.UTC> type MS = Maybe String
+-- > Prelude Data.UTC> (parseRfc3339 "2014-12-24T13:37:00Z" :: MT) >>= addHours 25 >>= setMonth 1 >>= renderRfc3339 :: MS
+-- > Just "2014-01-25T14:37:00Z"
+
+-- $failure
+--
+-- The library's main idea is to make it hard to use it wrong. It should
+-- be impossible by the API's design to construct invalid date or time values.
+--
+-- Furthermore, the library is safe in the sense that its functions don't do
+-- anything that is not visible in the functions signature. Escpecially, none of the
+-- functions throw exceptions via 'Prelude.error' or 'Prelude.undefined'.
+--
+-- Whenever a function cannot be total, its result is wrapped in a type variable with
+-- a 'Prelude.Monad' restriction on it. You can always just use 'Prelude.Maybe' and
+-- 'Data.Maybe.fromMaybe' to obtain a plain value:
+--
+-- >  fromMaybe epoch (addDays 24 epoch) :: Date
+-- > > 1970-01-25
+--
+-- Using another 'Prelude.Monad' instance might give you additional information in case of failure:
+--
+-- > setHour 10 midnight >>= setMinute 61 :: IO Time
+-- > > *** Exception: user error (Time.setMinute 61)
+
+-- $integerint
+--
+-- This library uses 'Prelude.Integer' instead of 'Prelude.Int'. This bears the advantage
+-- of easier reasoning about the code's correctness and the ability
+-- to work on date and time with arbitrary range and precision.
+--
+-- One might think that 'Prelude.Integer' is slower, but indeed it uses 'Prelude.Int'
+-- internally unless its range is exceeded. The dispatching should not take more than a
+-- few cycles. Using 'Prelude.Int' in the first place can rightly be considered
+-- /premature optimisation/. If this really is your application's bottle neck
+-- you should first consider creating your own time type (i.e. a newtyped 'Prelude.Int'
+-- representing Unixtime) and making it an instance of the relevant classes.
+-- Do the critical operations on the bare type. If that is not an option consider
+-- using a more specialised library.
+
+-- $leapseconds
+--
+-- As most other date and time libraries this library does __not support__ handling of leap seconds.
+--
+-- The problem is not so much that this task would be tedious and difficult, but
+-- rather that future leap seconds are not known in advance and are announced
+-- just a few weeks before they occur.
+-- How should a library deal with this? Changing the function's semantic from version to version whenever
+-- a leap second occured? Probably not desireable. The only sane answer seems to be: /Not at all!/
+--
+-- In reality the problem is less severe than it seems: Your system clock is most probably
+-- counting unix seconds and does not know about leap seconds either. So chances are that
+-- when dealing with computer generated timestamps you'll never encounter problems with
+-- leap seconds.
diff --git a/Data/UTC/Class.hs b/Data/UTC/Class.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class.hs
@@ -0,0 +1,15 @@
+module Data.UTC.Class
+  ( module Data.UTC.Class.IsDate
+  , module Data.UTC.Class.IsTime
+  , module Data.UTC.Class.IsUnixTime
+  , module Data.UTC.Class.HasUnixTime
+  , module Data.UTC.Class.Epoch
+  , module Data.UTC.Class.Midnight
+  ) where
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.Midnight
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+import Data.UTC.Class.IsUnixTime
+import Data.UTC.Class.HasUnixTime
diff --git a/Data/UTC/Class/Epoch.hs b/Data/UTC/Class/Epoch.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/Epoch.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.Epoch
+  ( Epoch(..)
+  ) where
+
+-- | The instant in time also known as __the epoch__: 1970-01-01T00:00:00Z
+class Epoch t where
+  epoch :: t
+
diff --git a/Data/UTC/Class/HasUnixTime.hs b/Data/UTC/Class/HasUnixTime.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/HasUnixTime.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.HasUnixTime where
+
+import Data.Ratio
+import System.Clock as C
+
+import Data.UTC.Class.IsUnixTime
+
+-- | This class defines an interface for contexts that can be asked for a timestamp.
+--
+-- Most users are likely to just need the 'Prelude.IO' instance, but you might
+-- think of other instances:
+--
+--    - A wrapper around the system clock with internal state that ensures
+--      strict monotonically increasing values.
+--    - A custom monadic computation that needs time, but should not be given
+--     'Prelude.IO' access.
+--    - Testing contexts where you might want to inject and test specific timestamps.
+class HasUnixTime m where
+  -- | Get a timestamp from the surrounding context. The 'Prelude.IO' instance gives
+  --  access to the __system clock__ and is what most users are probably looking for.
+  --
+  -- /Beware:/ The 'Prelude.IO' instance does __not__ guarantee that
+  -- subsequent calls are monotonically increasing. The system's clock might
+  -- stop or even go backwards when synchronised manually or via NTP or when
+  -- adapting to a leap second.
+  --
+  -- /Example:/
+  --
+  -- > import Data.UTC
+  -- > 
+  -- > printCurrentYear :: IO ()
+  -- > printCurrentYear
+  -- >   = do now <- getUnixTime :: IO DateTime
+  -- >        print (year now)
+  getUnixTime :: (Monad m, IsUnixTime a) => m a
+
+instance HasUnixTime IO where
+  getUnixTime
+    = do TimeSpec s ns <- C.getTime Realtime
+         case fromUnixSeconds ((fromIntegral s) % 1 + (fromIntegral ns) % 1000000000) of
+           Just t  -> return t
+           Nothing -> fail "Data.UTC.Class.HasUnixTime.getUnixTime: failed"
+
diff --git a/Data/UTC/Class/IsDate.hs b/Data/UTC/Class/IsDate.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/IsDate.hs
@@ -0,0 +1,106 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.IsDate
+  ( IsDate (..)
+  ) where
+
+import Data.UTC.Internal
+import Data.UTC.Class.Epoch
+
+-- | This class captures the behaviour of the __Proleptic Gregorian Calendar__.
+--
+-- Without any exception the following holds:
+--
+--   * A regular year has 365 days and the corresponding February has 28 days.
+--   * A leap year has 366 days and the corresponding February has 29 days.
+--   * A year that is a multiple of 400 is a leap year.
+--   * A year that is a multiple of 100 but not of 400 is not a leap year.
+--   * A year that is a multiple of 4 but not of 100 is a leap year.
+class Epoch t => IsDate t where
+  -- | > year  "2014-⁠12-⁠24" == 2014
+  -- For negative years the function assumes astronomical year
+  -- numbering (year 1 ~ 1 AD, year 0 ~ 1 BC, year -1 ~ 2 BC etc).
+  -- Note that 1 BC and 5 BC are therefore leap years.
+  year                  :: t -> Integer
+  -- | > month "2014-⁠12-⁠24" == 12
+  -- The function only returns values ranging from 1 to 12.
+  month                 :: t -> Integer
+  -- | > day   "2014-⁠12-⁠24" == 24
+  -- The function only returns values ranging from 1 to 31.
+  day                   :: t -> Integer
+
+  -- | Sets the year and fails if the result would be invalid.
+  --
+  -- > setYear 2005 "2004-02-28" :: Maybe Date
+  -- > > Just 2005-02-28
+  -- > setYear 2005 "2004-02-29" :: Maybe Date
+  -- > > Nothing
+  setYear               :: (Monad m) => Integer  -> t -> m t
+  -- | Sets the month of year and fails if the result would be invalid.
+  --
+  -- The function only accepts input ranging from 1 to 12.
+  setMonth              :: (Monad m) => Integer  -> t -> m t
+  -- | Sets the day of month and fails if the result would be invalid.
+  --
+  -- The function only accepts input ranging from 1 to 31 (or less depending on month and year).
+  setDay                :: (Monad m) => Integer  -> t -> m t
+
+  -- | A /year/ is a relative amount of time.
+  -- The function's semantic is a follows:
+  --
+  --   * The years (positive or negative) are added.
+  --   * If the target date is invalid then days are subtracted until the date gets valid.
+  --   * If the resulting date is out of the instance type's range, the function fails
+  --     (cannot happen for 'Data.UTC.Date' and 'Data.UTC.DateTime' as they use 
+  --     multiprecision integers).
+  --
+  -- > addYears 4 "2000-02-29" :: Maybe Date
+  -- > > Just 2004-02-29
+  -- > addYears 1 "2000-02-29" :: Maybe Date
+  -- > > Just 2001-02-28
+  addYears              :: (Monad m) => Integer  -> t -> m t
+  addYears ys t
+    = if isValidDate (year t + ys, month t, day t)
+        then setYear (year t + ys) t
+        else setYear (year t + ys) =<< setDay (day t - 1) t
+
+  -- | A /month/ is a relative amount of time.
+  -- The function's semantic is equivalent to that of 'addYears'.
+  --
+  -- The function fails if the resulting date is out of the instance type's range
+  -- (cannot happen for 'Data.UTC.Date' and 'Data.UTC.DateTime' as they use 
+  --  multiprecision integers).
+  --
+  -- > addMonths (-13) "1970-01-01" :: Maybe Date
+  -- > > Just 1968-12-01
+  addMonths             :: (Monad m) => Integer  -> t -> m t
+  addMonths ms t
+    = setDay 1 t >>= setYear y >>= setMonth m >>= setDay d
+    where
+      ym = (year t * monthsPerYear)
+         + (month t - 1)
+         + ms
+      y  = ym `div` monthsPerYear
+      m  = (ym `mod` monthsPerYear) + 1
+      d' = day t
+      d  | isValidDate (y, m, d')     = d'
+         | isValidDate (y, m, d' - 1) = d' - 1
+         | isValidDate (y, m, d' - 2) = d' - 2
+         | otherwise                  = d' - 3 -- was 31, now 28
+
+  -- | A /day/ is an absolute amount of time. There is no surprise to expect.
+  --
+  -- The function fails if the resulting date is out of the instance type's range
+  -- (cannot happen for 'Data.UTC.Date' and 'Data.UTC.DateTime' as they use 
+  --  multiprecision integers).
+  --
+  -- > addDays 365 "1970-01-01" :: Maybe Date
+  -- > > Just 1971-01-01
+  -- > addDays 365 "2000-01-01" :: Maybe Date
+  -- > > Just 2000-12-31
+  addDays               :: (Monad m) => Integer  -> t -> m t
+  addDays ds t
+    -- setDay 1 to avoid intermediate generation of invalid dates!
+    = setDay 1 t >>= setYear y >>= setMonth m >>= setDay d
+    where
+      ds'       = yearMonthDayToDays (year t, month t, day t)
+      (y, m, d) = daysToYearMonthDay (ds' + ds)
diff --git a/Data/UTC/Class/IsDate/Test.hs b/Data/UTC/Class/IsDate/Test.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/IsDate/Test.hs
@@ -0,0 +1,89 @@
+module Data.UTC.Class.IsDate.Test where
+
+import Test.QuickCheck
+import Test.Framework (testGroup)
+import Test.Framework (Test)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Data.UTC
+
+test :: Test
+test
+  = testGroup "Data.UTC.Class.IsDate"
+  $ [ testGroup "addYears"
+      [ testProperty "addYears 1 '2000-02-28'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 2 >>= setDay 28 >>= addYears 1
+        in  conjoin
+             [ (year  `fmap` d) === Just 2001
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 28
+             ]
+      , testProperty "addYears 1 '2000-02-29'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 2 >>= setDay 29 >>= addYears 1
+        in  conjoin
+             [ (year  `fmap` d) === Just 2001
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 28
+             ]
+      , testProperty "addYears 4 '2000-02-29'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 2 >>= setDay 29 >>= addYears 4
+        in  conjoin
+             [ (year  `fmap` d) === Just 2004
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 29
+             ]
+      , testProperty "addYears (-4) '2000-02-29'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 2 >>= setDay 29 >>= addYears (-4)
+        in  conjoin
+             [ (year  `fmap` d) === Just 1996
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 29
+             ]
+      ]
+    , testGroup "addMonths"
+      [ testProperty "addMonths 1 '2000-01-31'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 1 >>= setDay 31 >>= addMonths 1
+        in  conjoin
+             [ (year  `fmap` d) === Just 2000
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 29
+             ]
+      , testProperty "addMonths 2 '2000-01-31'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 1 >>= setDay 31 >>= addMonths 2
+        in  conjoin
+             [ (year  `fmap` d) === Just 2000
+             , (month `fmap` d) === Just 3
+             , (day   `fmap` d) === Just 31
+             ]
+      , testProperty "addMonths 3 '2000-01-31'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 1 >>= setDay 31 >>= addMonths 3
+        in  conjoin
+             [ (year  `fmap` d) === Just 2000
+             , (month `fmap` d) === Just 4
+             , (day   `fmap` d) === Just 30
+             ]
+      , testProperty "addMonths 1 '2001-01-31'"
+      $ let d = setYear 2001 (epoch :: Date) >>= setMonth 1 >>= setDay 31 >>= addMonths 1
+        in  conjoin
+             [ (year  `fmap` d) === Just 2001
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 28
+             ]
+      , testProperty "addMonths (-47) '2000-01-31'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 1 >>= setDay 31 >>= addMonths (-47)
+        in  conjoin
+             [ (year  `fmap` d) === Just 1996
+             , (month `fmap` d) === Just 2
+             , (day   `fmap` d) === Just 29
+             ]
+      ] 
+    , testGroup "addDays"
+      [ testProperty "addDays 366 '2000-01-01'"
+      $ let d = setYear 2000 (epoch :: Date) >>= setMonth 1 >>= setDay 1 >>= addDays 366
+        in  conjoin
+             [ (year  `fmap` d) === Just 2001
+             , (month `fmap` d) === Just 1
+             , (day   `fmap` d) === Just 1
+             ]
+      ]
+    ]
diff --git a/Data/UTC/Class/IsTime.hs b/Data/UTC/Class/IsTime.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/IsTime.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.IsTime
+  ( IsTime (..)
+  ) where
+
+import Data.Ratio
+
+import Data.UTC.Internal
+
+-- | This class captures the concept of a 24-hour clock time
+-- during a day.
+class IsTime t where
+  -- | Returns values in the range 0 to 23.
+  hour                  :: t -> Integer
+  -- | Returns values in the range 0 to 59.
+  minute                :: t -> Integer
+  -- | Returns values in the range 0 to 59.
+  second                :: t -> Integer
+  -- | Returns values in the range 0.0 <= x < 1.0.
+  secondFraction        :: t -> Rational
+  -- | Accepts values in the range 0 to 23.
+  --
+  -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  setHour               :: (Monad m) => Integer  -> t -> m t
+  -- | Accepts values in the range 0 to 59.
+  --
+  -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  setMinute             :: (Monad m) => Integer  -> t -> m t
+  -- | Accepts values in the range 0 to 59.
+  --
+  -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  setSecond             :: (Monad m) => Integer  -> t -> m t
+  -- | Accepts values in the range 0.0 <= x < 1.0.
+  --
+  -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  setSecondFraction     :: (Monad m) => Rational -> t -> m t
+
+  -- | Adds an arbitrary count of hours (positive or negative).
+  --
+  --   * Full days flow over to 'Data.UTC.addDays' if the type is also an instance of 'Data.UTC.Class.IsDate' (this is the case for 'Data.UTC.DateTime').
+  --   * Types not implementing the 'Data.UTC.Class.IsDate' class should just ignore the days part on overflow (like 'Data.UTC.Time' does).
+  --   * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  addHours              :: (Monad m) => Integer  -> t -> m t
+  addHours h t
+    = setHour hors t
+    where
+      h'   = h + (hour t)
+      hors = h' `mod` hoursPerDay
+
+  -- | Adds an arbitrary count of minutes (positive or negative).
+  --
+  --   * Full hours flow over to 'addHours'.
+  --   * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  addMinutes            :: (Monad m) => Integer  -> t -> m t
+  addMinutes m t
+    = setMinute mins t >>= addHours hors
+    where
+      m'   = m + (minute t)
+      mins = m' `mod` minsPerHour
+      hors = m' `div` minsPerHour
+
+  -- | Adds an arbitrary count of seconds (positive or negative).
+  --
+  --   * Full minutes flow over to 'addMinutes'.
+  --   * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  addSeconds            :: (Monad m) => Integer  -> t -> m t
+  addSeconds s t
+    = setSecond secs t >>= addMinutes mins
+    where
+      s'   = s + (second t)
+      secs = s' `mod` secsPerMinute
+      mins = s' `div` secsPerMinute
+
+  -- | Adds an arbitrary second fraction (positive or negative).
+  --
+  --   * Full seconds flow over to 'addSeconds'.
+  --   * Instances of this class are not required to preserve full precision (although 'Data.UTC.Time' and 'Data.UTC.DateTime' do so).
+  --   * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime').
+  addSecondFractions    :: (Monad m) => Rational -> t -> m t
+  addSecondFractions f t
+    | f == 0    = return t
+    | f >= 0    = setSecondFraction frcs t         >>= addSeconds secs
+    | otherwise = setSecondFraction (frcs + 1.0) t >>= addSeconds (secs - 1)
+    where
+      f'   = f + (secondFraction t)
+      frcs = f' - (truncate f' % 1)
+      secs = truncate f'
diff --git a/Data/UTC/Class/IsUnixTime.hs b/Data/UTC/Class/IsUnixTime.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/IsUnixTime.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.IsUnixTime
+  ( IsUnixTime(..)
+  ) where
+
+-- | This class is for types that have a well-defined
+-- mapping to and from the <https://en.wikipedia.org/wiki/Unix_time Unix Time> system 
+-- (based on <https://en.wikipedia.org/wiki/Coordinated_Universal_Time UTC>).
+--
+-- /Beware/: It is a common misconception that the Unix time in general counts
+-- <https://en.wikipedia.org/wiki/International_System_of_Units SI> seconds since 1970-01-01T00:00:00Z.
+-- There is a common definition that may be called /Unix time based on UTC/: In general, the second
+-- boundaries match with UTC, but in the event of a positive (or negative) leap second
+-- the Unix second has a duration of 2 (or 0) SI seconds. This library is in accordance with
+-- this definition. This definition can also be understood as "ignoring leap seconds"
+-- (a Unix day therefore always has 86400 Unix seconds).
+--
+-- The concrete behaviour of your system clock is implementation dependant.
+class IsUnixTime t where
+  unixSeconds      :: t -> Rational
+  fromUnixSeconds  :: Monad m => Rational -> m t
diff --git a/Data/UTC/Class/Midnight.hs b/Data/UTC/Class/Midnight.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Class/Midnight.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Class.Midnight
+  ( Midnight(..)
+  ) where
+
+-- | The beginning of a day: 00:00:00
+class Midnight t where
+  midnight :: t
+
diff --git a/Data/UTC/Format/Rfc3339.hs b/Data/UTC/Format/Rfc3339.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Format/Rfc3339.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE FlexibleInstances #-}
+module Data.UTC.Format.Rfc3339
+  ( -- * Parsing
+    Rfc3339Parser(..)
+    -- * Rendering
+  , Rfc3339Renderer(..)
+    -- * Low-Level
+  , rfc3339Parser, rfc3339Builder
+  ) where
+
+import qualified Data.Attoparsec.ByteString as Atto
+import qualified Data.ByteString            as BS
+import qualified Data.ByteString.Lazy       as BSL
+import qualified Data.ByteString.Builder    as B
+import qualified Data.Text                  as T
+import qualified Data.Text.Encoding         as T
+import qualified Data.Text.Lazy             as TL
+import qualified Data.Text.Lazy.Encoding    as TL
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+import Data.UTC.Type.Local
+import Data.UTC.Format.Rfc3339.Parser
+import Data.UTC.Format.Rfc3339.Builder
+
+class Rfc3339Parser a where
+  parseRfc3339 :: (Monad m, IsDate t, IsTime t, Epoch t) => a -> m (Local t)
+
+instance Rfc3339Parser BS.ByteString where
+  parseRfc3339 s
+    = case Atto.parseOnly rfc3339Parser s of
+        Right t -> return t
+        Left  _ -> fail ""
+
+instance Rfc3339Parser BSL.ByteString where
+  parseRfc3339 s
+    = parseRfc3339 (BSL.toStrict s)
+
+instance Rfc3339Parser T.Text where
+  parseRfc3339 s
+    = parseRfc3339 (T.encodeUtf8 s)
+
+instance Rfc3339Parser TL.Text where
+  parseRfc3339 s
+    = parseRfc3339 (TL.encodeUtf8 s)
+
+instance Rfc3339Parser [Char] where
+  parseRfc3339 s
+    = parseRfc3339 (T.pack s)
+
+
+class Rfc3339Renderer a where
+  renderRfc3339 :: (Monad m, IsDate t, IsTime t, Epoch t) => Local t -> m a
+
+instance Rfc3339Renderer BS.ByteString where
+  renderRfc3339 t
+    = renderRfc3339 t     >>= return . BSL.toStrict
+
+instance Rfc3339Renderer BSL.ByteString where
+  renderRfc3339 t
+    = rfc3339Builder t    >>= return . B.toLazyByteString
+
+instance Rfc3339Renderer T.Text where
+  renderRfc3339 t
+    = renderRfc3339     t >>= return . T.decodeUtf8
+
+instance Rfc3339Renderer TL.Text where
+  renderRfc3339 t
+    = renderRfc3339     t >>= return . TL.decodeUtf8
+
+instance Rfc3339Renderer [Char] where
+  renderRfc3339 t 
+    = renderRfc3339     t >>= return . T.unpack
diff --git a/Data/UTC/Format/Rfc3339/Builder.hs b/Data/UTC/Format/Rfc3339/Builder.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Format/Rfc3339/Builder.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE Safe #-}
+module Data.UTC.Format.Rfc3339.Builder
+  ( rfc3339Builder
+  ) where
+
+import Data.Monoid
+import Data.ByteString.Builder as BS
+
+import Data.UTC.Type.Local
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+
+rfc3339Builder :: (Monad m, IsDate t, IsTime t) => Local t -> m BS.Builder
+rfc3339Builder (Local t os)
+  = do -- calculate the single digits
+       let y3 = fromIntegral $ year t   `div` 1000 `mod` 10
+           y2 = fromIntegral $ year t   `div` 100  `mod` 10
+           y1 = fromIntegral $ year t   `div` 10   `mod` 10
+           y0 = fromIntegral $ year t   `div` 1    `mod` 10
+           m1 = fromIntegral $ month t  `div` 10   `mod` 10
+           m0 = fromIntegral $ month t  `div` 1    `mod` 10
+           d1 = fromIntegral $ day t    `div` 10   `mod` 10
+           d0 = fromIntegral $ day t    `div` 1    `mod` 10
+           h1 = fromIntegral $ hour t   `div` 10   `mod` 10
+           h0 = fromIntegral $ hour t   `div` 1    `mod` 10
+           n1 = fromIntegral $ minute t `div` 10   `mod` 10
+           n0 = fromIntegral $ minute t `div` 1    `mod` 10
+           s1 = fromIntegral $ second t `div` 10   `mod` 10
+           s0 = fromIntegral $ second t `div` 1    `mod` 10
+           f2 = truncate (secondFraction t * 10) `mod` 10
+           f1 = truncate (secondFraction t * 100) `mod` 10
+           f0 = truncate (secondFraction t * 1000) `mod` 10
+       return $ mconcat
+        [ BS.word16HexFixed (y3*16*16*16 + y2*16*16 + y1*16 + y0)
+        , BS.char7 '-'
+        , BS.word8HexFixed (m1*16 + m0)
+        , BS.char7 '-'
+        , BS.word8HexFixed (d1*16 + d0)
+        , BS.char7 'T'
+        , BS.word8HexFixed (h1*16 + h0)
+        , BS.char7 ':'
+        , BS.word8HexFixed (n1*16 + n0)
+        , BS.char7 ':'
+        , BS.word8HexFixed (s1*16 + s0)
+        , if f0 == 0
+            then if f1 == 0
+                   then if f2 == 0
+                          then mempty
+                          else BS.char7 '.' `mappend` BS.intDec f2
+                   else BS.char7 '.' `mappend` BS.intDec f2 `mappend` BS.intDec f1
+            else BS.char7 '.' `mappend` BS.intDec f2 `mappend` BS.intDec f1 `mappend` BS.intDec f0
+        , case os of
+            Nothing -> BS.string7 "-00:00"
+            Just 0  -> BS.char7 'Z'
+            Just o  -> let oh1 = fromIntegral $ abs (truncate o `quot` 600          `rem` 10 :: Integer)
+                           oh0 = fromIntegral $ abs (truncate o `quot` 60           `rem` 10 :: Integer)
+                           om1 = fromIntegral $ abs (truncate o `rem`  60 `quot` 10 `rem` 10 :: Integer)
+                           om0 = fromIntegral $ abs (truncate o `rem`  60           `rem` 10 :: Integer)
+                       in  mconcat
+                             [ if o < 0
+                                 then BS.char7 '-'
+                                 else BS.char7 '+'
+                             , BS.word8HexFixed (oh1*16 + oh0)
+                             , BS.char7 ':'
+                             , BS.word8HexFixed (om1*16 + om0)
+                             ]
+        ]
diff --git a/Data/UTC/Format/Rfc3339/Parser.hs b/Data/UTC/Format/Rfc3339/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Format/Rfc3339/Parser.hs
@@ -0,0 +1,112 @@
+module Data.UTC.Format.Rfc3339.Parser
+  ( rfc3339Parser
+  ) where
+
+import Data.Ratio
+
+import Data.Attoparsec.ByteString ( Parser, skipWhile, choice, option, satisfy )
+import Data.Attoparsec.ByteString.Char8 ( char, isDigit_w8 )
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+import Data.UTC.Type.Local
+
+rfc3339Parser :: (IsDate t, IsTime t) => Parser (Local t)
+rfc3339Parser 
+  = do year'    <- dateFullYear
+       _        <- char '-'
+       month'   <- dateMonth
+       _        <- char '-'
+       day'     <- dateMDay
+       _        <- char 'T'
+       hour'    <- timeHour
+       _        <- char ':'
+       minute'  <- timeMinute
+       _        <- char ':'
+       second'  <- timeSecond
+       secfrac' <- option 0 timeSecfrac
+       offset'  <- timeOffset
+       datetime <- return epoch
+            >>= setYear                year'
+            >>= setMonth               month'
+            >>= setDay                 day'
+            >>= setHour                hour'
+            >>= setMinute              minute'
+            >>= setSecond              second'
+            >>= setSecondFraction      secfrac'
+       return (Local datetime offset')
+  where
+    dateFullYear
+      = decimal4
+    dateMonth
+      = decimal2
+    dateMDay
+      = decimal2
+    timeHour
+      = decimal2
+    timeMinute
+      = decimal2
+    timeSecond
+      = decimal2
+    timeSecfrac
+      = do _ <- char '.'
+           choice
+             [ do d <- decimal3
+                  skipWhile isDigit_w8
+                  return (d % 1000)
+             , do d <- decimal2
+                  return (d % 100)
+             , do d <- decimal1
+                  return (d % 10)
+             ]
+    timeOffset
+      = choice
+          [ do _  <- char 'Z'
+               return $ Just 0
+          , do _  <- char '+'
+               x1 <- decimal2
+               _  <- char ':'
+               x2 <- decimal2
+               return $ Just
+                      $ (x1 * 3600 + x2 * 60) % 1
+          , do _  <- char '-'
+               _  <- char '0'
+               _  <- char '0'
+               _  <- char ':'
+               _  <- char '0'
+               _  <- char '0'
+               return Nothing
+          , do _  <- char '-'
+               x1 <- decimal2
+               _  <- char ':'
+               x2 <- decimal2
+               return $ Just
+                      $ negate
+                      $ (x1 * 3600 + x2 * 60) % 1
+          ]
+
+    decimal1
+      = do w8 <- satisfy isDigit_w8
+           return (fromIntegral (w8 - 48))
+    decimal2
+      = do d1 <- decimal1
+           d2 <- decimal1
+           return $ d1 * 10
+                  + d2
+    decimal3
+      = do d1 <- decimal1
+           d2 <- decimal1
+           d3 <- decimal1
+           return $ d1 * 100
+                  + d2 * 10
+                  + d3
+    decimal4
+      = do d1 <- decimal1
+           d2 <- decimal1
+           d3 <- decimal1
+           d4 <- decimal1
+           return $ d1 * 1000
+                  + d2 * 100
+                  + d3 * 10
+                  + d4
diff --git a/Data/UTC/Internal.hs b/Data/UTC/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Internal.hs
@@ -0,0 +1,162 @@
+module Data.UTC.Internal
+  ( daysToYearMonthDay
+  , yearMonthDayToDays
+
+  , yearToDays
+  , daysToYear
+
+  , deltaUnixEpochCommonEpoch
+
+  , isValidDate
+
+  , secsPerDay, secsPerHour, secsPerMinute, minsPerHour, hoursPerDay
+  , monthsPerYear
+  ) where
+
+deltaUnixEpochCommonEpoch :: Rational
+deltaUnixEpochCommonEpoch
+  = 62167219200
+
+secsPerDay :: Integer
+secsPerDay
+  = hoursPerDay * secsPerHour
+
+secsPerHour :: Integer
+secsPerHour
+  = minsPerHour * secsPerMinute
+
+secsPerMinute :: Integer
+secsPerMinute
+  = 60
+
+minsPerHour :: Integer
+minsPerHour
+  = 60
+
+hoursPerDay :: Integer
+hoursPerDay
+  = 24
+
+monthsPerYear :: Integer
+monthsPerYear
+  = 12
+
+-- | Convert Year-Month-Day to since 0000-01-01 in the Gregorian Calendar
+--
+--    * year   0         is     a leap year
+--    * year 400         is     a leap year
+--    * year 100,200,300 is not a leap year
+--    * year / 4         is     a leap year
+--    * year (-4) (5 BC) is     a leap year
+
+--    * AD/BC vs. astronomical year numbering (as used by ISO8601)
+--    * year   0         ia 1 BC
+--    * year (-1)        is 2 BC
+--    * year (-2)        is 3 BC etc.
+
+yearMonthDayToDays :: (Integer, Integer, Integer) -> Integer
+yearMonthDayToDays (year,month,day)
+  = -- count of days of the "finalised" years
+    let daysY = yearToDays year
+    -- count of days of the "finalised" months
+        daysM = case month - 1 of
+                  1  -> 31
+                  2  -> 31 + 28 + leapDay
+                  3  -> 31 + 28 + 31 + leapDay
+                  4  -> 31 + 28 + 31 + 30 + leapDay
+                  5  -> 31 + 28 + 31 + 30 + 31 + leapDay
+                  6  -> 31 + 28 + 31 + 30 + 31 + 30 + leapDay
+                  7  -> 31 + 28 + 31 + 30 + 31 + 30 + 31 + leapDay
+                  8  -> 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + leapDay
+                  9  -> 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + leapDay
+                  10 -> 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + leapDay
+                  11 -> 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + leapDay
+                  _  -> 0
+       -- count of the "finalised" days
+        daysD = day - 1
+    in  daysY + daysM + daysD
+  where
+    leapDay :: Integer
+    leapDay
+      | (year `mod` 4 == 0) && ((year `mod` 400 == 0) || (year `mod` 100 /= 0)) = 1
+      | otherwise                                                               = 0
+
+yearToDays :: Integer -> Integer
+yearToDays y
+  | y == 0    = 0
+  | y >= 0    = 366
+              + ((y-1) * 365)       -- .. and a normal year has 365 days ..
+              + ((y-1) `quot` 4)    -- .. and every 4 years a leap day occurs..
+              - ((y-1) `quot` 100)  -- .. but not in centuries ..
+              + ((y-1) `quot` 400)  -- .. except every 400 years.
+  | otherwise = (y * 365)
+              + (y `quot` 4)
+              - (y `quot` 100)
+              + (y `quot` 400)
+
+daysToYear :: Integer -> Integer
+daysToYear ds
+  = if ds <= d
+      then y - 1
+      else y
+  where
+    y = ds `div` 365
+    d = yearToDays y
+
+daysToYearMonthDay :: Integer -> (Integer, Integer, Integer)
+daysToYearMonthDay d'
+  = (year + y400 * 400, month, day)
+  where
+    d400            = yearToDays 400
+    y400            = d' `div` d400
+    d               = d' `mod` d400
+    year            = daysToYear (d + 1)
+    ld              = if (year `mod` 4   == 0) &&
+                        ((year `mod` 400 == 0)
+                      || (year `mod` 100 /= 0)) then (1+) else id
+    doy             = d - yearToDays year
+    (month,day) 
+     | doy <     31 = ( 1, doy          + 1)
+     | doy < ld  59 = ( 2, doy -     31 + 1)
+     | doy < ld  90 = ( 3, doy - ld  59 + 1)
+     | doy < ld 120 = ( 4, doy - ld  90 + 1)
+     | doy < ld 151 = ( 5, doy - ld 120 + 1)
+     | doy < ld 181 = ( 6, doy - ld 151 + 1)
+     | doy < ld 212 = ( 7, doy - ld 181 + 1)
+     | doy < ld 243 = ( 8, doy - ld 212 + 1)
+     | doy < ld 273 = ( 9, doy - ld 243 + 1)
+     | doy < ld 304 = (10, doy - ld 273 + 1)
+     | doy < ld 334 = (11, doy - ld 304 + 1)
+     | otherwise    = (12, doy - ld 334 + 1)
+
+isValidDate :: (Integer, Integer, Integer) -> Bool
+isValidDate (y,m,d)
+  | m < 1  = False
+  | m > 12 = False
+  | otherwise = case m of
+                   1  -> validateDays31
+                   2  -> validateDays28or29
+                   3  -> validateDays31
+                   4  -> validateDays30
+                   5  -> validateDays31
+                   6  -> validateDays30
+                   7  -> validateDays31
+                   8  -> validateDays31
+                   9  -> validateDays30
+                   10 -> validateDays31
+                   11 -> validateDays30
+                   12 -> validateDays31
+                   _  -> False
+  where
+    validateDays31
+      | 1 <= d && d <= 31           = True
+      | otherwise                   = False
+    validateDays30
+      | 1 <= d && d <= 30           = True
+      | otherwise                   = False
+    validateDays28or29
+      | 1 <= d && d <= 28           = True
+      | d == 29 && isLeapYear       = True
+      | otherwise                   = False
+    isLeapYear
+      = (y `mod` 4 == 0) && ((y `mod` 400 == 0) || (y `mod` 100 /= 0))
diff --git a/Data/UTC/Internal/Test.hs b/Data/UTC/Internal/Test.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Internal/Test.hs
@@ -0,0 +1,332 @@
+module Data.UTC.Internal.Test where
+
+import Test.QuickCheck
+import Test.Framework (testGroup)
+import Test.Framework (Test)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Data.UTC.Internal
+
+test :: Test
+test
+  = testGroup "Data.UTC.Internal"
+  $ [ testYearMonthDayToDays
+    , testYearToDays
+    , testYearToDaysAndDaysToYear
+    , testDaysToYearMonthDay
+    , testYearMonthDayToDaysAndDaysToYearMonthDay
+
+    , testProperty "yearMonthDayToDays for every day in year 0-400"
+    $ conjoin
+    $ map (\(ds, ymd)-> ds == yearMonthDayToDays ymd
+          ) (dayDateTuples)
+
+    , testProperty "daysToYearMonthDay for every day in year 0-400"
+    $ conjoin
+    $ map (\(ds, ymd)-> daysToYearMonthDay ds == ymd
+          ) (dayDateTuples)
+    ]
+
+dayDateTuples :: [(Integer, (Integer, Integer, Integer))]
+dayDateTuples
+  = zip [0..]
+        [ (y,m,d) | y <- [0..400]
+                  , m <- [1..12]
+                  , d <- [1..31]
+                  , isValidDate (y,m,d)
+        ]
+
+testYearMonthDayToDays :: Test
+testYearMonthDayToDays
+  = testGroup "yearMonthDayToDays"
+  $ [ testProperty ("yearMonthDayToDays (0,1,1) === 0")
+    $ yearMonthDayToDays (0,1,1) === 0
+
+    , testProperty ("yearMonthDayToDays (1,1,1) === 366 + 365")
+    $ yearMonthDayToDays (1,1,1) === 366
+
+    , testProperty ("yearMonthDayToDays (2,1,1) === 366 + 365")
+    $ yearMonthDayToDays (2,1,1) === 366 + 365
+
+    , testProperty ("yearMonthDayToDays (3,1,1) === 366 + 365 + 365")
+    $ yearMonthDayToDays (3,1,1) === 366 + 365 + 365
+
+    , testProperty ("yearMonthDayToDays (4,1,1) === 366 + 365 + 365 + 365")
+    $ yearMonthDayToDays (4,1,1) === 366 + 365 + 365 + 365
+
+    , testProperty ("yearMonthDayToDays (4,3,3) === 366 + 365 + 365 + 365 + 31 + 29 + 2")
+    $ yearMonthDayToDays (4,3,3) === 366 + 365 + 365 + 365 + 31 + 29 + 2
+    ]
+
+testYearToDaysAndDaysToYear :: Test
+testYearToDaysAndDaysToYear
+  = testGroup "yearToDays <-> daysToYear"
+  $ [ testProperty "yearToDays (daysToYear x) - x === 0"
+    $ forAll (choose (0, 146463))
+    $ \x-> (x - yearToDays (daysToYear x)) `div` 366 === 0
+    ]
+
+testYearToDays :: Test
+testYearToDays
+  = testGroup "yearToDays"
+  $ [ testProperty "400 years are 146463 days"
+    $ yearToDays 401    === (fromIntegral $ length dayDateTuples)
+    , testProperty ("yearToDays 800    === 365 * 800 + length " ++ show (js 800))
+    $ yearToDays 800    === 365 * 800 + (fromIntegral $ length (js 800))
+    , testProperty ("yearToDays 700    === 365 * 700 + length " ++ show (js 700))
+    $ yearToDays 700    === 365 * 700 + (fromIntegral $ length (js 700))
+    , testProperty ("yearToDays 600    === 365 * 600 + length " ++ show (js 600))
+    $ yearToDays 600    === 365 * 600 + (fromIntegral $ length (js 600))
+    , testProperty ("yearToDays 500    === 365 * 500 + length " ++ show (js 500))
+    $ yearToDays 500    === 365 * 500 + (fromIntegral $ length (js 500))
+    , testProperty ("yearToDays 401    === 365 * 401 + length " ++ show (js 401))
+    $ yearToDays 401    === 365 * 401 + (fromIntegral $ length (js 401))
+    , testProperty ("yearToDays 400    === 365 * 400 + length " ++ show (js 400))
+    $ yearToDays 400    === 365 * 400 + (fromIntegral $ length (js 400))
+    , testProperty ("yearToDays 399    === 365 * 399 + length " ++ show (js 399))
+    $ yearToDays 399    === 365 * 399 + (fromIntegral $ length (js 399))
+    , testProperty ("yearToDays 300    === 365 * 300 + length " ++ show (js 300))
+    $ yearToDays 300    === 365 * 300 + (fromIntegral $ length (js 300))
+    , testProperty ("yearToDays 200    === 365 * 200 + length " ++ show (js 200))
+    $ yearToDays 200    === 365 * 200 + (fromIntegral $ length (js 200))
+    , testProperty ("yearToDays 101    === 365 * 101 + length " ++ show (js 101))
+    $ yearToDays 101    === 365 * 101 + (fromIntegral $ length (js 101))
+    , testProperty ("yearToDays 100    === 365 * 100 + length " ++ show (js 100))
+    $ yearToDays 100    === 365 * 100 + (fromIntegral $ length (js 100))
+    , testProperty ("yearToDays  99    === 365 *  99 + length " ++ show (js 99))
+    $ yearToDays  99    === 365 * 99 + (fromIntegral $ length (js 99))
+    , testProperty ("yearToDays   4    === 365 * 4 + length " ++ show (js 4))
+    $ yearToDays   4    === 365 * 4 + (fromIntegral $ length (js 4))
+    , testProperty ("yearToDays   3    === 365 * 3 + length " ++ show (js 3))
+    $ yearToDays   3    === 365 * 3 + (fromIntegral $ length (js 3))
+    , testProperty ("yearToDays   2    === 365 * 2 + length " ++ show (js 2))
+    $ yearToDays   2    === 365 * 2 + (fromIntegral $ length (js 2))
+    , testProperty ("yearToDays   1    === 365 * 1 + length " ++ show (js 1))
+    $ yearToDays   1    === 365 * 1 + (fromIntegral $ length (js 1))
+    , testProperty  "yearToDays   0    === 0"
+    $ yearToDays   0    === 0
+    , testProperty  "yearToDays (-1)   === 0 - 365"
+    $ yearToDays (-1)   === 0 - 365
+    , testProperty  "yearToDays (-2)   === 0 - 365 - 365"
+    $ yearToDays (-2)   === 0 - 365 - 365
+    , testProperty  "yearToDays (-3)   === 0 - 365 - 365 - 365"
+    $ yearToDays (-3)   === 0 - 365 - 365 - 365
+    , testProperty  "yearToDays (-4)   === 0 - 365 - 365 - 365 - 366"
+    $ yearToDays (-4)   === 0 - 365 - 365 - 365 - 366
+    , testProperty ("yearToDays (-100) === (-365) * 100 - length " ++ show (ls 100))
+    $ yearToDays (-100) === (-365) * 100 - (fromIntegral $ length (ls 100))
+    , testProperty ("yearToDays (-200) === (-365) * 200 - length " ++ show (ls 200))
+    $ yearToDays (-200) === (-365) * 200 - (fromIntegral $ length (ls 200))
+    , testProperty ("yearToDays (-300) === (-365) * 300 - length " ++ show (ls 300))
+    $ yearToDays (-300) === (-365) * 300 - (fromIntegral $ length (ls 300))
+    , testProperty ("yearToDays (-400) === (-365) * 400 - length " ++ show (ls 400))
+    $ yearToDays (-400) === (-365) * 400 - (fromIntegral $ length (ls 400))
+    ]
+  where
+    ls  :: Int -> [Int]
+    ls z = [x | x <- [negate z..(-1)], x `mod` 400 == 0 || (x `mod` 4 == 0 && x `mod` 100 /= 0 )]
+    js  :: Int -> [Int]
+    js z = [x | x <- [0..(z-1)], x `mod` 400 == 0 || (x `mod` 4 == 0 && x `mod` 100 /= 0 )]
+
+testDaysToYearMonthDay :: Test
+testDaysToYearMonthDay
+  = testGroup "daysToYearMonthDay"
+  $ [ 
+      testProperty "daysToYearMonthDay 366 === (1,1,1)"
+    $ daysToYearMonthDay 366 === (1,1,1)
+
+    , testProperty "daysToYearMonthDay 365 === (0,12,31)"
+    $ daysToYearMonthDay 365 === (0,12,31)
+
+    , testProperty "daysToYearMonthDay 335 === (0,12,1)"
+    $ daysToYearMonthDay 335 === (0,12,1)
+
+    , testProperty "daysToYearMonthDay 334 === (0,11,30)"
+    $ daysToYearMonthDay 334 === (0,11,30)
+
+    , testProperty "daysToYearMonthDay 305 === (0,11,1)"
+    $ daysToYearMonthDay 305 === (0,11,1)
+
+    , testProperty "daysToYearMonthDay 304 === (0,10,31)"
+    $ daysToYearMonthDay 304 === (0,10,31)
+
+    , testProperty "daysToYearMonthDay 274 === (0,10,1)"
+    $ daysToYearMonthDay 274 === (0,10,1)
+
+    , testProperty "daysToYearMonthDay 273 === (0,9,30)"
+    $ daysToYearMonthDay 273 === (0,9,30)
+
+    , testProperty "daysToYearMonthDay 244 === (0,9,1)"
+    $ daysToYearMonthDay 244 === (0,9,1)
+
+    , testProperty "daysToYearMonthDay 243 === (0,8,31)"
+    $ daysToYearMonthDay 243 === (0,8,31)
+
+    , testProperty "daysToYearMonthDay 213 === (0,8,1)"
+    $ daysToYearMonthDay 213 === (0,8,1)
+
+    , testProperty "daysToYearMonthDay 212 === (0,7,31)"
+    $ daysToYearMonthDay 212 === (0,7,31)
+
+    , testProperty "daysToYearMonthDay 182 === (0,7,1)"
+    $ daysToYearMonthDay 182 === (0,7,1)
+
+    , testProperty "daysToYearMonthDay 181 === (0,6,30)"
+    $ daysToYearMonthDay 181 === (0,6,30)
+
+    , testProperty "daysToYearMonthDay 152 === (0,6,1)"
+    $ daysToYearMonthDay 152 === (0,6,1)
+
+    , testProperty "daysToYearMonthDay 151 === (0,5,31)"
+    $ daysToYearMonthDay 151 === (0,5,31)
+
+    , testProperty "daysToYearMonthDay 121 === (0,5,1)"
+    $ daysToYearMonthDay 121 === (0,5,1)
+
+    , testProperty "daysToYearMonthDay 120 === (0,4,30)"
+    $ daysToYearMonthDay 120 === (0,4,30)
+
+    , testProperty "daysToYearMonthDay 91 === (0,4,1)"
+    $ daysToYearMonthDay 91 === (0,4,1)
+
+    , testProperty "daysToYearMonthDay 90 === (0,3,31)"
+    $ daysToYearMonthDay 90 === (0,3,31)
+
+    , testProperty "daysToYearMonthDay 60 === (0,3,1)"
+    $ daysToYearMonthDay 60 === (0,3,1)
+
+    , testProperty "daysToYearMonthDay 59 === (0,2,29)"
+    $ daysToYearMonthDay 59 === (0,2,29)
+
+    , testProperty "daysToYearMonthDay 58 === (0,2,28)"
+    $ daysToYearMonthDay 58 === (0,2,28)
+
+    , testProperty "daysToYearMonthDay 31 === (0,2,1)"
+    $ daysToYearMonthDay 31 === (0,2,1)
+
+    , testProperty "daysToYearMonthDay 30 === (0,1,31)"
+    $ daysToYearMonthDay 30 === (0,1,31)
+
+    , testProperty "daysToYearMonthDay 0 === (0,1,1)"
+    $ daysToYearMonthDay 0 === (0,1,1)
+
+    , testProperty "daysToYearMonthDay (-1) === (-1,12,31)"
+    $ daysToYearMonthDay (-1) === (-1,12,31)
+
+    , testProperty "daysToYearMonthDay (-31) === (-1,12,1)"
+    $ daysToYearMonthDay (-31) === (-1,12,1)
+
+    , testProperty "daysToYearMonthDay (-32) === (-1,11,30)"
+    $ daysToYearMonthDay (-32) === (-1,11,30)
+
+    , testProperty "daysToYearMonthDay (-61) === (-1,11,1)"
+    $ daysToYearMonthDay (-61) === (-1,11,1)
+
+    , testProperty "daysToYearMonthDay (-62) === (-1,10,31)"
+    $ daysToYearMonthDay (-62) === (-1,10,31)
+
+    , testProperty "daysToYearMonthDay (-92) === (-1,10,1)"
+    $ daysToYearMonthDay (-92) === (-1,10,1)
+
+    , testProperty "daysToYearMonthDay (-93) === (-1,9,30)"
+    $ daysToYearMonthDay (-93) === (-1,9,30)
+
+    , testProperty "daysToYearMonthDay (-103) === (-1,9,20)"
+    $ daysToYearMonthDay (-103) === (-1,9,20)
+
+    , testProperty "daysToYearMonthDay (-108) === (-1,9,15)"
+    $ daysToYearMonthDay (-108) === (-1,9,15)
+
+    , testProperty "daysToYearMonthDay (-111) === (-1,9,12)"
+    $ daysToYearMonthDay (-111) === (-1,9,12)
+
+    , testProperty "daysToYearMonthDay (-112) === (-1,9,11)"
+    $ daysToYearMonthDay (-112) === (-1,9,11)
+
+    , testProperty "daysToYearMonthDay (-113) === (-1,9,10)"
+    $ daysToYearMonthDay (-113) === (-1,9,10)
+
+    , testProperty "daysToYearMonthDay (-114) === (-1,9,9)"
+    $ daysToYearMonthDay (-114) === (-1,9,9)
+
+    , testProperty "daysToYearMonthDay (-122) === (-1,9,1)"
+    $ daysToYearMonthDay (-122) === (-1,9,1)
+
+    , testProperty "daysToYearMonthDay (-123) === (-1,8,31)"
+    $ daysToYearMonthDay (-123) === (-1,8,31)
+
+    , testProperty "daysToYearMonthDay (-153) === (-1,8,1)"
+    $ daysToYearMonthDay (-153) === (-1,8,1)
+
+    , testProperty "daysToYearMonthDay (-154) === (-1,7,31)"
+    $ daysToYearMonthDay (-154) === (-1,7,31)
+
+    , testProperty "daysToYearMonthDay (-184) === (-1,7,1)"
+    $ daysToYearMonthDay (-184) === (-1,7,1)
+
+    , testProperty "daysToYearMonthDay (-185) === (-1,6,30)"
+    $ daysToYearMonthDay (-185) === (-1,6,30)
+
+    , testProperty "daysToYearMonthDay (-214) === (-1,6,1)"
+    $ daysToYearMonthDay (-214) === (-1,6,1)
+
+    , testProperty "daysToYearMonthDay (-215) === (-1,5,31)"
+    $ daysToYearMonthDay (-215) === (-1,5,31)
+
+    , testProperty "daysToYearMonthDay (-245) === (-1,5,1)"
+    $ daysToYearMonthDay (-245) === (-1,5,1)
+
+    , testProperty "daysToYearMonthDay (-246) === (-1,4,30)"
+    $ daysToYearMonthDay (-246) === (-1,4,30)
+
+    , testProperty "daysToYearMonthDay (-275) === (-1,4,1)"
+    $ daysToYearMonthDay (-275) === (-1,4,1)
+
+    , testProperty "daysToYearMonthDay (-276) === (-1,3,31)"
+    $ daysToYearMonthDay (-276) === (-1,3,31)
+
+    , testProperty "daysToYearMonthDay (-306) === (-1,3,1)"
+    $ daysToYearMonthDay (-306) === (-1,3,1)
+
+    , testProperty "daysToYearMonthDay (-307) === (-1,2,28)"
+    $ daysToYearMonthDay (-307) === (-1,2,28)
+
+    , testProperty "daysToYearMonthDay (-334) === (-1,2,1)"
+    $ daysToYearMonthDay (-334) === (-1,2,1)
+
+    , testProperty "daysToYearMonthDay (-335) === (-1,1,31)"
+    $ daysToYearMonthDay (-335) === (-1,1,31)
+
+    , testProperty "daysToYearMonthDay (-365) === (-1,1,1)"
+    $ daysToYearMonthDay (-365) === (-1,1,1)
+
+    , testProperty "daysToYearMonthDay (-366) === (-2,12,31)"
+    $ daysToYearMonthDay (-366) === (-2,12,31)
+    ]
+
+testYearMonthDayToDaysAndDaysToYearMonthDay :: Test
+testYearMonthDayToDaysAndDaysToYearMonthDay
+  = testGroup "yearMonthDayToDays <-> daysToYearMonthDay"
+  $ [ testProperty ("daysToYearMonthDay (yearMonthDayToDays (0,1,1)) === (0,1,1)")
+    $ daysToYearMonthDay (yearMonthDayToDays (0,1,1)) === (0,1,1)
+
+    , testProperty ("yearMonthDayToDays (daysToYearMonthDay x) - x === 0 for 0 < x < 1000")
+    $ forAll (choose (0, 1000))
+    $ \x-> yearMonthDayToDays (daysToYearMonthDay x) - x === 0
+
+    , testProperty ("yearMonthDayToDays (daysToYearMonthDay x) - x === 0 for 0 < x < 2000")
+    $ forAll (choose (0, 2000))
+    $ \x-> yearMonthDayToDays (daysToYearMonthDay x) - x === 0
+
+    , testProperty ("yearMonthDayToDays (daysToYearMonthDay x) - x === 0 for 0 < x < 3652424")
+    $ forAll (choose (0, 3652424)) -- 0000-01-01 to 9999-12-31
+    $ \x-> yearMonthDayToDays (daysToYearMonthDay x) - x === 0
+
+    , testProperty ("yearMonthDayToDays (daysToYearMonthDay x) - x === 0 for -100 < x < 100")
+    $ forAll (choose ((-100), 100)) -- 0000-01-01 to 9999-12-31
+    $ \x-> yearMonthDayToDays (daysToYearMonthDay x) - x === 0
+
+    , testProperty ("yearMonthDayToDays (daysToYearMonthDay x) - x === 0 for -1000000 < x < 5000000")
+    $ forAll (choose (-1000000, 5000000)) -- 0000-01-01 to 9999-12-31
+    $ \x-> yearMonthDayToDays (daysToYearMonthDay x) - x === 0
+    ]
diff --git a/Data/UTC/Type.hs b/Data/UTC/Type.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Type.hs
@@ -0,0 +1,11 @@
+module Data.UTC.Type
+  ( module Data.UTC.Type.Date
+  , module Data.UTC.Type.Time
+  , module Data.UTC.Type.DateTime
+  , module Data.UTC.Type.Local
+  ) where
+
+import Data.UTC.Type.Date
+import Data.UTC.Type.Time
+import Data.UTC.Type.DateTime
+import Data.UTC.Type.Local
diff --git a/Data/UTC/Type/Date.hs b/Data/UTC/Type/Date.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Type/Date.hs
@@ -0,0 +1,71 @@
+module Data.UTC.Type.Date
+  ( Date ()
+  ) where
+
+import Data.Ratio
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsUnixTime
+import Data.UTC.Internal
+
+-- | This type represents dates in the __Proleptic Gregorian Calendar__.
+--
+--   * It can represent any date in the past and in the future by using
+--     'Prelude.Integer' internally.
+--   * The internal structure is not exposed to avoid the construction of invalid values.
+--     Use 'Data.UTC.epoch' or a parser to construct values.
+--   * The instance of 'Prelude.Show' is only meant for debugging purposes
+--     and is subject to change.
+data Date
+   = Date
+     { dYear           :: Integer
+     , dMonth          :: Integer
+     , dDay            :: Integer
+     } deriving (Eq, Ord, Show)
+
+instance Epoch Date where
+  epoch
+    = Date
+      { dYear           = 1970
+      , dMonth          = 1
+      , dDay            = 1
+      }
+
+instance IsUnixTime Date where
+  unixSeconds t
+    = (days       * secsPerDay    % 1)
+    - deltaUnixEpochCommonEpoch
+    where
+      days = yearMonthDayToDays (year t, month t, day t)
+  fromUnixSeconds u
+    = return
+    $ Date
+      { dYear           = y
+      , dMonth          = m
+      , dDay            = d
+      }
+    where
+      s         = u + deltaUnixEpochCommonEpoch
+      (y, m, d) = daysToYearMonthDay (truncate s `div` secsPerDay)
+
+instance IsDate Date where
+  year
+    = dYear
+  month
+    = dMonth
+  day
+    = dDay
+  setYear x t
+    = if isValidDate (x, month t, day t)
+      then return $ t { dYear  = x }
+      else fail   $ "Dated.setYear "  ++ show x
+  setMonth x t
+    = if isValidDate (year t, x, day t)
+      then return $ t { dMonth = x }
+      else fail   $ "Dated.setMonth " ++ show x
+  setDay x t
+    = if isValidDate (year t, month t, x)
+      then return $ t { dDay   = x }
+      else fail   $ "Dated.setDay "   ++ show x
+
diff --git a/Data/UTC/Type/DateTime.hs b/Data/UTC/Type/DateTime.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Type/DateTime.hs
@@ -0,0 +1,228 @@
+{-# LANGUAGE FlexibleInstances #-}
+module Data.UTC.Type.DateTime
+  ( -- * Type
+    DateTime (..)
+  ) where
+
+import Data.String
+import Data.Maybe
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.IsDate
+import Data.UTC.Class.IsTime
+import Data.UTC.Class.IsUnixTime
+import Data.UTC.Type.Date
+import Data.UTC.Type.Time
+import Data.UTC.Type.Local
+import Data.UTC.Format.Rfc3339
+import Data.UTC.Internal
+
+-- | A time representation based on a 'Data.UTC.Date' and the 'Data.UTC.Time' of the day.
+--
+--   * The type uses multiprecision integers internally and is able to represent
+--     any UTC date in the past and in the future with arbitrary precision
+--     (apart from the time span within a leap second).
+--   * The instances of 'Data.String.IsString' and 'Prelude.Show' are only
+--     meant for debugging purposes and default to 'epoch' in case of
+--     failure. Don't rely on their behaviour!
+data DateTime
+   = DateTime
+     { date  :: Date
+     , time  :: Time
+     } deriving (Eq, Ord)
+
+instance Show DateTime where
+  show = fromMaybe "1970-01-01T00:00:00-00:00" . renderRfc3339 . unknown
+
+instance Show (Local DateTime) where
+  show = fromMaybe "1970-01-01T00:00:00-00:00" . renderRfc3339
+
+instance IsString DateTime where
+  fromString = utc . fromMaybe epoch . parseRfc3339
+
+instance Epoch DateTime where
+  epoch = DateTime epoch midnight
+
+instance IsUnixTime DateTime where
+  unixSeconds (DateTime d t)
+    = unixSeconds d
+    + unixSeconds t
+  fromUnixSeconds u
+    = do d <- fromUnixSeconds u
+         t <- fromUnixSeconds u
+         return (DateTime d t)
+
+instance IsDate DateTime where
+  year
+    = year . date
+  month
+    = month . date
+  day
+    = day . date
+  setYear y t
+    = do dt <- setYear y (date t)
+         return $ t { date = dt }
+  setMonth m t
+    = do dt <- setMonth m (date t)
+         return $ t { date = dt }
+  setDay d t
+    = do dt <- setDay d (date t)
+         return $ t { date = dt }
+
+instance IsTime DateTime where
+  hour
+    = hour . time
+  minute
+    = minute . time
+  second
+    = second . time
+  secondFraction
+    = secondFraction . time
+  setHour y t
+    = do tm <- setHour y (time t)
+         return $ t { time = tm }
+  setMinute y t
+    = do tm <- setMinute y (time t)
+         return $ t { time = tm }
+  setSecond y t
+    = do tm <- setSecond y (time t)
+         return $ t { time = tm }
+  setSecondFraction y t
+    = do tm <- setSecondFraction y (time t)
+         return $ t { time = tm }
+
+  -- The default implementation of addHours fails whena day flows over. 
+  -- For DateTimes we can let it ripple into the days.
+  addHours h t
+    = setHour hors t >>= addDays days
+    where
+      h'   = h + (hour t)
+      hors = h' `mod` hoursPerDay
+      days = h' `div` hoursPerDay
+
+-- assumption: addSecondFractions for DateTime is always successful
+instance IsDate (Local DateTime) where
+  year (Local t Nothing)
+    = year t
+  year (Local t (Just 0))
+    = year t
+  year (Local t (Just o))
+    = year
+    $ fromMaybe undefined $ addSecondFractions o t
+  month (Local t Nothing)
+    = month t
+  month (Local t (Just 0))
+    = month t
+  month (Local t (Just o))
+    = month
+    $ fromMaybe undefined $ addSecondFractions o t
+  day (Local t Nothing)
+    = day t
+  day (Local t (Just 0))
+    = day t
+  day  (Local t (Just o))
+    = day
+    $ fromMaybe undefined $ addSecondFractions o t
+  setYear h (Local t o@Nothing)
+    = do t' <- setYear h t
+         return (Local t' o)
+  setYear h (Local t o@(Just 0))
+    = do t' <- setYear h t
+         return (Local t' o)
+  setYear h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setYear h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setMonth h (Local t o@Nothing)
+    = do t' <- setMonth h t
+         return (Local t' o)
+  setMonth h (Local t o@(Just 0))
+    = do t' <- setMonth h t
+         return (Local t' o)
+  setMonth h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setMonth h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setDay h (Local t o@Nothing)
+    = do t' <- setDay h t
+         return (Local t' o)
+  setDay h (Local t o@(Just 0))
+    = do t' <- setDay h t
+         return (Local t' o)
+  setDay h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setDay h >>= addSecondFractions (negate i)
+         return (Local t' o)
+
+-- assumption: addSecondFractions for DateTime is always successful
+instance IsTime (Local DateTime) where
+  hour (Local t Nothing)
+    = hour t
+  hour (Local t (Just 0))
+    = hour t
+  hour (Local t (Just o))
+    = hour
+    $ fromMaybe undefined $ addSecondFractions o t
+  minute (Local t Nothing)
+    = minute t
+  minute (Local t (Just 0))
+    = minute t
+  minute (Local t (Just o))
+    = minute
+    $ fromMaybe undefined $ addSecondFractions o t
+  second (Local t Nothing)
+    = second t
+  second (Local t (Just 0))
+    = second t
+  second  (Local t (Just o))
+    = second
+    $ fromMaybe undefined $ addSecondFractions o t
+  secondFraction (Local t Nothing)
+    = secondFraction t
+  secondFraction (Local t (Just 0))
+    = secondFraction t
+  secondFraction (Local t (Just o))
+    = secondFraction
+    $ fromMaybe undefined $ addSecondFractions o t
+  setHour h (Local t o@Nothing)
+    = do t' <- setHour h t
+         return (Local t' o)
+  setHour h (Local t o@(Just 0))
+    = do t' <- setHour h t
+         return (Local t' o)
+  setHour h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setHour h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setMinute h (Local t o@Nothing)
+    = do t' <- setMinute h t
+         return (Local t' o)
+  setMinute h (Local t o@(Just 0))
+    = do t' <- setMinute h t
+         return (Local t' o)
+  setMinute h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setMinute h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setSecond h (Local t o@Nothing)
+    = do t' <- setSecond h t
+         return (Local t' o)
+  setSecond h (Local t o@(Just 0))
+    = do t' <- setSecond h t
+         return (Local t' o)
+  setSecond h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setSecond h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setSecondFraction h (Local t o@Nothing)
+    = do t' <- setSecondFraction h t
+         return (Local t' o)
+  setSecondFraction h (Local t o@(Just 0))
+    = do t' <- setSecondFraction h t
+         return (Local t' o)
+  setSecondFraction h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setSecondFraction h >>= addSecondFractions (negate i)
+         return (Local t' o)
+
+  -- This one is necessary to override, because the overflow should
+  -- ripple into the date part.
+  addHours h t
+    = setHour hors t >>= addDays days
+    where
+      h'   = h + (hour t)
+      days = h' `div` hoursPerDay
+      hors = h' `mod` hoursPerDay
diff --git a/Data/UTC/Type/Local.hs b/Data/UTC/Type/Local.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Type/Local.hs
@@ -0,0 +1,126 @@
+{-# LANGUAGE Safe, FlexibleInstances #-}
+module Data.UTC.Type.Local
+  ( Local (..)
+  , unknown
+  ) where
+
+import Data.Maybe
+
+import Data.UTC.Class.Epoch
+import Data.UTC.Class.Midnight
+import Data.UTC.Class.IsTime
+import Data.UTC.Type.Time
+
+-- | This type is used to extend UTC time types by a local offset in seconds (positive or negative).
+--
+--   /Beware/: A local offset is not a time zone. It is just a fix
+--   period of time. In contrast to a time zone this does not take
+--   summer or winter time into account.
+data Local time
+   = Local 
+     { -- | The time to be interpreted as UTC+00:00 (__W__estern __E__uropean __T__ime)
+       utc    :: time
+     , -- | ['Nothing'] The local offset is unknown (behaves like __W__estern __E__uropean __T__ime)
+       --
+       -- ['Just' 0] UTC+00:00 (__W__estern __E__uropean __T__ime)
+       --
+       -- ['Just' 3600] UTC+01:00 (__C__entral __E__uropean __T__ime)
+       offset :: Maybe Rational
+     }
+
+instance Eq t => Eq (Local t) where
+  (==) (Local a _) (Local b _)
+    = a == b
+
+instance Ord t => Ord (Local t) where
+  compare (Local a _) (Local b _)
+    = compare a b
+
+instance Epoch t => Epoch (Local t) where
+  epoch
+    = unknown epoch
+
+instance Midnight t => Midnight (Local t) where
+  midnight
+    = unknown midnight
+
+instance Functor Local where
+  fmap f (Local t o)
+    = Local (f t) o
+
+instance Bounded t => Bounded (Local t) where
+  minBound  = unknown minBound
+  maxBound  = unknown maxBound
+
+-- assumption: addSecondFractions for Time is always successful
+instance IsTime (Local Time) where
+  hour (Local t Nothing)
+    = hour t
+  hour (Local t (Just 0))
+    = hour t
+  hour (Local t (Just o))
+    = hour
+    $ fromMaybe undefined $ addSecondFractions o t
+  minute (Local t Nothing)
+    = minute t
+  minute (Local t (Just 0))
+    = minute t
+  minute (Local t (Just o))
+    = minute
+    $ fromMaybe undefined $ addSecondFractions o t
+  second (Local t Nothing)
+    = second t
+  second (Local t (Just 0))
+    = second t
+  second  (Local t (Just o))
+    = second
+    $ fromMaybe undefined $ addSecondFractions o t
+  secondFraction (Local t Nothing)
+    = secondFraction t
+  secondFraction (Local t (Just 0))
+    = secondFraction t
+  secondFraction (Local t (Just o))
+    = secondFraction
+    $ fromMaybe undefined $ addSecondFractions o t
+  setHour h (Local t o@Nothing)
+    = do t' <- setHour h t
+         return (Local t' o)
+  setHour h (Local t o@(Just 0))
+    = do t' <- setHour h t
+         return (Local t' o)
+  setHour h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setHour h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setMinute h (Local t o@Nothing)
+    = do t' <- setMinute h t
+         return (Local t' o)
+  setMinute h (Local t o@(Just 0))
+    = do t' <- setMinute h t
+         return (Local t' o)
+  setMinute h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setMinute h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setSecond h (Local t o@Nothing)
+    = do t' <- setSecond h t
+         return (Local t' o)
+  setSecond h (Local t o@(Just 0))
+    = do t' <- setSecond h t
+         return (Local t' o)
+  setSecond h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setSecond h >>= addSecondFractions (negate i)
+         return (Local t' o)
+  setSecondFraction h (Local t o@Nothing)
+    = do t' <- setSecondFraction h t
+         return (Local t' o)
+  setSecondFraction h (Local t o@(Just 0))
+    = do t' <- setSecondFraction h t
+         return (Local t' o)
+  setSecondFraction h (Local t o@(Just i))
+    = do t' <- addSecondFractions i t >>= setSecondFraction h >>= addSecondFractions (negate i)
+         return (Local t' o)
+
+
+
+unknown :: t -> Local t
+unknown t
+  = Local t Nothing
diff --git a/Data/UTC/Type/Time.hs b/Data/UTC/Type/Time.hs
new file mode 100644
--- /dev/null
+++ b/Data/UTC/Type/Time.hs
@@ -0,0 +1,68 @@
+module Data.UTC.Type.Time
+  ( Time ()
+  , midnight
+  ) where
+
+import Data.Ratio
+
+import Data.UTC.Class.Midnight
+import Data.UTC.Class.IsTime
+import Data.UTC.Class.IsUnixTime
+import Data.UTC.Internal
+
+-- | This type represents time instants during a day (__00:00:00 - 23:59:59.999__..)
+--   with arbitrary precision (uses 'Prelude.Integer' internally).
+--
+--   * The internal structure is not exposed to avoid the creation of
+--     invalid values.
+--     Use 'Data.UTC.midnight' or a parser to construct values.
+--   * The instance of 'Prelude.Show' is only meant for debugging purposes
+--     and is subject to change.
+data Time
+   = Time
+     { tHour           :: Integer
+     , tMinute         :: Integer
+     , tSecond         :: Integer
+     , tSecondFraction :: Rational
+     } deriving (Eq, Ord, Show)
+
+instance Midnight Time where
+  midnight
+    = Time 0 0 0 0
+
+instance IsUnixTime Time where
+  unixSeconds t
+    = (hour t     * secsPerHour   % 1)
+    + (minute t   * secsPerMinute % 1)
+    + (second t                   % 1)
+    + (secondFraction t)
+  fromUnixSeconds s
+    = return $ Time
+               { tHour           = truncate s `div` secsPerHour   `mod` hoursPerDay
+               , tMinute         = truncate s `div` secsPerMinute `mod` minsPerHour
+               , tSecond         = truncate s                     `mod` secsPerMinute
+               , tSecondFraction = s - (truncate s % 1)
+               }
+
+instance IsTime Time where
+  hour
+    = tHour
+  minute
+    = tMinute
+  second
+    = tSecond
+  secondFraction
+    = tSecondFraction
+
+  setHour x t
+    | x < 0 || 23 < x = fail   $ "Time.setHour "           ++ show x
+    | otherwise       = return $ t { tHour           = x }
+  setMinute x t
+    | x < 0 || 59 < x = fail   $ "Time.setMinute "         ++ show x
+    | otherwise       = return $ t { tMinute         = x }
+  setSecond x t
+    | x < 0 || 59 < x = fail   $ "Time.setSecond "         ++ show x
+    | otherwise       = return $ t { tSecond         = x }
+  setSecondFraction x t
+    | x < 0 || 1 <= x = fail   $ "Time.setSecondFraction " ++ show x
+    | otherwise       = return $ t { tSecondFraction = x }
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Lars Petersen
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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/tests/Test.hs b/tests/Test.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test.hs
@@ -0,0 +1,211 @@
+module Main (main) where
+
+import Test.QuickCheck
+import Test.Framework (defaultMain, testGroup)
+import Test.Framework (Test)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Data.String
+
+import Data.UTC
+
+import Data.UTC.Internal.Test (test)
+import Data.UTC.Class.IsDate.Test (test)
+
+main :: IO ()
+main
+  = defaultMain
+     [ testGroup "instance IsUnixTime DateTime"
+     $ testUnixTimeInstance (undefined :: DateTime)
+     , testGroup "instance IsDate DateTime"
+     $ testDateInstance (undefined :: DateTime)
+     , testGroup "instance Time DateTime"
+     $ testTimeInstance (epoch     :: DateTime)
+     , testGroup "instance Time Time"
+     $ testTimeInstance (midnight  :: Time)
+
+     , Data.UTC.Internal.Test.test
+     , Data.UTC.Class.IsDate.Test.test
+
+     , testProperty "(parseRfc3339 \"2014-12-24T13:37:00Z\" :: Maybe (Local DateTime)) >>= addHours 25 >>= setMonth 1 >>= renderRfc3339"
+     $ ((parseRfc3339 "2014-12-24T13:37:00Z" :: Maybe (Local DateTime)) >>= addHours 25 >>= setMonth 1 >>= renderRfc3339)
+       === (Just "2014-01-25T14:37:00Z" :: Maybe String)
+     ]
+
+testTimeInstance :: (IsTime t, Eq t) => t -> [Test]
+testTimeInstance t
+  = [ testProperty ("test 1.t1")
+    $ (t1 >>= return . hour) == Just 12
+    , testProperty ("test 1.t2")
+    $ (t2 >>= return . hour) == Just 12
+    , testProperty ("test 2.t1")
+    $ (t2 >>= return . minute) == Just 13
+    , testProperty ("test 2.t2")
+    $ (t2 >>= return . minute) == Just 13
+    , testProperty ("test 3.t1")
+    $ (t2 >>= return . second) == Just 14
+    , testProperty ("test 3.t2")
+    $ (t2 >>= return . second) == Just 14
+    , testProperty ("test 4.t1")
+    $ (t2 >>= return . secondFraction) == Just 0.56789
+    , testProperty ("test 4.t2")
+    $ (t2 >>= return . secondFraction) == Just 0.56789
+    ]
+    ++ map (\(n,d)-> testProperty ("test 5." ++ n)
+                   $ d == (Nothing `asTypeOf` Just t)
+           ) invalidTimes
+    ++
+    -- Testing the add* functions of the Time class.
+    [ testGroup "addHours"
+      [ testProperty ("adding 1 hour should result in 01:00")
+      $ (addHours 1 t >>= return . hour) == Just 1
+      , testProperty ("adding 25 hours should result in 01:00")
+      $ (addHours 25 t >>= return . hour) == Just 1
+      , testProperty ("adding 49 hours should result in 01:00")
+      $ (addHours 49 t >>= return . hour) == Just 1
+      , testProperty ("adding -1 hours should result in 23:00")
+      $ (addHours (-1) t >>= return . hour) == Just 23
+      , testProperty ("adding twice in sequence")
+      $ (addHours 3 t >>= addHours 4 >>= return . hour) == Just 7
+      ]
+    , testGroup "addMinutes"
+      [ testProperty ("adding 1 minute should result in 00:01")
+      $ (addMinutes 1 t >>= return . hour) == Just 0 &&
+        (addMinutes 1 t >>= return . minute) == Just 1
+      , testProperty ("adding 60 minutes should result in 01:00")
+      $ (addMinutes 60 t >>= return . hour) == Just 1 &&
+        (addMinutes 60 t >>= return . minute) == Just 0 
+      , testProperty ("adding 62 minutes should result in 01:02")
+      $ (addMinutes 62 t >>= return . hour) == Just 1 &&
+        (addMinutes 62 t >>= return . minute) == Just 2
+      , testProperty ("adding -1 minute should result in 23:59")
+      $ (addMinutes (-1) t >>= return . hour) == Just 23 &&
+        (addMinutes (-1) t >>= return . minute) == Just 59
+      ]
+    , testGroup "addSeconds"
+      [ testProperty ("adding 1 second should result in 00:00:01")
+      $ (addSeconds 1 t >>= return . second) == Just 1
+      , testProperty ("adding 60 seconds should result in 00:01:00")
+      $ (addSeconds 60 t >>= return . minute) == Just 1 &&
+        (addSeconds 60 t >>= return . second) == Just 0
+      , testProperty ("adding 61 seconds should result in 00:01:02")
+      $ (addSeconds 62 t >>= return . minute) == Just 1 &&
+        (addSeconds 62 t >>= return . second) == Just 2
+      , testProperty ("adding -1 second should result in 23:59:59")
+      $ (addSeconds (-1) t >>= return . hour) == Just 23 &&
+        (addSeconds (-1) t >>= return . minute) == Just 59 &&
+        (addSeconds (-1) t >>= return . second) == Just 59
+      ]
+    , testGroup "addSecondFractions"
+      [ testProperty ("adding 0.1 seconds should add 0.1 seconds")
+      $ (addSecondFractions 0.1 t >>= return . secondFraction) == Just 0.1
+      , testProperty ("adding 1.2 seconds should add 1 second and 0.2 seconds")
+      $ (addSecondFractions 1.2 t >>= return . second) == Just 1 &&
+        (addSecondFractions 1.2 t >>= return . secondFraction) == Just 0.2
+      , testProperty ("adding -0.001 seconds should result in 23:59:59.999")
+      $ (addSecondFractions (-0.001) t >>= return . hour)           == Just 23 &&
+        (addSecondFractions (-0.001) t >>= return . minute)         == Just 59 &&
+        (addSecondFractions (-0.001) t >>= return . second)         == Just 59 &&
+        (addSecondFractions (-0.001) t >>= return . secondFraction) == Just 0.999
+      ]
+    ]
+  where
+    t1 = setHour 12 (t `asTypeOf` t) >>= setMinute 13 >>= setSecond 14 >>= setSecondFraction 0.56789
+    t2 = setSecondFraction 0.56789 (t `asTypeOf` t) >>= setSecond 14 >>= setMinute 13 >>= setHour 12
+    invalidTimes
+       = [ ("01", setHour (-1) t)
+         , ("02", setHour 24 t)
+         , ("03", setMinute (-1) t)
+         , ("04", setMinute 60 t)
+         , ("05", setSecond (-1) t)
+         , ("06", setSecond 60 t)
+         , ("07", setSecondFraction (-1.0) t)
+         , ("08", setSecondFraction (-0.1) t)
+         , ("09", setSecondFraction 1.0 t)
+         , ("10", setSecondFraction 1.1 t)
+         ]
+
+testUnixTimeInstance :: (Show t, IsUnixTime t, IsString t,Eq t) => t -> [Test]
+testUnixTimeInstance t
+  = (map
+       (\(i64,s)->
+        testProperty ("fromUnixSeconds " ++ show i64 ++ ") == Just " ++ show s)
+        $  (fromUnixSeconds i64) === Just (fromString s `asTypeOf` t)
+       )
+       unixEpochMsRfc3339TimeTuples
+      )
+      ++
+      (map
+       (\(i64,s)->
+        testProperty ("unixSeconds (" ++ show s ++ ") == " ++ show i64)
+        $ unixSeconds (fromString s `asTypeOf` t) === i64
+       )
+       unixEpochMsRfc3339TimeTuples
+      )
+
+testDateInstance :: (IsDate t, Eq t) => t -> [Test]
+testDateInstance t
+  = [ testProperty ("year dat1")
+      $ (dat1 >>= return . year)  == Just 1972
+    , testProperty ("month dat1")
+      $ (dat1 >>= return . month) == Just 7
+    , testProperty ("day dat1")
+      $ (dat1 >>= return . day)   == Just 23
+    ]
+    ++ map (\(n,d)-> testProperty n
+                   $ d == Nothing
+           ) invalidDates
+  where
+    dat1 = (setYear 1972 epoch >>= setMonth 7 >>= setDay 23) `asTypeOf` (Just t)
+    invalidDates
+      = [-- * invalid dates
+         -- ** month or day out of bound bound
+          ("inv001", (setYear 1973 epoch >>= setMonth 0               ) `asTypeOf` (Just t))
+        , ("inv002", (setYear 1973 epoch >>= setMonth 13              ) `asTypeOf` (Just t))
+        , ("inv003", (setYear 1973 epoch                 >>= setDay 00) `asTypeOf` (Just t))
+         -- ** day beyond upper bound
+        , ("inv004", (setYear 1973 epoch >>= setMonth 1  >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv005", (setYear 1973 epoch >>= setMonth 2  >>= setDay 29) `asTypeOf` (Just t))
+        , ("inv006", (setYear 1973 epoch >>= setMonth 3  >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv007", (setYear 1973 epoch >>= setMonth 4  >>= setDay 31) `asTypeOf` (Just t))
+        , ("inv008", (setYear 1973 epoch >>= setMonth 5  >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv009", (setYear 1973 epoch >>= setMonth 6  >>= setDay 31) `asTypeOf` (Just t))
+        , ("inv010", (setYear 1973 epoch >>= setMonth 7  >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv011", (setYear 1973 epoch >>= setMonth 8  >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv012", (setYear 1973 epoch >>= setMonth 9  >>= setDay 31) `asTypeOf` (Just t))
+        , ("inv013", (setYear 1973 epoch >>= setMonth 10 >>= setDay 32) `asTypeOf` (Just t))
+        , ("inv014", (setYear 1973 epoch >>= setMonth 11 >>= setDay 31) `asTypeOf` (Just t))
+        , ("inv015", (setYear 1973 epoch >>= setMonth 12 >>= setDay 32) `asTypeOf` (Just t))
+        ]
+
+unixEpochMsRfc3339TimeTuples :: [(Rational,String)]
+unixEpochMsRfc3339TimeTuples
+  = [ ( -62167219200.000, "0000-01-01T00:00:00Z")     -- verified against moment.js (lowest possible date)
+    , ( -62162208000.000, "0000-02-28T00:00:00Z")     -- -62162035200000 - (2*24*60*60*1000)
+    , ( -62162121600.000, "0000-02-29T00:00:00Z")     -- -62162035200000 - (24*60*60*1000)
+    , ( -62162035200.000, "0000-03-01T00:00:00Z")     -- verified against moment.js
+    , ( -62135596800.000, "0001-01-01T00:00:00Z")     -- verified against moment.js
+    , ( -12275625600.000, "1581-01-01T00:00:00Z")     -- verified against moment.js
+    , ( -12244089600.000, "1582-01-01T00:00:00Z")     -- verified against moment.js
+    , ( -12212553600.000, "1583-01-01T00:00:00Z")     -- verified against moment.js
+    , (  -2208988800.000, "1900-01-01T00:00:00Z")     -- verified against moment.js
+    , (  -2203891200.000, "1900-03-01T00:00:00Z")     -- verified against moment.js (1900 is not a leap year)
+    , (           -1.000, "1969-12-31T23:59:59Z")     -- verified against moment.js
+    , (            0.000, "1970-01-01T00:00:00Z")     -- verified against moment.js
+    , (    951696000.000, "2000-02-28T00:00:00Z")     -- verified against moment.js (2000 is a leap year)
+    , (   1234234234.000, "2009-02-10T02:50:34Z")     -- verified against moment.js
+    , (   1330473600.000, "2012-02-29T00:00:00Z")     -- verified against moment.js (2012 is a leap year)
+    , (   1330559999.999, "2012-02-29T23:59:59.999Z") -- verified against moment.js
+    , (   1330560000.000, "2012-03-01T00:00:00Z")     -- verified against moment.js
+    , (   1330560000.001, "2012-03-01T00:00:00.001Z") -- verified against moment.js
+    , (   1334491994.000, "2012-04-15T12:13:14Z")     -- verified against moment.js
+    , (   2177410394.000, "2038-12-31T12:13:14Z")     -- verified against moment.js
+    , (  32503680000.000, "3000-01-01T00:00:00Z")     -- verified against moment.js
+    , ( 190288396800.000, "8000-01-01T00:00:00Z")     -- verified against moment.js
+    , ( 221845392000.000, "9000-01-01T00:00:00Z")     -- verified against moment.js
+    , ( 240779520000.000, "9600-01-01T00:00:00Z")     -- verified against moment.js
+    , ( 253370764800.000, "9999-01-01T00:00:00Z")     -- verified against moment.js
+    , ( 253402214400.000, "9999-12-31T00:00:00Z")     -- verified against moment.js
+    , ( 253402300799.999, "9999-12-31T23:59:59.999Z") -- verified against moment.js (highest possible date)
+    ]
+
diff --git a/utc.cabal b/utc.cabal
new file mode 100644
--- /dev/null
+++ b/utc.cabal
@@ -0,0 +1,102 @@
+name:                utc
+version:             0.1.0.0
+stability:           experimental
+synopsis:            A pragmatic time and date library.
+description:         This library aims to supply you with common
+                     types and operations for working with time and date.
+                     .
+                       * Parsing and rendering common formats like ISO8601 and RFC3339.
+                     .
+                       * Modifying dates and times in a way that is easy to understand
+                         and not overengineered (srsly, who needs something else than the
+                         Gregorian Calendar?)
+                     .
+                       * A set of classes that provide interfaces for your own application
+                         specific or maybe more efficient time and date types. Implement the
+                         interfaces and get all the parsing and rendering functions for free!
+                     .
+                     This is a work in progress!
+                     .
+                     Try it out and play with it. Tell me what you think
+                     about the API design and naming, but __DON'T USE IT
+                     IN PRODUCTION YET!__
+                     .
+                     Bug reports or (even better) tests are welcome.
+license:             MIT
+license-file:        LICENSE
+author:              Lars Petersen
+maintainer:          lars@nyantec.com
+copyright:           Copyright (c) Lars Petersen 2014-2015
+homepage:            https://github.com/lpeterse/haskell-utc
+bug-reports:         https://github.com/lpeterse/haskell-utc/issues
+category:            Data, Time, Parsing
+build-type:          Simple
+cabal-version:       >=1.10
+
+Library
+  build-depends:     base < 5
+                   , text
+                   , bytestring >= 0.10.4.0
+                   , attoparsec
+                   , clock >= 0.3 && < 0.5
+  ghc-options:       -Wall
+  hs-source-dirs:    .
+  default-language:  Haskell98
+  exposed-modules:   Data.UTC
+                   , Data.UTC.Class
+                   , Data.UTC.Class.Epoch
+                   , Data.UTC.Class.Midnight
+                   , Data.UTC.Class.IsDate
+                   , Data.UTC.Class.IsTime
+                   , Data.UTC.Class.IsUnixTime
+                   , Data.UTC.Class.HasUnixTime
+                   , Data.UTC.Type
+                   , Data.UTC.Type.Date
+                   , Data.UTC.Type.Time
+                   , Data.UTC.Type.DateTime
+                   , Data.UTC.Type.Local
+                   , Data.UTC.Format.Rfc3339
+                   , Data.UTC.Internal
+  other-modules:     Data.UTC.Format.Rfc3339.Parser
+                   , Data.UTC.Format.Rfc3339.Builder
+
+Test-Suite test
+  type:              exitcode-stdio-1.0
+  main-is:           Test.hs
+  ghc-options:       -Wall -O2
+  hs-source-dirs:    tests .
+  other-modules:     Data.UTC
+                   , Data.UTC.Class
+                   , Data.UTC.Class.Epoch
+                   , Data.UTC.Class.Midnight
+                   , Data.UTC.Class.IsDate
+                   , Data.UTC.Class.IsDate.Test
+                   , Data.UTC.Class.IsTime
+                   , Data.UTC.Class.IsUnixTime
+                   , Data.UTC.Class.HasUnixTime
+                   , Data.UTC.Type
+                   , Data.UTC.Type.Date
+                   , Data.UTC.Type.Time
+                   , Data.UTC.Type.DateTime
+                   , Data.UTC.Type.Local
+                   , Data.UTC.Format.Rfc3339
+                   -- in library unexposed modules
+                   , Data.UTC.Format.Rfc3339.Parser
+                   , Data.UTC.Format.Rfc3339.Builder
+                   , Data.UTC.Internal
+                   , Data.UTC.Internal.Test
+  build-depends:     base < 5
+                   , text
+                   , bytestring >= 0.10.4.0
+                   , attoparsec
+                   , clock >= 0.3 && < 0.5
+                   -- additional deps for testing
+                   , Cabal
+                   , test-framework >= 0.8.0.2
+                   , test-framework-quickcheck2 >= 0.3.0.3
+                   , QuickCheck
+  default-language:  Haskell98
+
+source-repository head
+  type:     git
+  location: https://github.com/lpeterse/haskell-utc.git
