diff --git a/Data/Dates.hs b/Data/Dates.hs
--- a/Data/Dates.hs
+++ b/Data/Dates.hs
@@ -18,60 +18,8 @@
 import Text.Parsec.String
 import Data.Generics
 
--- | Date / Time
-data DateTime =
-  DateTime {
-    year ∷ Int,
-    month ∷ Int,
-    day ∷ Int,
-    hour ∷ Int,
-    minute ∷ Int,
-    second ∷ Int }
-  deriving (Eq,Ord,Data,Typeable)
-
--- | 12 months names.
-months ∷ [String]
-months = ["january",
-          "february",
-          "march",
-          "april",
-          "may",
-          "june",
-          "july",
-          "august",
-          "september",
-          "october",
-          "november",
-          "december"]
-
--- | capitalize first letter of the string
-capitalize ∷ String → String
-capitalize [] = []
-capitalize (x:xs) = (toUpper x):xs
-
--- | Show name of given month
-showMonth ∷  Int → String
-showMonth i = capitalize $ months !! (i-1)
-
-instance Show DateTime where
-  show (DateTime y m d h mins s) = 
-    show d ⧺ " " ⧺ showMonth m ⧺ " " ⧺ show y ⧺ ", " ⧺
-      show h ⧺ ":" ⧺ show mins ⧺ ":" ⧺ show s
-
--- | Only time, without date
-data Time = 
-  Time {
-    tHour ∷ Int,
-    tMinute ∷ Int,
-    tSecond ∷ Int }
-  deriving (Eq,Ord,Show,Data,Typeable)
-
--- | Parser version of Prelude.read
-tryRead :: Read a => String -> Parsec String st a
-tryRead str =
-  case reads str of
-    [(res, "")] -> return res
-    _ -> fail $ "Cannot read: " ++ str
+import Data.Dates.Types
+import Data.Dates.Internal
 
 -- | Get current date and time.
 getCurrentDateTime ∷  IO DateTime
@@ -111,35 +59,6 @@
                  hour = tHour t + hour dt,
                  minute = tMinute t + minute dt,
                  second = tSecond t + second dt }
-
-times ∷ Int → Parsec String st t → Parsec String st [t]
-times 0 _ = return []
-times n p = do
-  ts ← times (n-1) p
-  t ← optionMaybe p
-  case t of
-    Just t' → return (ts ++ [t'])
-    Nothing → return ts
-                               
-number ∷ Int → Int → Parsec String st Int
-number n m = do
-  t ← tryRead =<< (n `times` digit)
-  if t > m
-    then fail "number too large"
-    else return t
-
-pYear ∷ Parsec String st Int
-pYear = do
-  y ← number 4 10000
-  if y < 2000
-    then return (y+2000)
-    else return y
-
-pMonth ∷ Parsec String st Int
-pMonth = number 2 12
-
-pDay ∷ Parsec String st Int
-pDay = number 2 31
 
 euroNumDate ∷ Parsec String st DateTime
 euroNumDate = do
