Index: main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.py
===================================================================
--- main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.py	(revision 5323)
+++ main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.py	(revision 5330)
@@ -1,4 +1,5 @@
 """Converters for zope.schema-based datatypes.
 """
+import datetime
 import grok
 from zope.component import getMultiAdapter
@@ -8,5 +9,5 @@
 except ImportError:
     from zope.browser.interfaces import ITerms
-from zope.schema.interfaces import IBool, IText, IInt, IChoice
+from zope.schema.interfaces import IBool, IText, IInt, IChoice, IDate
 from waeup.sirp.interfaces import ISchemaTypeConverter
 
@@ -52,4 +53,6 @@
 
     def toString(self, value, strict=True):
+        """Convert given `value` to string according to assigned field type.
+        """
         if strict:
             self.context.validate(value)
@@ -131,8 +134,22 @@
     def _convertValueFromString(self, string):
         if self.tokens is not None:
-            return self.tokens[string]
+            result = None
+            try:
+                result = self.tokens[string]
+            except KeyError:
+                # Be gentle...
+                try:
+                    result = self.tokens[string.lower()]
+                    return result
+                except KeyError:
+                    tokenlist = (','.join(self.tokens[:2]))
+                    raise ValueError(
+                        'The token %s is not valid. Use one of %s, ...' % (
+                            string, tokenlist))
+            return result
         if self.terms is not None:
             return self.terms.getValue(string)
-        return self.context.source.getTermByToken(string).value
+        result = self.context.source.getTermByToken(string).value
+        return result
 
     def _convertValueToString(self, value):
@@ -164,2 +181,59 @@
         return super(ChoiceConverter, self).toString(value=value,
                                                      strict=strict)
+
+class DateConverter(Converter):
+    """A converter for zope.schema.IDate fields.
+
+    Converts date to string and vice versa. Stringified dates are
+    expected in format ``YYYY-MM-DD``.
+
+    To support at least some other formats, we accept for conversion
+    from string also the following formats:
+
+    * ``YYYY/MM/DD``
+    
+    * ``DD/MM/YYYY``
+    
+    * ``D/M/YYYY``
+
+    * ``DD.MM.YYYY``
+
+    * ``D.M.YYYY``
+
+    When converting to strings, always 'YYYY-MM-DD' is returned.
+
+    For convenience, when converting from strings also string data
+    separated by space is stripped before processing. Therefore
+    strings like '1990-04-01 12:12:01 GMT +1' will also work.
+    """
+    grok.context(IDate)
+    grok.provides(ISchemaTypeConverter)
+
+    #: List of supported date formats in `strftime()` notation.
+    formats = ['%Y-%m-%d', '%Y/%m/%d' , '%d/%m/%Y', '%D/%M/%Y',
+               '%d.%m.%Y', '%D.%M.%Y']
+    
+    def _convertValueFromString(self, string):
+        orig_string = string
+        if string is NONE_STRING_VALUE:
+            string = None
+        if string is None:
+            return None
+        value = None
+        if ' ' in string:
+            string = string.split(' ')[0]
+        for format in self.formats:
+            try:
+                value = datetime.datetime.strptime(string, format)
+                break
+            except ValueError:
+                pass
+        if value is None:
+            raise ValueError(
+                'Cannot convert to date: %s. Use YYYY-MM-DD.' %
+                orig_string)
+        value = value.date()
+        return value
+
+    def _convertValueToString(self, value):
+        return datetime.date.strftime(value, '%Y-%m-%d')
Index: main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.txt
===================================================================
--- main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.txt	(revision 5323)
+++ main/waeup.sirp/branches/henrik-experimental-layout/src/waeup/sirp/utils/converters.txt	(revision 5330)
@@ -267,4 +267,12 @@
        validation checks are disabled with this converter. This speeds
        up things as expensive lookups are avoided.
