diff --git a/Data/Time/LocalTime/TimeZone/Olson/Parse.hs b/Data/Time/LocalTime/TimeZone/Olson/Parse.hs
--- a/Data/Time/LocalTime/TimeZone/Olson/Parse.hs
+++ b/Data/Time/LocalTime/TimeZone/Olson/Parse.hs
@@ -39,9 +39,12 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import Data.Monoid (mappend)
+import Data.List (sortBy, groupBy)
 import Data.Maybe (listToMaybe, fromMaybe)
 import Data.Word (Word8)
 import Data.Int (Int32, Int64)
+import Data.Ord (comparing)
+import Data.Function (on)
 import Data.Typeable (Typeable)
 import Control.Monad (guard, replicateM, replicateM_, when)
 import Control.Exception.Extensible (try, throw, Exception, ErrorCall)
@@ -57,8 +60,8 @@
 -- | Convert parsed Olson timezone data into a @TimeZoneSeries@.
 olsonToTimeZoneSeries :: OlsonData -> Maybe TimeZoneSeries
 olsonToTimeZoneSeries (OlsonData ttimes ttinfos@(dflt0:_) _ _) =
-    fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) $
-    reverse ttimes
+    fmap (TimeZoneSeries $ mkTZ dflt) . mapM (lookupTZ ttinfos) .
+    uniqTimes . sortBy futureToPast $ ttimes
   where
     dflt = fromMaybe dflt0 . listToMaybe $ filter isStd ttinfos
     isStd (TtInfo _ isdst _ _) = not isdst
@@ -67,6 +70,8 @@
     lookupTZ ttinfos ttime = fmap (((,) $ toUTC ttime) . mkTZ) . listToMaybe $
                              drop (transIndex ttime) ttinfos
     toUTC = posixSecondsToUTCTime . fromIntegral . transTime
+    uniqTimes = map last . groupBy ((==) `on` transTime)
+    futureToPast = comparing $ negate . transTime
 olsonToTimeZoneSeries _ = Nothing
 
 -- | Read timezone data from a binary Olson timezone file and convert
diff --git a/Data/Time/LocalTime/TimeZone/Olson/Render.hs b/Data/Time/LocalTime/TimeZone/Olson/Render.hs
--- a/Data/Time/LocalTime/TimeZone/Olson/Render.hs
+++ b/Data/Time/LocalTime/TimeZone/Olson/Render.hs
@@ -30,7 +30,7 @@
  renderOlsonToFile,
  verifyOlsonLimits,
  putOlson,
- splitOlson
+ extractOlsonV1
 )
 where
 
@@ -69,9 +69,9 @@
  | otherwise = Just $
     OlsonData
       [Transition secs ttinfo |
-         (t, tzs) <- reverse pairs,
+         (t, tz) <- reverse pairs,
          let secs = round $ utcTimeToPOSIXSeconds t,
-         ttinfo <- maybeToList $ lookup (mkTT tzs) ttAssocs]
+         ttinfo <- maybeToList $ lookup (mkTT tz) ttAssocs]
       ttinfos
       []
       Nothing
@@ -110,66 +110,52 @@
 
 -- | Render Olson timezone data in binary Olson timezone file format
 -- as a lazy @ByteString@
-putOlson :: OlsonData -> Put
-putOlson olson = putOlsonParts version olson1 olson2 posix >> flush
+putOlson olson@(OlsonData _ _ _ posix)
+  | fitsInVersion1 =    putOlsonPart 0  put32bitIntegral olson
+  | otherwise      = do putOlsonPart 50 put32bitIntegral olson1
+                        putOlsonPart 50 put64bitIntegral olson
+                        putPosixTZ posix
   where
-    (olson1, olson2, posix) = splitOlson olson
-    version
-     | olson2 /= mempty = 50
-     | otherwise        = maybe 0 (const 50) $ posix >>= guard . not . null
+    olson1 = extractOlsonV1 olson
+    fitsInVersion1 = fmap null posix /= Just False &&
+                     olson1 == olson {olsonPosixTZ = Nothing}
 