diff --git a/Data/Dates/Formats.hs b/Data/Dates/Formats.hs
new file mode 100644
--- /dev/null
+++ b/Data/Dates/Formats.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable #-}
+-- | This module allows to parse arbitrary date formats.
+-- Date formats are specified as strings:
+--
+--  * "DD.MM.YYY"
+--
+--  * "YYYY\/MM\/DD"
+--
+--  * "DD\/MM\/YYYY, HH:mm:SS"
+--
+--  * and so on.
+--
+module Data.Dates.Formats
+  (FormatElement (..), Format,
+   pFormat, formatParser,
+   parseDateFormat
+  ) where
+
+import Control.Applicative ((<$>))
+import Data.Monoid
+import Text.Parsec
+
+import Data.Dates.Types
+import Data.Dates.Internal (number)
+
+-- | Date\/time format element
+data FormatElement =
+    YEAR   Int
+  | MONTH  Int
+  | DAY    Int
+  | HOUR   Int
+  | MINUTE Int
+  | SECOND Int
+  | Fixed String
+  deriving (Eq, Show)
+
+-- | Date\/time format
+type Format = [FormatElement]
+
+nchars ∷ Char → Parsec String st Int
+nchars c = do
+  s ← many1 $ char c
+  return $ length s
+
+-- | Parser for date\/time format.
+pFormat ∷ Parsec String st Format
+pFormat = many1 $ choice $ map try [pYear, pMonth, pDay,
+                                    pHour, pMinute, pSecond,
+                                    pFixed]
+  where
+    pYear   = YEAR   <$> nchars 'Y'
+    pMonth  = MONTH  <$> nchars 'M'
+    pDay    = DAY    <$> nchars 'D'
+    pHour   = HOUR   <$> nchars 'H'
+    pMinute = MINUTE <$> nchars 'm'
+    pSecond = SECOND <$> nchars 'S'
+    pFixed  = Fixed <$> (many1 $ noneOf "YMDHmS")
+
+pYear ∷ Int → Parsec String st DateTime
+pYear n = do
+  y ← number n 10000
+  if y < 2000
+    then return $ mempty {year = y+2000}
+    else return $ mempty {year = y}
+
+pMonth ∷ Int → Parsec String st DateTime
+pMonth n = do
+  m ← number n 12
+  return $ mempty {month = m}
+
+pDay ∷ Int → Parsec String st DateTime
+pDay n = do
+  d ← number n 31
+  return $ mempty {day = d}
+
+pHour ∷ Int → Parsec String st DateTime
+pHour n = do
+  h ← number n 23
+  return $ mempty {hour = h}
+
+pMinute ∷ Int → Parsec String st DateTime
+pMinute n = do
+  m ← number n 59
+  return $ mempty {minute = m}
+
+pSecond ∷ Int → Parsec String st DateTime
+pSecond n = do
+  s ← number n 59
+  return $ mempty {second = s}
+
+-- | Make Parser for specified date format.
+formatParser ∷ Format → Parsec String st DateTime
+formatParser format = mconcat <$> mapM parser format
+  where
+    parser (YEAR   n) = pYear n
+    parser (MONTH  n) = pMonth n
+    parser (DAY    n) = pDay n
+    parser (HOUR   n) = pHour n
+    parser (MINUTE n) = pMinute n
+    parser (SECOND n) = pSecond n
+    parser (Fixed s) = string s >> return mempty
+
+-- | Parse date\/time in specified format.
+parseDateFormat :: String  -- ^ Format string, i.e. "DD.MM.YY"
+                -> String  -- ^ String to parse
+                -> Either ParseError DateTime
+parseDateFormat formatStr str = do
+  format <- runParser pFormat () "(date format string)" formatStr
+  runParser (formatParser format) () "(date)" str
+
diff --git a/Data/Dates/Internal.hs b/Data/Dates/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/Dates/Internal.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable #-}
+
+module Data.Dates.Internal where
+
+import Text.Parsec
+import Text.Parsec.String
+
+-- | Parser version of Prelude.read
+tryRead :: Read a => String -> Parsec String st a
+tryRead str =
+  case reads str of
+    [(res, "")] -> return res
+    _ -> fail $ "Cannot read: " ++ str
+
+-- | Apply parser N times
+times ∷ Int
+     → Parsec String st t
+     → Parsec String st [t]
+times 0 _ = return []
+times n p = do
+  ts ← times (n-1) p
+  t ← optionMaybe p
+  case t of
+    Just t' → return (ts ++ [t'])
+    Nothing → return ts
+                               
+-- | Parse natural number of N digits
+-- which is not greater than M
+number ∷ Int   -- ^ Number of digits
+       → Int   -- ^ Maximum value
+       → Parsec String st Int
+number n m = do
+  t ← tryRead =<< (n `times` digit)
+  if t > m
+    then fail "number too large"
+    else return t
+
+pYear ∷ Parsec String st Int
+pYear = do
+  y ← number 4 10000
+  if y < 2000
+    then return (y+2000)
+    else return y
+
+pMonth ∷ Parsec String st Int
+pMonth = number 2 12
+
+pDay ∷ Parsec String st Int
+pDay = number 2 31
+
diff --git a/Data/Dates/Types.hs b/Data/Dates/Types.hs
new file mode 100644
--- /dev/null
+++ b/Data/Dates/Types.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable #-}
+
+module Data.Dates.Types
+  (DateTime (..),
+   Time (..),
+   months, capitalize
+  ) where
+
+import Prelude.Unicode
+import Data.Monoid
+import Data.Char
+import Data.Generics
+
+-- | Date / Time
+data DateTime =
+  DateTime {
+    year   ∷ Int,
+    month  ∷ Int,
+    day    ∷ Int,
+    hour   ∷ Int,
+    minute ∷ Int,
+    second ∷ Int }
+  deriving (Eq,Ord,Data,Typeable)
+
+-- | 12 months names.
+months ∷ [String]
+months = ["january",
+          "february",
+          "march",
+          "april",
+          "may",
+          "june",
+          "july",
+          "august",
+          "september",
+          "october",
+          "november",
+          "december"]
+
+-- | capitalize first letter of the string
+capitalize ∷ String → String
+capitalize [] = []
+capitalize (x:xs) = (toUpper x):xs
+
+-- | Show name of given month
+showMonth ∷  Int → String
+showMonth i = capitalize $ months !! (i-1)
+
+instance Show DateTime where
+  show (DateTime y m d h mins s) = 
+    show d ⧺ " " ⧺ showMonth m ⧺ " " ⧺ show y ⧺ ", " ⧺
+      show h ⧺ ":" ⧺ show mins ⧺ ":" ⧺ show s
+
+-- | Only time, without date
+data Time = 
+  Time {
+    tHour   ∷ Int,
+    tMinute ∷ Int,
+    tSecond ∷ Int }
+  deriving (Eq,Ord,Show,Data,Typeable)
+
+instance Monoid DateTime where
+  mempty = DateTime 0 0 0 0 0 0
+  mappend dt1 dt2 =
+      DateTime (year dt1   `plus` year dt2)
+               (month dt1  `plus` month dt2)
+               (day dt1    `plus` day dt2)
+               (hour dt1   `plus` hour dt2)
+               (minute dt1 `plus` minute dt2)
+               (second dt1 `plus` second dt2)
+    where
+      plus :: Int → Int → Int
+      plus 0 x = x
+      plus x _ = x
+
diff --git a/dates.cabal b/dates.cabal
--- a/dates.cabal
+++ b/dates.cabal
@@ -1,5 +1,5 @@
 name:                dates
-version:             0.1.2.0
+version:             0.2.0.0
 synopsis:            Small library for parsing different dates formats.
 description:         This package allows to parse many different formats
                      of dates. Both absolute and relative dates are supported.
@@ -17,6 +17,9 @@
                      .
                      4-digits years may be abbreviated (such as 12 for 2012).
                      Both 12-hour and 24-hour time formats are supported.
+                     .
+                     User-specified date formats are supported by
+                     Data.Dates.Formats module.
 
 homepage:            http://redmine.iportnov.ru/projects/dates/
 license:             BSD3
@@ -29,7 +32,10 @@
 cabal-version:       >=1.8
 
 library
-  exposed-modules:     Data.Dates
+  exposed-modules:     Data.Dates,
+                       Data.Dates.Types,
+                       Data.Dates.Formats,
+                       Data.Dates.Internal
   -- other-modules:       
   build-depends:       base ==4.5.*,
                        base-unicode-symbols ==0.2.*,
