diff --git a/Graphics/HsExif.hs b/Graphics/HsExif.hs
--- a/Graphics/HsExif.hs
+++ b/Graphics/HsExif.hs
@@ -1,18 +1,23 @@
 {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
 -- | Ability to work with the EXIF data contained in JPEG files.
 module Graphics.HsExif (
-	ExifTag(..),
-	TagLocation(..),
-	ExifValue(..),
+	-- $intro
+
+	-- * Main functions
 	parseFileExif,
 	parseExif,
+
+	-- * Higher-level helper functions
 	readExifDateTime,
 	getDateTimeOriginal,
 	getOrientation,
 	ImageOrientation(..),
 	RotationDirection(..),
 
-	-- now all known exif tags
+	-- * The ExifValue type
+	ExifValue(..),
+
+	-- * All known exif tags
 	exposureTime,
 	fnumber,
 	exposureProgram,
@@ -49,6 +54,10 @@
 	model,
 	software,
 	copyright,
+
+	-- * If you need to declare your own exif tags
+	ExifTag(..),
+	TagLocation(..),
 ) where
 
 import Data.Binary.Get
@@ -56,6 +65,7 @@
 import Control.Monad (liftM, unless)
 import qualified Data.ByteString.Char8 as Char8
 import Data.Word
+import Data.Char (isDigit, ord, chr)
 import Data.Int (Int32)
 import Data.List
 import Data.Maybe (fromMaybe)
@@ -74,14 +84,14 @@
 -- of the value, simply use 'show'.
 data ExifValue = ExifNumber Int
 	-- ^ An exif number. Could be short, int, signed or not.
-	| ExifText String
+	| ExifText !String
 	-- ^ ASCII text.
-	| ExifRational Int Int
+	| ExifRational !Int !Int
 	-- ^ A rational number (numerator, denominator).
 	-- Sometimes we're used to it as rational (exposition time: 1/160),
 	-- sometimes as float (exposure compensation, we rather think -0.75)
 	-- 'show' will display it as 1/160.
-	| ExifUnknown Word16 Int -- type then value
+	| ExifUnknown !Word16 !Int -- type then value
 	-- ^ Unknown exif value type. Maybe float? If the JPEG file is not
 	-- corrupted, please send it to me.
 	deriving Eq
@@ -328,21 +338,54 @@
 signedInt32ToInt :: Word32 -> Int
 signedInt32ToInt w = fromIntegral (fromIntegral w :: Int32)
 
+-- i had this as runGetM and reusing in parseExif,
+-- sadly fail is not implemented for Either.
+-- will do for now.
+runMaybeGet :: Get a -> B.ByteString -> Maybe a
+runMaybeGet get bs = case runGetOrFail get bs of
+	Left _ -> Nothing
+	Right (_,_,x) -> Just x
+
 -- | Decode an EXIF date time value.
-readExifDateTime :: String -> LocalTime
-readExifDateTime dateStr = 
-	-- i know more elegant ways to code this.. parsec, regex, text..
-	-- but i don't want to bring in too many dependencies to this library.
-	-- the date is like "YYYY:MM:DD HH:MM:SS"
-	LocalTime
-		(fromGregorian (read $ take 4 dateStr) (read $ take 2 . drop 5 $ dateStr) (read $ take 2 . drop 8 $ dateStr))
-		(TimeOfDay (read $ take 2 . drop 11 $ dateStr) (read $ take 2 . drop 14 $ dateStr) (read $ take 2 . drop 17 $ dateStr))
+-- Will return 'Nothing' in case parsing fails.
+readExifDateTime :: String -> Maybe LocalTime
+readExifDateTime dateStr = runMaybeGet getExifDateTime $ B.pack $ map (fromIntegral . ord) dateStr
 
+getExifDateTime :: Get LocalTime
+getExifDateTime = do
+	year <- readDigit 4
+	month <- getCharValue ':' >> readDigit 2
+	day <- getCharValue ':' >> readDigit 2
+	hour <- getCharValue ' ' >> readDigit 2
+	minute <- getCharValue ':' >> readDigit 2
+	second <- getCharValue ':' >> readDigit 2
+	return $ LocalTime (fromGregorian year month day) (TimeOfDay hour minute second)
+	where
+		readDigit x = liftM read $ count x getDigit
+
+count :: Int -> Get a -> Get [a]
+count n p | n <= 0 = return []
+        | otherwise = sequence (replicate n p)
+	
+
 -- | Extract the date and time when the picture was taken
 -- from the EXIF information.
 getDateTimeOriginal :: Map ExifTag ExifValue -> Maybe LocalTime
-getDateTimeOriginal exifData = liftM (readExifDateTime . show) $ Map.lookup dateTimeOriginal exifData
+getDateTimeOriginal exifData = Map.lookup dateTimeOriginal exifData >>= readExifDateTime . show 
 
+getCharWhere :: (Char->Bool) -> Get Char
+getCharWhere wher = do
+	char <- liftM (chr . fromIntegral) getWord8
+	if wher char
+		then return char
+		else fail "no parse"
+
+getDigit :: Get Char
+getDigit = getCharWhere isDigit
+
+getCharValue :: Char -> Get Char
+getCharValue char = getCharWhere (==char)
+
 data RotationDirection = MinusNinety
 	| Ninety
 	| HundredAndEighty
@@ -355,6 +398,7 @@
 	deriving (Show, Eq)
 
 -- | Extract the image orientation from the EXIF information.
+-- Will return 'Nothing' on parse error.
 getOrientation :: Map ExifTag ExifValue -> Maybe ImageOrientation
 getOrientation exifData = do
 	rotationVal <- Map.lookup orientation exifData
@@ -367,4 +411,18 @@
 		ExifNumber 6 -> Just $ Rotation MinusNinety
 		ExifNumber 7 -> Just $ MirrorRotation Ninety
 		ExifNumber 8 -> Just $ Rotation Ninety
-		_ -> error $ show rotationVal
+		_ -> Nothing
+
+-- $intro
+--
+-- EXIF parsing from JPEG files.
+-- EXIF tags are enumerated as ExifTag values, check 'exposureTime' for instance.
+-- If you use the predefined ExifTag values, you don't care about details
+-- of the ExifTag type, however you should check out the 'ExifValue' type.
+--
+-- You start from a JPEG file, you can parse its exif tags as a 'Map' of
+-- 'ExifTag' to 'ExifValue' using 'parseExif' or 'parseFileExif'.
+-- You can enumerate the map or 'lookup' the tags that interest you.
+--
+-- There are also a couple of higher-level helpers like 'getOrientation',
+-- 'getDateTimeOriginal'.
diff --git a/hsexif.cabal b/hsexif.cabal
--- a/hsexif.cabal
+++ b/hsexif.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                hsexif
-version:             0.2.0.1
+version:             0.3.0.0
 synopsis:            EXIF handling library in pure Haskell
 description:         The hsexif library provides functions for working with EXIF data
                      contained in JPEG files. Currently it only supports reading the data.
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -22,18 +22,19 @@
 		describe "basic parsing" $ testBasic imageContents
 		describe "extract picture date" $ testDate exifData
 		describe "image orientation" $ testOrientation exifData
+		describe "read exif date time" $ testReadExifDateTime
 
 testNotAJpeg :: B.ByteString -> Spec
 testNotAJpeg imageContents = it "returns empty list if not a JPEG" $
-	assertEqual "doesn't match" (Left "Not a JPEG file") (parseExif imageContents)
+	assertEqual' (Left "Not a JPEG file") (parseExif imageContents)
 
 testNoExif :: B.ByteString -> Spec
 testNoExif imageContents = it "returns empty list if no EXIF" $
-	assertEqual "doesn't match" (Left "No EXIF in JPEG") (parseExif imageContents)
+	assertEqual' (Left "No EXIF in JPEG") (parseExif imageContents)
 
 testBasic :: B.ByteString -> Spec
 testBasic imageContents = it "parses a simple JPEG" $
-	assertEqual "doesn't match" (Right $ Map.fromList [
+	assertEqual' (Right $ Map.fromList [
 		(exposureTime, ExifRational 1 160),
 		(fnumber, ExifRational 0 10),
 		(exposureProgram, ExifNumber 2),
@@ -90,9 +91,17 @@
 
 testDate :: Map ExifTag ExifValue -> Spec
 testDate exifData = it "extracts the date correctly" $
-	assertEqual "doesn't match" (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33))
+	assertEqual' (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33))
 		(getDateTimeOriginal exifData)
 
 testOrientation :: Map ExifTag ExifValue -> Spec
 testOrientation exifData = it "reads exif orientation" $
-	assertEqual "doesn't match" (Just Normal) $ getOrientation exifData
+	assertEqual' (Just Normal) $ getOrientation exifData
+
+testReadExifDateTime :: Spec
+testReadExifDateTime = it "reads exif date time" $ do
+	assertEqual' (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33)) (readExifDateTime "2013:10:02 20:33:33")
+	assertEqual' Nothing (readExifDateTime "2013:10:02 20:33:3")
+
+assertEqual' :: (Show a, Eq a) => a -> a -> Assertion
+assertEqual' = assertEqual "doesn't match"
