diff --git a/Text/ICalendar.hs b/Text/ICalendar.hs
--- a/Text/ICalendar.hs
+++ b/Text/ICalendar.hs
@@ -1,9 +1,9 @@
 module Text.ICalendar
-    ( module Text.ICalendar.Types
-    , module Text.ICalendar.Parser
+    ( module Text.ICalendar.Parser
     , module Text.ICalendar.Printer
+    , module Text.ICalendar.Types
     ) where
 
-import Text.ICalendar.Types
 import Text.ICalendar.Parser
 import Text.ICalendar.Printer
+import Text.ICalendar.Types
diff --git a/Text/ICalendar/Parser.hs b/Text/ICalendar/Parser.hs
--- a/Text/ICalendar/Parser.hs
+++ b/Text/ICalendar/Parser.hs
@@ -1,31 +1,31 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE OverloadedStrings    #-}
+{-# LANGUAGE ScopedTypeVariables  #-}
 {-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE FlexibleInstances #-}
 module Text.ICalendar.Parser
     ( parseICal
     , parseICalFile
     , DecodingFunctions(..)
     ) where
 
-import Prelude
-import Control.Applicative
-import Control.Monad
-import Control.Monad.Error
-import Control.Monad.RWS ( runRWS )
-import Data.ByteString.Lazy (ByteString)
+import           Control.Applicative
+import           Control.Monad
+import           Control.Monad.Error
+import           Control.Monad.RWS          (runRWS)
+import           Data.ByteString.Lazy       (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as B
-import Data.Monoid
+import           Data.Monoid
+import           Prelude
 
-import Text.Parsec.Prim hiding (many, (<|>))
-import Text.Parsec.Pos
 import Text.Parsec.ByteString.Lazy ()
-import Text.Parsec.Text.Lazy ()
+import Text.Parsec.Pos
+import Text.Parsec.Prim            hiding (many, (<|>))
+import Text.Parsec.Text.Lazy       ()
 
-import Text.ICalendar.Types
 import Text.ICalendar.Parser.Common
-import Text.ICalendar.Parser.Content
 import Text.ICalendar.Parser.Components
+import Text.ICalendar.Parser.Content
+import Text.ICalendar.Types
 
 
 -- | Parse a ByteString containing iCalendar data.
diff --git a/Text/ICalendar/Parser/Common.hs b/Text/ICalendar/Parser/Common.hs
--- a/Text/ICalendar/Parser/Common.hs
+++ b/Text/ICalendar/Parser/Common.hs
@@ -1,38 +1,39 @@
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 module Text.ICalendar.Parser.Common where
 
-import Prelude hiding (mapM)
-import Control.Applicative
-import Control.Arrow (second)
-import Control.Monad.Error hiding (mapM)
-import Control.Monad.RWS ( RWS, MonadWriter(tell) , MonadState(get, put), asks
-                         , modify )
-import Data.ByteString.Lazy.Char8 (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as B
+import           Control.Applicative
+import           Control.Arrow                (second)
+import           Control.Monad.Error          hiding (mapM)
+import           Control.Monad.RWS            (MonadState (get, put),
+                                               MonadWriter (tell), RWS, asks,
+                                               modify)
 import qualified Data.ByteString.Lazy.Builder as Bu
-import Data.CaseInsensitive (CI)
-import qualified Data.CaseInsensitive as CI
-import Data.Char
-import Data.Default
-import Data.List (partition)
-import Data.Maybe
-import Data.Monoid
-import Data.Set (Set)
-import qualified Data.Set as S
-import Data.Text.Lazy (Text)
-import qualified Data.Text.Lazy as T
-import qualified Data.Text.Lazy.Encoding as TE
-import Data.Time ( UTCTime(UTCTime), LocalTime(LocalTime), Day
-                 , TimeOfDay())
-import qualified Data.Time as Time
-import Data.Traversable (mapM)
-import qualified Network.URI as URI
-import qualified System.Locale as L
+import           Data.ByteString.Lazy.Char8   (ByteString)
+import qualified Data.ByteString.Lazy.Char8   as B
+import           Data.CaseInsensitive         (CI)
+import qualified Data.CaseInsensitive         as CI
+import           Data.Char
+import           Data.Default
+import           Data.List                    (partition)
+import           Data.Maybe
+import           Data.Monoid
+import           Data.Set                     (Set)
+import qualified Data.Set                     as S
+import           Data.Text.Lazy               (Text)
+import qualified Data.Text.Lazy               as T
+import qualified Data.Text.Lazy.Encoding      as TE
+import           Data.Time                    (Day, LocalTime (LocalTime),
+                                               TimeOfDay (), UTCTime (UTCTime))
+import qualified Data.Time                    as Time
+import           Data.Traversable             (mapM)
+import qualified Network.URI                  as URI
+import           Prelude                      hiding (mapM)
+import qualified System.Locale                as L
 
-import qualified Text.Parsec as P
-import Text.Parsec.Prim hiding ((<|>))
-import Text.Parsec.Combinator hiding (optional)
+import qualified Text.Parsec            as P
+import           Text.Parsec.Combinator hiding (optional)
+import           Text.Parsec.Prim       hiding ((<|>))
 
 import Text.ICalendar.Types
 
@@ -49,7 +50,7 @@
 
 -- | Functions for decoding 'ByteString's into 'Text'.
 data DecodingFunctions = DecodingFunctions
-    { dfBS2Text :: ByteString -> Text
+    { dfBS2Text  :: ByteString -> Text
     , dfBS2IText :: ByteString -> CI Text
     }
 
@@ -64,7 +65,7 @@
                         Left e -> throwError $ "parseText': " ++ show e
                         Right (x, r) -> return ( map (c . Bu.toLazyByteString) x
                                                , r)
-  where texts = sepBy text (P.char ',')
+  where texts = sepBy1 text (P.char ',') <|> return [mempty]
         text = do x <- P.satisfy isTSafe'
                   case x of
                        '\\' -> do y <- P.anyChar
@@ -75,9 +76,10 @@
                                        z | z `elem` "nN" -> nxt '\n'
                                        _ -> fail $ "unexpected " ++ show x
                        y -> nxt y
-        -- isTSafe + 0x22, 0x3A
+        -- isTSafe + 0x22, 0x3A, and 0x5C is pattern matched against.
         isTSafe' c = let n = ord c
-                      in n == 9 || (n >= 0x20 && n <= 0x3A)
+                      in n == 9 || (n >= 0x20 && n <= 0x2B)
+                                || (n >= 0x2D && n <= 0x3A)
                                 || (n >= 0x3C && n /= 0x7F)
         nxt c = (Bu.char8 c <>) <$> (text <|> return mempty)
 
diff --git a/Text/ICalendar/Parser/Components.hs b/Text/ICalendar/Parser/Components.hs
--- a/Text/ICalendar/Parser/Components.hs
+++ b/Text/ICalendar/Parser/Components.hs
@@ -1,19 +1,22 @@
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE RecordWildCards   #-}
 module Text.ICalendar.Parser.Components where
 
-import Control.Applicative
-import Control.Monad.Error hiding (mapM)
-import Control.Monad.RWS ( MonadState(get), tell )
+import           Control.Applicative
+import           Control.Arrow        ((&&&))
+import           Control.Monad.Error  hiding (mapM)
+import           Control.Monad.RWS    (MonadState (get), tell)
 import qualified Data.CaseInsensitive as CI
-import Data.List (partition)
-import Data.Maybe
-import Data.Set (Set)
-import qualified Data.Set as S
+import qualified Data.Foldable        as F
+import           Data.List            (partition)
+import qualified Data.Map             as M
+import           Data.Maybe
+import           Data.Set             (Set)
+import qualified Data.Set             as S
 
-import Text.ICalendar.Types
 import Text.ICalendar.Parser.Common
 import Text.ICalendar.Parser.Properties
+import Text.ICalendar.Types
 
 -- | Parse a VCALENDAR component. 3.4
 parseVCalendar :: Content -> ContentParser VCalendar
@@ -22,14 +25,27 @@
     vcVersion <- reqLine1 "VERSION" parseVersion
     vcScale <- optLine1 "CALSCALE" (parseSimpleI Scale)
     vcMethod <- optLine1 "METHOD" (parseSimpleI ((Just .) . Method))
-    vcTimeZones <- optCompN "VTIMEZONE" parseVTimeZone
-    vcEvents <- optCompN "VEVENT" (parseVEvent vcMethod)
-    vcTodos <- optCompN "VTODO" parseVTodo
-    vcJournals <- optCompN "VJOURNAL" parseVJournal
-    vcFreeBusys <- optCompN "VFREEBUSY" parseVFreeBusy
+    vcTimeZones <- f (tzidValue . vtzId) =<< optCompN "VTIMEZONE" parseVTimeZone
+    vcEvents <- f (uidValue . veUID &&& recur . veRecurId)
+                    =<< optCompN "VEVENT" (parseVEvent vcMethod)
+    vcTodos <- f (uidValue . vtUID &&& recur . vtRecurId)
+                    =<< optCompN "VTODO" parseVTodo
+    vcJournals <- f (uidValue . vjUID &&& recur . vjRecurId)
+                    =<< optCompN "VJOURNAL" parseVJournal
+    vcFreeBusys <- f (uidValue . vfbUID) =<< optCompN "VFREEBUSY" parseVFreeBusy
     vcOtherComps <- otherComponents
     vcOther <- otherProperties
     return VCalendar {..}
+  where recur :: Maybe RecurrenceId -> Maybe (Either Date DateTime)
+        recur Nothing = Nothing
+        recur (Just (RecurrenceIdDate x _ _)) = Just (Left x)
+        recur (Just (RecurrenceIdDateTime x _ _)) = Just (Right x)
+        f :: Ord b => (a -> b) -> Set a -> ContentParser (M.Map b a)
+        f g = F.foldlM h M.empty
+          where h m e = let k = g e
+                         in if k `M.member` m
+                               then throwError "Duplicate UID/RecurId/TZID."
+                               else return $ M.insert k e m
 parseVCalendar _ = throwError "parseVCalendar: Content given not a VCALENDAR\
                               \ component."
 
diff --git a/Text/ICalendar/Parser/Content.hs b/Text/ICalendar/Parser/Content.hs
--- a/Text/ICalendar/Parser/Content.hs
+++ b/Text/ICalendar/Parser/Content.hs
@@ -1,20 +1,20 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Text.ICalendar.Parser.Content where
 
-import Control.Applicative
-import Control.Monad
-import Data.ByteString.Lazy (ByteString)
+import           Control.Applicative
+import           Control.Monad
+import           Data.ByteString.Lazy         (ByteString)
 import qualified Data.ByteString.Lazy.Builder as Bu
-import Data.CaseInsensitive (CI)
-import Data.Char
-import Data.Monoid
-import Data.Text.Lazy (Text)
+import           Data.CaseInsensitive         (CI)
+import           Data.Char
+import           Data.Monoid
+import           Data.Text.Lazy               (Text)
 
-import qualified Text.Parsec as P
-import Text.Parsec.Prim hiding (many, (<|>))
-import Text.Parsec.Combinator hiding (optional)
-import Text.Parsec.ByteString.Lazy ()
-import Text.Parsec.Text.Lazy ()
+import qualified Text.Parsec                 as P
+import           Text.Parsec.ByteString.Lazy ()
+import           Text.Parsec.Combinator      hiding (optional)
+import           Text.Parsec.Prim            hiding (many, (<|>))
+import           Text.Parsec.Text.Lazy       ()
 
 import Text.ICalendar.Parser.Common
 
diff --git a/Text/ICalendar/Parser/Parameters.hs b/Text/ICalendar/Parser/Parameters.hs
--- a/Text/ICalendar/Parser/Parameters.hs
+++ b/Text/ICalendar/Parser/Parameters.hs
@@ -1,27 +1,27 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Text.ICalendar.Parser.Parameters where
 
-import Control.Applicative
-import Control.Monad.Error
-import Control.Monad.RWS ( MonadWriter(tell) )
-import Data.ByteString.Lazy.Char8 (ByteString)
+import           Control.Applicative
+import           Control.Monad.Error
+import           Control.Monad.RWS          (MonadWriter (tell))
+import           Data.ByteString.Lazy.Char8 (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as B
-import Data.CaseInsensitive (CI)
-import Data.Char
-import Data.Default
-import Data.Maybe
-import Data.Text.Lazy (Text)
-import qualified Data.Text.Lazy as T
+import           Data.CaseInsensitive       (CI)
+import           Data.Char
+import           Data.Default
+import           Data.Maybe
+import           Data.Text.Lazy             (Text)
+import qualified Data.Text.Lazy             as T
 
-import Codec.MIME.Parse (parseMIMEType)
-import Codec.MIME.Type (mimeType, MIMEType)
-import qualified Text.Parsec as P
-import Text.Parsec.Prim hiding ((<|>))
-import Text.Parsec.Perm
-import Text.Parsec.Combinator hiding (optional)
+import           Codec.MIME.Parse       (parseMIMEType)
+import           Codec.MIME.Type        (MIMEType, mimeType)
+import qualified Text.Parsec            as P
+import           Text.Parsec.Combinator hiding (optional)
+import           Text.Parsec.Perm
+import           Text.Parsec.Prim       hiding ((<|>))
 
-import Text.ICalendar.Types
 import Text.ICalendar.Parser.Common
+import Text.ICalendar.Types
 
 parseAlarmTriggerRelationship :: CI Text
                               -> ContentParser AlarmTriggerRelationship
diff --git a/Text/ICalendar/Parser/Properties.hs b/Text/ICalendar/Parser/Properties.hs
--- a/Text/ICalendar/Parser/Properties.hs
+++ b/Text/ICalendar/Parser/Properties.hs
@@ -1,31 +1,31 @@
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TupleSections     #-}
 module Text.ICalendar.Parser.Properties where
 
-import Prelude hiding (mapM)
-import Control.Applicative
-import Control.Monad.Error hiding (mapM)
-import Control.Monad.RWS ( asks )
-import qualified Data.ByteString.Base64.Lazy as B64
-import qualified Data.ByteString.Lazy.Char8 as B
-import Data.CaseInsensitive (CI)
-import qualified Data.CaseInsensitive as CI
-import Data.Char
-import Data.Default
-import Data.Maybe
-import qualified Data.Set as S
-import Data.Text.Lazy (Text)
-import qualified Data.Text.Lazy as T
-import Data.Traversable (mapM)
-import qualified Data.Version as Ver
-import Text.ParserCombinators.ReadP (readP_to_S)
+import           Control.Applicative
+import           Control.Monad.Error          hiding (mapM)
+import           Control.Monad.RWS            (asks)
+import qualified Data.ByteString.Base64.Lazy  as B64
+import qualified Data.ByteString.Lazy.Char8   as B
+import           Data.CaseInsensitive         (CI)
+import qualified Data.CaseInsensitive         as CI
+import           Data.Char
+import           Data.Default
+import           Data.Maybe
+import qualified Data.Set                     as S
+import           Data.Text.Lazy               (Text)
+import qualified Data.Text.Lazy               as T
+import           Data.Traversable             (mapM)
+import qualified Data.Version                 as Ver
+import           Prelude                      hiding (mapM)
+import           Text.ParserCombinators.ReadP (readP_to_S)
 
 import Text.Parsec.Prim hiding ((<|>))
 
-import Text.ICalendar.Types
 import Text.ICalendar.Parser.Common
 import Text.ICalendar.Parser.Parameters
+import Text.ICalendar.Types
 
 parseFreeBusy :: Content -> ContentParser FreeBusy
 parseFreeBusy (ContentLine _ "FREEBUSY" o bs) = do
@@ -198,8 +198,7 @@
 
 parseRecurId :: Maybe DTStart -> Content -> ContentParser RecurrenceId
 parseRecurId dts (ContentLine p "RECURRENCE-ID" o bs) = do
-    range' <- maybe (return def) (parseRange . CI.mk <=< paramOnlyOne) $
-                                   lookup "RANGE" o
+    range' <- mapM (parseRange . CI.mk <=< paramOnlyOne) $ lookup "RANGE" o
     recurid <- parseSimpleDateOrDateTime
             (($ range') . RecurrenceIdDateTime)
             (($ range') . RecurrenceIdDate)
diff --git a/Text/ICalendar/Printer.hs b/Text/ICalendar/Printer.hs
--- a/Text/ICalendar/Printer.hs
+++ b/Text/ICalendar/Printer.hs
@@ -1,39 +1,40 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TupleSections     #-}
 module Text.ICalendar.Printer
     ( EncodingFunctions(..)
     , printICal
     ) where
 
-import Prelude hiding (mapM_)
-import Control.Applicative
-import Control.Arrow ((&&&))
-import Control.Monad hiding (mapM_, forM_)
-import Control.Monad.RWS ( RWS, runRWS, MonadWriter(tell)
-                         , MonadState(get, put), asks, modify)
-import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as BS
-import Data.ByteString.Lazy.Builder (Builder)
+import           Control.Applicative
+import           Control.Arrow                ((&&&))
+import           Control.Monad                hiding (forM_, mapM_)
+import           Control.Monad.RWS            (MonadState (get, put),
+                                               MonadWriter (tell), RWS, asks,
+                                               modify, runRWS)
+import           Data.ByteString.Lazy         (ByteString)
+import           Data.ByteString.Lazy.Builder (Builder)
 import qualified Data.ByteString.Lazy.Builder as Bu
-import qualified Data.CaseInsensitive as CI
-import Data.Char (ord, toUpper)
-import Data.Default
-import Data.Foldable (mapM_, forM_)
-import Data.Monoid
-import Data.Set (Set)
-import qualified Data.Set as S
-import Data.Text.Lazy (Text)
-import qualified Data.Text.Lazy as T
-import Data.Time (FormatTime())
-import qualified Data.Time as Time
-import qualified Data.Version as Ver
-import qualified Network.URI as URI
-import qualified System.Locale as L
-import Text.Printf (printf)
+import qualified Data.ByteString.Lazy.Char8   as BS
+import qualified Data.CaseInsensitive         as CI
+import           Data.Char                    (ord, toUpper)
+import           Data.Default
+import           Data.Foldable                (forM_, mapM_)
+import           Data.Monoid
+import           Data.Set                     (Set)
+import qualified Data.Set                     as S
+import           Data.Text.Lazy               (Text)
+import qualified Data.Text.Lazy               as T
+import           Data.Time                    (FormatTime ())
+import qualified Data.Time                    as Time
+import qualified Data.Version                 as Ver
+import qualified Network.URI                  as URI
+import           Prelude                      hiding (mapM_)
+import qualified System.Locale                as L
+import           Text.Printf                  (printf)
 
-import Codec.MIME.Type (showMIMEType, MIMEType)
+import           Codec.MIME.Type             (MIMEType, showMIMEType)
 import qualified Data.ByteString.Base64.Lazy as B64
 
 import Text.ICalendar.Types
@@ -352,7 +353,7 @@
                                                      printValue classValue
 
 instance IsProperty Created where
-    printProperty Created {..} = ln $ do 
+    printProperty Created {..} = ln $ do
         prop "CREATED" $ toParam createdOther <> toParam createdValue
         printValue createdValue
 
@@ -513,7 +514,7 @@
      -> a
      -> ContentPrinter ()
 prop b x = do
-    put (fromIntegral $ BS.length b) -- 
+    put (fromIntegral $ BS.length b)
     tell (Bu.lazyByteString b)
     mapM_ param $ toParam x
     out ":"
@@ -605,10 +606,16 @@
 
 instance ToParam RecurrenceId where
     toParam RecurrenceIdDate {..} = [("VALUE", [(NoQuotes, "DATE")])] <>
+                                    toParam recurrenceIdRange <>
                                     toParam recurrenceIdOther
     toParam RecurrenceIdDateTime {..} = toParam recurrenceIdDateTime <>
+                                        toParam recurrenceIdRange <>
                                         toParam recurrenceIdOther
 
+instance ToParam Range where
+    toParam ThisAndFuture = [("RANGE", [(NoQuotes, "THISANDFUTURE")])]
+    toParam _ = [] -- ThisAndPrior MUST NOT be generated.
+
 instance ToParam FBType where
     toParam x | x == def    = []
     toParam Free            = [("FBTYPE", [(NoQuotes, "FREE")])]
@@ -659,7 +666,7 @@
     toParam Delegated           = [("PARTSTAT", [(NoQuotes, "DELEGATED")])]
     toParam PartStatCompleted   = [("PARTSTAT", [(NoQuotes, "COMPLETED")])]
     toParam InProcess           = [("PARTSTAT", [(NoQuotes, "IN-PROCESS")])]
-    toParam (PartStatX x)       = [("PARTSTAT", [(NoQuotes, CI.original x)])]
+    toParam (PartStatX x)       = [("PARTSTAT", [(Optional, CI.original x)])]
 
 instance ToParam RelationshipType where
     toParam x | x == def          = []
diff --git a/Text/ICalendar/Types.hs b/Text/ICalendar/Types.hs
--- a/Text/ICalendar/Types.hs
+++ b/Text/ICalendar/Types.hs
@@ -1,21 +1,26 @@
 {-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings  #-}
 -- | ICalendar types, based on RFC5545.
 module Text.ICalendar.Types
     ( module Text.ICalendar.Types
     ) where
 
-import Codec.MIME.Type (MIMEType)
-import Data.ByteString.Lazy.Char8 (ByteString)
-import Data.CaseInsensitive (CI)
-import Data.Default
-import Data.Set (Set)
-import Data.Text.Lazy (Text)
-import Data.Time
-import Data.Typeable (Typeable)
-import Data.Version (Version(..))
-import Network.URI (URI)
+import           Codec.MIME.Type            (MIMEType)
+import           Data.ByteString.Lazy.Char8 (ByteString)
+import           Data.CaseInsensitive       (CI)
+import           Data.Default
+import           Data.Map                   (Map)
+import qualified Data.Map                   as M
+import           Data.Monoid
+import           Data.Set                   (Set)
+import           Data.Text.Lazy             (Text, pack)
+import           Data.Time
+import           Data.Typeable              (Typeable)
+import           Data.Version               (Version (..), showVersion)
+import           Network.URI                (URI)
 
+import Paths_iCalendar (version)
+
 -- | Language.
 newtype Language = Language (CI Text) -- TODO: RFC5646 types and parser.
                    deriving (Eq, Show, Ord, Typeable)
@@ -40,19 +45,61 @@
     , vcScale      :: Scale
     , vcMethod     :: Maybe Method
     , vcOther      :: Set OtherProperty
-    , vcTimeZones  :: Set VTimeZone
-    , vcEvents     :: Set VEvent
-    , vcTodos      :: Set VTodo
-    , vcJournals   :: Set VJournal
-    , vcFreeBusys  :: Set VFreeBusy
+    , vcTimeZones  :: Map Text VTimeZone
+    -- ^ Map TZID-value VTimeZone
+    , vcEvents     :: Map (Text, Maybe (Either Date DateTime)) VEvent
+    -- ^ Map (UID-value, Maybe RecurrenceID-value) VEvent
+    , vcTodos      :: Map (Text, Maybe (Either Date DateTime)) VTodo
+    -- ^ Map (UID-value, Maybe RecurrenceID-value) VTodo
+    , vcJournals   :: Map (Text, Maybe (Either Date DateTime)) VJournal
+    -- ^ Map (UID-value, Maybe RecurrenceID-value) VJournal
+    , vcFreeBusys  :: Map Text VFreeBusy
+    -- ^ Map UID-value VFreeBusy
     , vcOtherComps :: Set VOther
     } deriving (Show, Eq, Ord, Typeable)
 
 instance Default VCalendar where
-    def = VCalendar (ProdId "-//haskell.org/NONSGML iCalendar-0.1//EN" def)
+    def = VCalendar (ProdId ("-//haskell.org/NONSGML iCalendar-" <>
+                             pack (showVersion version) <> "//EN") def)
                     (MaxICalVersion (Version [2,0] []) def)
                     def Nothing def def def def def def def
 
+-- | 'vcMethod' is ignored at the moment.
+--
+-- Picks the left in most cases.
+--
+-- On UID/RecurrenceId/TZID clash, picks the 'VEvent's, 'VTodo's and
+-- 'VJournal's with the highest ('Sequence', 'DTStamp'), the 'VTimeZone's
+-- with the highest 'LastModified', and 'VFreeBusy' with the highest 'DTStamp'.
+--
+-- If the Sequence, DTStamp or LastModified is the same, picks the left.
+instance Monoid VCalendar where
+    mempty = def
+    mappend a b = VCalendar { vcProdId     = vcProdId a
+                            , vcVersion    = vcVersion a
+                            , vcScale      = vcScale a
+                            , vcMethod     = vcMethod a
+                            , vcOther      = vcOther a <> vcOther b
+                            , vcTimeZones  = merge tz (vcTimeZones a)
+                                                      (vcTimeZones b)
+                            , vcEvents     = merge ev (vcEvents a) (vcEvents b)
+                            , vcTodos      = merge td (vcTodos a) (vcTodos b)
+                            , vcJournals   = merge jo (vcJournals a)
+                                                      (vcJournals b)
+                            , vcFreeBusys  = merge fb (vcFreeBusys a)
+                                                      (vcFreeBusys b)
+                            , vcOtherComps = vcOtherComps a <> vcOtherComps b
+                            }
+      where merge f = M.mergeWithKey (((Just .) .) . const f) id id
+            tz c d = if vtzLastMod c >= vtzLastMod d then c else d
+            ev c d = if (veSeq c, veDTStamp c) >= (veSeq d, veDTStamp d)
+                        then c else d
+            td c d = if (vtSeq c, vtDTStamp c) >= (vtSeq d, vtDTStamp d)
+                        then c else d
+            jo c d = if (vjSeq c, vjDTStamp c) >= (vjSeq d, vjDTStamp d)
+                        then c else d
+            fb c d = if vfbDTStamp c >= vfbDTStamp d then c else d
+
 -- | Product Identifier. 3.7.3.
 data ProdId = ProdId
     { prodIdValue :: Text
@@ -123,92 +170,92 @@
 
 -- | To-Do Component. 3.6.2
 data VTodo = VTodo
-    { vtDTStamp       :: DTStamp
-    , vtUID           :: UID
-    , vtClass         :: Class -- ^ 'def' = 'Public'
-    , vtCompleted     :: Maybe Completed
-    , vtCreated       :: Maybe Created
-    , vtDescription   :: Maybe Description
-    , vtDTStart       :: Maybe DTStart
-    , vtGeo           :: Maybe Geo
-    , vtLastMod       :: Maybe LastModified
-    , vtLocation      :: Maybe Location
-    , vtOrganizer     :: Maybe Organizer
-    , vtPercent       :: Maybe PercentComplete
-    , vtPriority      :: Priority -- ^ 'def' = 0
-    , vtRecurId       :: Maybe RecurrenceId
-    , vtSeq           :: Sequence -- ^ 'def' = 0
-    , vtStatus        :: Maybe TodoStatus
-    , vtSummary       :: Maybe Summary
-    , vtUrl           :: Maybe URL
-    , vtRRule         :: Set RRule
-    , vtDueDuration   :: Maybe (Either Due DurationProp)
-    , vtAttach        :: Set Attachment
-    , vtAttendee      :: Set Attendee
-    , vtCategories    :: Set Categories
-    , vtComment       :: Set Comment
-    , vtContact       :: Set Contact
-    , vtExDate        :: Set ExDate
-    , vtRStatus       :: Set RequestStatus
-    , vtRelated       :: Set RelatedTo
-    , vtResources     :: Set Resources
-    , vtRDate         :: Set RDate
-    , vtAlarms        :: Set VAlarm
-    , vtOther         :: Set OtherProperty
+    { vtDTStamp     :: DTStamp
+    , vtUID         :: UID
+    , vtClass       :: Class -- ^ 'def' = 'Public'
+    , vtCompleted   :: Maybe Completed
+    , vtCreated     :: Maybe Created
+    , vtDescription :: Maybe Description
+    , vtDTStart     :: Maybe DTStart
+    , vtGeo         :: Maybe Geo
+    , vtLastMod     :: Maybe LastModified
+    , vtLocation    :: Maybe Location
+    , vtOrganizer   :: Maybe Organizer
+    , vtPercent     :: Maybe PercentComplete
+    , vtPriority    :: Priority -- ^ 'def' = 0
+    , vtRecurId     :: Maybe RecurrenceId
+    , vtSeq         :: Sequence -- ^ 'def' = 0
+    , vtStatus      :: Maybe TodoStatus
+    , vtSummary     :: Maybe Summary
+    , vtUrl         :: Maybe URL
+    , vtRRule       :: Set RRule
+    , vtDueDuration :: Maybe (Either Due DurationProp)
+    , vtAttach      :: Set Attachment
+    , vtAttendee    :: Set Attendee
+    , vtCategories  :: Set Categories
+    , vtComment     :: Set Comment
+    , vtContact     :: Set Contact
+    , vtExDate      :: Set ExDate
+    , vtRStatus     :: Set RequestStatus
+    , vtRelated     :: Set RelatedTo
+    , vtResources   :: Set Resources
+    , vtRDate       :: Set RDate
+    , vtAlarms      :: Set VAlarm
+    , vtOther       :: Set OtherProperty
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Journal Component. 3.6.3
 data VJournal = VJournal
-    { vjDTStamp       :: DTStamp
-    , vjUID           :: UID
-    , vjClass         :: Class -- ^ 'def' = 'Public'
-    , vjCreated       :: Maybe Created
-    , vjDTStart       :: Maybe DTStart
-    , vjLastMod       :: Maybe LastModified
-    , vjOrganizer     :: Maybe Organizer
-    , vjRecurId       :: Maybe RecurrenceId
-    , vjSeq           :: Sequence -- ^ 'def' = 0
-    , vjStatus        :: Maybe JournalStatus
-    , vjSummary       :: Maybe Summary
-    , vjUrl           :: Maybe URL
-    , vjRRule         :: Set RRule
-    , vjAttach        :: Set Attachment
-    , vjAttendee      :: Set Attendee
-    , vjCategories    :: Set Categories
-    , vjComment       :: Set Comment
-    , vjContact       :: Set Contact
-    , vjDescription   :: Set Description
-    , vjExDate        :: Set ExDate
-    , vjRelated       :: Set RelatedTo
-    , vjRDate         :: Set RDate
-    , vjRStatus       :: Set RequestStatus
-    , vjOther         :: Set OtherProperty
+    { vjDTStamp     :: DTStamp
+    , vjUID         :: UID
+    , vjClass       :: Class -- ^ 'def' = 'Public'
+    , vjCreated     :: Maybe Created
+    , vjDTStart     :: Maybe DTStart
+    , vjLastMod     :: Maybe LastModified
+    , vjOrganizer   :: Maybe Organizer
+    , vjRecurId     :: Maybe RecurrenceId
+    , vjSeq         :: Sequence -- ^ 'def' = 0
+    , vjStatus      :: Maybe JournalStatus
+    , vjSummary     :: Maybe Summary
+    , vjUrl         :: Maybe URL
+    , vjRRule       :: Set RRule
+    , vjAttach      :: Set Attachment
+    , vjAttendee    :: Set Attendee
+    , vjCategories  :: Set Categories
+    , vjComment     :: Set Comment
+    , vjContact     :: Set Contact
+    , vjDescription :: Set Description
+    , vjExDate      :: Set ExDate
+    , vjRelated     :: Set RelatedTo
+    , vjRDate       :: Set RDate
+    , vjRStatus     :: Set RequestStatus
+    , vjOther       :: Set OtherProperty
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Free/Busy Component. 3.6.4
 data VFreeBusy = VFreeBusy
-    { vfbDTStamp       :: DTStamp
-    , vfbUID           :: UID
-    , vfbContact       :: Maybe Contact
-    , vfbDTStart       :: Maybe DTStart
-    , vfbDTEnd         :: Maybe DTEnd
-    , vfbOrganizer     :: Maybe Organizer
-    , vfbUrl           :: Maybe URL
-    , vfbAttendee      :: Set Attendee
-    , vfbComment       :: Set Comment
-    , vfbFreeBusy      :: Set FreeBusy
-    , vfbRStatus       :: Set RequestStatus
-    , vfbOther         :: Set OtherProperty
+    { vfbDTStamp   :: DTStamp
+    , vfbUID       :: UID
+    , vfbContact   :: Maybe Contact
+    , vfbDTStart   :: Maybe DTStart
+    , vfbDTEnd     :: Maybe DTEnd
+    , vfbOrganizer :: Maybe Organizer
+    , vfbUrl       :: Maybe URL
+    , vfbAttendee  :: Set Attendee
+    , vfbComment   :: Set Comment
+    , vfbFreeBusy  :: Set FreeBusy
+    , vfbRStatus   :: Set RequestStatus
+    , vfbOther     :: Set OtherProperty
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Time Zone Component. 3.6.5.
 data VTimeZone = VTimeZone
-    { vtzId            :: TZID
-    , vtzLastMod       :: Maybe LastModified
-    , vtzUrl           :: Maybe TZUrl
-    , vtzStandardC     :: Set TZProp
-    , vtzDaylightC     :: Set TZProp
-    , vtzOther         :: Set OtherProperty
+    { vtzId        :: TZID
+    , vtzLastMod   :: Maybe LastModified
+    , vtzUrl       :: Maybe TZUrl
+    , vtzStandardC :: Set TZProp
+    , vtzDaylightC :: Set TZProp
+    , vtzOther     :: Set OtherProperty
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Time zone property, also 3.6.5.
@@ -261,7 +308,7 @@
 
 -- | Any other component not recognized.
 data VOther = VOther
-    { voName :: CI Text
+    { voName  :: CI Text
     , voProps :: Set OtherProperty
     } deriving (Show, Eq, Ord, Typeable)
 
@@ -407,7 +454,7 @@
     { dateTimeFloating :: LocalTime
     }
     | UTCDateTime
-    { dateTimeUTC      :: UTCTime
+    { dateTimeUTC :: UTCTime
     }
     | ZonedDateTime
     { dateTimeFloating :: LocalTime
@@ -421,8 +468,8 @@
     , dtEndOther         :: OtherParams
     }
     | DTEndDate
-    { dtEndDateValue     :: Date
-    , dtEndOther         :: OtherParams
+    { dtEndDateValue :: Date
+    , dtEndOther     :: OtherParams
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Date-Time Due. 3.8.2.3.
@@ -432,8 +479,8 @@
     , dueOther         :: OtherParams
     }
     | DueDate
-    { dueDateValue     :: Date
-    , dueOther         :: OtherParams
+    { dueDateValue :: Date
+    , dueOther     :: OtherParams
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Date-Time Start. 3.8.2.4.
@@ -443,8 +490,8 @@
     , dtStartOther         :: OtherParams
     }
     | DTStartDate
-    { dtStartDateValue     :: Date
-    , dtStartOther         :: OtherParams
+    { dtStartDateValue :: Date
+    , dtStartOther     :: OtherParams
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Duration value. 3.3.6.
@@ -463,8 +510,8 @@
     , durSecond :: Int
     }
     | DurationWeek
-    { durSign   :: Sign
-    , durWeek   :: Int
+    { durSign :: Sign
+    , durWeek :: Int
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Sign.
@@ -626,21 +673,18 @@
 data RecurrenceId
     = RecurrenceIdDate
     { recurrenceIdDate  :: Date
-    , recurrenceIdRange :: Range -- ^ 'def' = 'ThisAndFuture'
+    , recurrenceIdRange :: Maybe Range
     , recurrenceIdOther :: OtherParams
     }
     | RecurrenceIdDateTime
     { recurrenceIdDateTime :: DateTime
-    , recurrenceIdRange    :: Range -- ^ 'def' = 'ThisAndFuture'
+    , recurrenceIdRange    :: Maybe Range
     , recurrenceIdOther    :: OtherParams
     } deriving (Show, Eq, Ord, Typeable)
 
 -- | Recurrence Identifier Range. 3.2.13
 data Range = ThisAndFuture | ThisAndPrior
              deriving (Show, Eq, Ord, Typeable)
-
-instance Default Range where
-    def = ThisAndFuture
 
 -- | Related To. 3.8.4.5.
 data RelatedTo = RelatedTo
diff --git a/iCalendar.cabal b/iCalendar.cabal
--- a/iCalendar.cabal
+++ b/iCalendar.cabal
@@ -1,5 +1,5 @@
 name:                iCalendar
-version:             0.1
+version:             0.2
 synopsis:            iCalendar data types, parser, and printer.
 description:         Data definitions, parsing and printing of the iCalendar
                      format (RFC5545).
@@ -30,9 +30,10 @@
                      , Text.ICalendar.Parser.Content
                      , Text.ICalendar.Parser.Parameters
                      , Text.ICalendar.Parser.Properties
+                     , Paths_iCalendar
   build-depends:       base ==4.*, time ==1.4.*, data-default >=0.3
                      , case-insensitive ==0.4.*, network ==2.4.*
                      , bytestring >=0.10 && < 0.11, parsec ==3.1.*
-                     , text, containers >= 0.4 && < 0.6, mime ==0.3.*
+                     , text, containers >= 0.5 && < 0.6, mime ==0.3.*
                      , mtl ==2.1.*, old-locale, base64-bytestring ==1.0.*
   ghc-options:       -Wall
