1# kennitala.py - functions for handling Icelandic identity codes 2# coding: utf-8 3# 4# Copyright (C) 2015 Tuomas Toivonen 5# Copyright (C) 2015 Arthur de Jong 6# 7# This library is free software; you can redistribute it and/or 8# modify it under the terms of the GNU Lesser General Public 9# License as published by the Free Software Foundation; either 10# version 2.1 of the License, or (at your option) any later version. 11# 12# This library is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# Lesser General Public License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public 18# License along with this library; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20# 02110-1301 USA 21 22"""Kennitala (Icelandic personal and organisation identity code). 23 24Module for handling Icelandic personal and organisation identity codes 25(kennitala). 26 27>>> validate('450401-3150') # organisation 28'4504013150' 29>>> validate('120174-3399') # individual 30'1201743399' 31>>> validate('530575-0299') 32Traceback (most recent call last): 33 ... 34InvalidChecksum: ... 35>>> validate('320174-3399') 36Traceback (most recent call last): 37 ... 38InvalidComponent: ... 39>>> format('1201743399') 40'120174-3399' 41""" 42 43import datetime 44import re 45 46from stdnum.exceptions import * 47from stdnum.util import clean 48 49 50# Icelandic personal and organisation identity codes are composed of 51# date part, a dash, two random digits, a checksum, and a century 52# indicator where '9' for 1900-1999 and '0' for 2000 and beyond. For 53# organisations instead of birth date, the registration date is used, 54# and number 4 is added to the first digit. 55_kennitala_re = re.compile( 56 r'^(?P<day>[01234567]\d)(?P<month>[01]\d)(?P<year>\d\d)' 57 r'(?P<random>\d\d)(?P<control>\d)' 58 r'(?P<century>[09])$') 59 60 61def compact(number): 62 """Convert the kennitala to the minimal representation. This 63 strips surrounding whitespace and separation dash, and converts it 64 to upper case.""" 65 return clean(number, '-').upper().strip() 66 67 68def checksum(number): 69 """Calculate the checksum.""" 70 weights = (3, 2, 7, 6, 5, 4, 3, 2, 1, 0) 71 return sum(w * int(n) for w, n in zip(weights, number)) % 11 72 73 74def validate(number): 75 """Check if the number provided is a valid kennitala. It checks the 76 format, whether a valid date is given and whether the check digit is 77 correct.""" 78 number = compact(number) 79 match = _kennitala_re.search(number) 80 if not match: 81 raise InvalidFormat() 82 day = int(match.group('day')) 83 month = int(match.group('month')) 84 year = int(match.group('year')) 85 if match.group('century') == '9': 86 year += 1900 87 else: 88 year += 2000 89 # check if birth date or registration data is valid 90 try: 91 if day >= 40: # organisation 92 datetime.date(year, month, day - 40) 93 else: # individual 94 datetime.date(year, month, day) 95 except ValueError: 96 raise InvalidComponent() 97 # validate the checksum 98 if checksum(number) != 0: 99 raise InvalidChecksum() 100 return number 101 102 103def is_valid(number): 104 """Check if the number provided is a valid HETU. It checks the format, 105 whether a valid date is given and whether the check digit is correct.""" 106 try: 107 return bool(validate(number)) 108 except ValidationError: 109 return False 110 111 112def format(number): 113 """Reformat the number to the standard presentation format.""" 114 number = compact(number) 115 return number[:6] + '-' + number[6:] 116