Index: django/contrib/localflavor/fi/__init__.py
===================================================================
Index: django/contrib/localflavor/fi/forms.py
===================================================================
--- django/contrib/localflavor/fi/forms.py	(revision 0)

+++ django/contrib/localflavor/fi/forms.py	(revision 0)

@@ -0,0 +1,31 @@

+""" 
+FI-specific Form helpers 
+""" 
+
+from django.newforms import ValidationError
+from django.newforms.fields import Field, EMPTY_VALUES 
+from django.utils.translation import gettext
+import re
+
+class FISocialSecurityNumber(Field):
+    def clean(self, value):
+        super(FISocialSecurityNumber, self).clean(value)
+        if value in EMPTY_VALUES:
+ 	        return u''
+        
+        checkmarks = "0123456789ABCDEFHJKLMNPRSTUVWXY"
+        result = re.match(r"""^
+            (?P<date>([0-2]\d|3[01])
+            (0\d|1[012])
+            (\d{2}))
+            [A+-]
+            (?P<serial>(\d{3}))
+            (?P<chechsum>[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE)
+        if not result:
+            raise ValidationError(gettext(u'Enter a valid Finnish social security number.'))
+        checksum = int(result.groupdict()['date'] + result.groupdict()['serial'])
+
+        if checkmarks[checksum % len(checkmarks)] == result.groupdict()['chechsum'].upper():
+            return u'%s' % value.upper()
+        
+        raise ValidationError(gettext(u'Enter a valid Finnish social security number.'))
Index: tests/regressiontests/forms/tests.py

===================================================================

--- tests/regressiontests/forms/tests.py	(revision 4866)

+++ tests/regressiontests/forms/tests.py	(working copy)

@@ -3878,7 +3878,49 @@

 <option value="okinawa">Okinawa</option>
 </select>
 
+# FISocialSecurityNumber ###############################################################
 
+>>> from django.contrib.localflavor.fi.forms import FISocialSecurityNumber
+>>> f = FISocialSecurityNumber()
+>>> f.clean('010101-0101')
+u'010101-0101'
+>>> f.clean('010101+0101')
+u'010101+0101'
+>>> f.clean('010101A0101')
+u'010101A0101'
+>>> f.clean('101010-0102')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Finnish social security number.']
+>>> f.clean('10a010-0101')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Finnish social security number.']
+>>> f.clean('101010-0\xe401')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Finnish social security number.']
+>>> f.clean('101010b0101')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Finnish social security number.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f = FISocialSecurityNumber(required=False)
+>>> f.clean('010101-0101')
+u'010101-0101'
+>>> f.clean(None)
+u''
+>>> f.clean('')
+u''
+
 #################################
 # Tests of underlying functions #
 #################################