+
+
+:class:`DateConverter`
+----------------------
+
+.. autoclass:: waeup.sirp.utils.converters.DateConverter
+   :members:
+   :inherited-members:
 
 
@@ -682,5 +690,5 @@
 
 A more complex (but still realistic example) for conversion of
-:mode:`zope.schema.Choice` fields follows. Here we have a special kind
+:mod:`zope.schema.Choice` fields follows. Here we have a special kind
 of object, the `Cave` class, and get their `name` attribute as token.
 
@@ -739,2 +747,115 @@
     >>> converter.toString(obj2)
     'Fred'
+
+Date field converters
+---------------------
+
+We create a field that also implements ``IDate``, a regular
+``Date`` field:
+
+    >>> from zope.schema import Date
+    >>> from datetime import date
+    >>> field = Date(title=u'Some date',
+    ...             default=date(2010, 4, 1),
+    ...             required=True)
+
+Now we can get a converter for this field by explicitly creating a
+:class:`IntConverter` instance:
+
+    >>> from waeup.sirp.utils.converters import DateConverter
+    >>> converter = DateConverter(field)
+
+Or we can just grab a registered adapter:
+
+    >>> from waeup.sirp.interfaces import ISchemaTypeConverter
+    >>> converter = ISchemaTypeConverter(field)
+
+This will select the correct converter for us automatically.
+
+    >>> converter
+    <waeup.sirp.utils.converters.DateConverter object at 0x...>
+
+Now we can convert strings to this type:
+
+    >>> converter.fromString('2007-04-23')
+    datetime.date(2007, 4, 23)
+
+And back:
+
+    >>> converter.toString(date(2007,4,23))
+    '2007-04-23'
+
+As string input we also accept 'DD/MM/YYYY':
+
+    >>> converter.fromString('13/05/1945')
+    datetime.date(1945, 5, 13)
+
+and some more bizarre formats:
+
+    >>> converter.fromString('1945/05/13')
+    datetime.date(1945, 5, 13)
+
+    >>> converter.fromString('1945/05/13 12:12:34 GMT+1')
+    datetime.date(1945, 5, 13)
+
+    >>> converter.fromString('1945/5/6')
+    datetime.date(1945, 5, 6)
+
+    >>> converter.fromString('06.05.1945')
+    datetime.date(1945, 5, 6)
+
+    >>> converter.fromString('6.5.1945')
+    datetime.date(1945, 5, 6)
+
+while other date formats will mean trouble:
+
+    >>> converter.fromString('first of July 1960')
+    Traceback (most recent call last):
+    ...
+    ValueError: Cannot convert to date: first of July 1960. Use YYYY-MM-DD.
+
+Okay, not very surprising. But the field definitions can help also
+deliver values, if the given value is missing:
+
+    >>> converter.fromString(None)
+    datetime.date(2010, 4, 1)
+
+``None`` is not an acceptable value for fields which are required but
+provide no default:
+
+    >>> field = Date(title=u'Some Title',
+    ...             required=True)
+    >>> converter = ISchemaTypeConverter(field)
+    >>> converter.fromString(None)
+    Traceback (most recent call last):
+    ...
+    RequiredMissing
+
+If we want to avoid this type of exception (and risk non-applicable
+data to be stored), we can use the ``strict`` parameter of
+``fromString()``:
+
+    >>> converter.fromString(None, strict=False) is None
+    True
+
+The same for the inverse operation:
+
+    >>> converter.toString(None)
+    Traceback (most recent call last):
+    ...
+    RequiredMissing
+    >>> converter.toString(None, strict=False) is None
+    True
+
+
+If a field is not required, we will get the ``missing_value`` type in
+same case:
+
+    >>> field = Date(title=u'Some Title',
+    ...             required=False)
+    >>> converter = ISchemaTypeConverter(field)
+    >>> converter.fromString(None) is None
+    True
+
+    >>> converter.toString(None) is None
+    True