--- | Split Olson timezone data into three parts: timezone data that can
--- be rendered using Version 1 format, timezone data that can only be
--- rendered using Version 2 format, and the POSIX-style TZ string
--- if specified
-splitOlson :: OlsonData -> (OlsonData, OlsonData, Maybe String)
-splitOlson (OlsonData transs ttinfos leaps posix) =
-    (OlsonData transs1 ttinfos1 leaps1 Nothing,
-     OlsonData transs2 ttinfos2 leaps2 Nothing,
-     posix)
+-- | Extract Olson timezone data that can be rendered using Version 1 format
+extractOlsonV1 :: OlsonData -> OlsonData
+extractOlsonV1 (OlsonData transs  ttinfos  leaps  _)
+  | allV1     = OlsonData transs  ttinfos  leaps  Nothing
+  | otherwise = OlsonData transs1 ttinfos1 leaps1 Nothing
   where
     cutoff = 0x80000000 -- 2^31
     fitsIn32bits x = x < cutoff && x >= negate cutoff
-    ( leaps1 ,  leaps2 ) = partition (fitsIn32bits .  leapTime)  leaps
-    (transs1', transs2') = partition (fitsIn32bits . transTime) transs
-    assoc1 = mkAssoc [0] transs1'
-    assoc2 = mkAssoc []  transs2'
-    transs1 = mkTranss transs1' assoc1
-    transs2 = mkTranss transs2' assoc2
-    ttinfos1 = mkTtinfos assoc1
-    ttinfos2 = mkTtinfos assoc2
-    mkAssoc prepend transs' = zip
+    leaps1   = filter (fitsIn32bits .  leapTime)  leaps
+    transs1' = filter (fitsIn32bits . transTime) transs
+    allV1 = length leaps1 == length leaps && length transs1' == length transs
+    assoc1 = zip
       (sortBy (comparing $ fmap tt_ttype . listToMaybe . flip drop ttinfos) .
-        uniq . sort . (prepend ++) $ map transIndex transs')
+        uniq . sort . (0 :) $ map transIndex transs1')
       [0..]
-    mkTranss transs' assoc = [t {transIndex = i} |
-      t <- transs', i <- maybeToList $ lookup (transIndex t) assoc]
-    mkTtinfos assoc = map snd . dropWhile (isNothing . fst) .
-      sortBy (comparing fst) $ zip (map (flip lookup assoc) [0..]) ttinfos
-
-putOlsonParts :: Word8 -> OlsonData -> OlsonData -> Maybe String -> Put
-putOlsonParts 0  olson1 _      _     = putOlsonPart 0 put32bitIntegral olson1
-putOlsonParts v2 olson1 olson2 posix = do
-  putOlsonPart v2 put32bitIntegral olson1
-  putOlsonPart v2 put64bitIntegral olson2
-  putPosixTZ posix
+    transs1 = [t {transIndex = i} |
+      t <- transs1', i <- maybeToList $ lookup (transIndex t) assoc1]
+    ttinfos1 = map snd . dropWhile (isNothing . fst) .
+      sortBy (comparing fst) $ zip (map (flip lookup assoc1) [0..]) ttinfos
 
 putOlsonPart :: Word8 -> (Integer -> Put) -> OlsonData -> Put
 putOlsonPart version putTime (OlsonData transs ttinfos leaps _) = do
-    putASCII "TZif"
+    putASCII "magic number" "TZif"
     putWord8 version
     putByteString . B.pack $ replicate 15 0 -- padding nulls
     replicateM_ 2 $ putCount ttinfosWithTtype
-                       -- tzh_ttisgmtcnt
-                       -- tzh_ttisstdcnt
-    putCount leaps     -- tzh_leapcnt
-    putCount transs    -- tzh_timecnt
-    putCount ttinfos   -- tzh_typecnt
-    putCount abbrChars -- tzh_charcnt
+                               -- tzh_ttisgmtcnt
+                               -- tzh_ttisstdcnt
+    putCount leaps             -- tzh_leapcnt
+    putCount transs            -- tzh_timecnt
+    putCount ttinfos           -- tzh_typecnt
+    put32bitIntegral abbrChars -- tzh_charcnt
     mapM_ (putTime         . transTime ) transs
     mapM_ (put8bitIntegral . transIndex) transs
     mapM_ putTtInfo ttinfosIndexed
-    putASCII abbrChars
+    mapM_ putAbbr abbrStrings
     mapM_ (putLeapInfo putTime) leaps
     mapM_ (putBool . (== Std) . tt_ttype) ttinfosWithTtype -- isstd
     mapM_ (putBool . (== UTC) . tt_ttype) ttinfosWithTtype -- isgmt
@@ -177,7 +163,8 @@
     putCount = put32bitIntegral . length
     ttinfosWithTtype = takeWhile ((<= UTC) . tt_ttype) ttinfosIndexed
     abbrStrings = uniq . sort $ map tt_abbr ttinfos
-    abbrChars = concatMap (++ "\NUL") abbrStrings
+    abbrChars = sum (map length abbrStrings) + length abbrStrings
+    putAbbr abbr = putASCII "time zone abbreviation" abbr >> putWord8 0
     abbrAssocs = zip abbrStrings . scanl (+) 0 $
                  map ((+ 1) . length) abbrStrings
     ttinfosIndexed = [TtInfo gmtoff isdst ttype i |
@@ -187,7 +174,7 @@
 putPosixTZ :: Maybe String -> Put
 putPosixTZ posix = do
   putWord8 10
-  putASCII $ fromMaybe "" posix
+  putASCII "POSIX TZ string"$ fromMaybe "" posix
   putWord8 10
 
 putTtInfo :: TtInfo Int -> Put
@@ -220,5 +207,11 @@
 uniq :: Eq a => [a] -> [a]
 uniq = map head . group
 
-putASCII :: String -> Put
-putASCII = putByteString . B.pack . map (fromIntegral . fromEnum)
+putASCII :: String -> String -> Put
+putASCII what =
+    putByteString . B.pack . map (fromIntegral . verify . fromEnum)
+  where
+    verify c
+     | c >= 32 && c <= 126 = c
+     | otherwise           = error $ "Cannot render TimeZoneSeries: " ++
+                               what ++ " contains non-ASCII characters"
diff --git a/Data/Time/LocalTime/TimeZone/Olson/Types.hs b/Data/Time/LocalTime/TimeZone/Olson/Types.hs
--- a/Data/Time/LocalTime/TimeZone/Olson/Types.hs
+++ b/Data/Time/LocalTime/TimeZone/Olson/Types.hs
@@ -65,8 +65,11 @@
 
 instance Monoid OlsonData where
   mempty = OlsonData [] [] [] Nothing
-  mappend (OlsonData a  b  c  d ) (OlsonData a' b' c' d')
-    = OlsonData (a++a') (b++b') (c++c') (d `mplus` d')
+  mappend (OlsonData a  b  c  d ) (OlsonData a' b' c' d') =
+      OlsonData (a ++ map (shiftBy $ length b) a')
+                (b ++ b') (c ++ c') (d `mplus` d')
+    where
+      shiftBy n trans = trans {transIndex = n + transIndex trans}
 
 -- | A @Transition@ represents a moment when the clocks change in a
 -- timezone. It consists of a Unix timestamp value that indicates the
diff --git a/timezone-olson.cabal b/timezone-olson.cabal
--- a/timezone-olson.cabal
+++ b/timezone-olson.cabal
@@ -7,7 +7,7 @@
 -- The package version. See the Haskell package versioning policy
 -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
 -- standards guiding when and how versions should be incremented.
-Version:             0.1.0
+Version:             0.1.1
 
 -- A short (one-line) description of the package.
 Synopsis:            A pure Haskell parser and renderer for binary Olson timezone files
