lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.13-8-gdf9f922

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 1.13-8-gdf9f922



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  df9f9222f97ad2cccb3dd93499ae9e7768584565 (commit)
      from  4500881cf6aad1ddd1ad8b3bcc1495f5a48c45cf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=df9f9222f97ad2cccb3dd93499ae9e7768584565

commit df9f9222f97ad2cccb3dd93499ae9e7768584565
Author: Leon Sandøy <leon.haland@gmail.com>
Date:   Fri Jan 31 17:32:00 2020 +0100

    Implement get_birth_date() for no.fodselsnummer
    
    This adds a function that allows you to determine a persons birth date
    from a Norwegian fødselsnummer.
    
    This also accounts for D-numbers, H-numbers, and FH-numbers, which
    contain special exceptions and modifications to the birthdate portion of
    the number.
    
    Most of the information this is based on was found here:
    https://no.wikipedia.org/wiki/F%C3%B8dselsnummer#H-nummer
    
    It also updates the list of valid fødselsnummer in the tests, since this
    list contained many numbers that are not valid by this new validation
    that now accounts for dates.
    
    Additionally, this updates all tests that were failing under the new
    validation, and adds a few new tests to bring the coverage to 100%.
    
    Closes https://github.com/arthurdejong/python-stdnum/pull/187

diff --git a/stdnum/no/fodselsnummer.py b/stdnum/no/fodselsnummer.py
index ce1ef13..50d27b6 100644
--- a/stdnum/no/fodselsnummer.py
+++ b/stdnum/no/fodselsnummer.py
@@ -3,6 +3,7 @@
 #
 # Copyright (C) 2018 Ilya Vihtinsky
 # Copyright (C) 2018 Arthur de Jong
+# Copyright (C) 2020 Leon Sandøy
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -29,18 +30,20 @@ More information:
 * https://no.wikipedia.org/wiki/F%C3%B8dselsnummer
 * https://en.wikipedia.org/wiki/National_identification_number#Norway
 
->>> validate('684131 52112')
-'68413152112'
->>> get_gender('684131 52112')
-'M'
->>> validate('684131 52123')
+>>> validate('151086 95088')
+'15108695088'
+>>> get_gender('151086-95088')
+'F'
+>>> validate('15108695077')
 Traceback (most recent call last):
     ...
 InvalidChecksum: ...
->>> format('68413152112')
-'684131 52112'
+>>> format('15108695077')
+'151086 95077'
 """
 
+import datetime
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -72,6 +75,42 @@ def get_gender(number):
         return 'F'
 
 
+def get_birth_date(number):
+    """Determine and return the birth date."""
+    number = compact(number)
+    day = int(number[0:2])
+    month = int(number[2:4])
+    year = int(number[4:6])
+    individual_digits = int(number[6:9])
+    # Raise a more useful exception for FH numbers
+    if day >= 80:
+        raise InvalidComponent(
+            'This number is an FH-number, and does not contain birth date 
information by design.')
+    # Correct the birth day for D-numbers. These have a modified first digit.
+    # https://no.wikipedia.org/wiki/F%C3%B8dselsnummer#D-nummer
+    if day > 40:
+        day -= 40
+    # Correct the birth month for H-numbers. These have a modified third digit.
+    # https://no.wikipedia.org/wiki/F%C3%B8dselsnummer#H-nummer
+    if month > 40:
+        month -= 40
+    if individual_digits < 500:
+        year += 1900
+    elif 500 <= individual_digits < 750 and year >= 54:
+        year += 1800
+    elif 500 <= individual_digits < 1000 and year < 40:
+        year += 2000
+    elif 900 <= individual_digits < 1000 and year >= 40:
+        year += 1900
+    else:
+        # The birth century falls outside all defined ranges.
+        raise InvalidComponent('The birthdate century cannot be determined')
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent('The number does not contain valid birth date 
information.')
+
+
 def validate(number):
     """Check if the number is a valid birth number."""
     number = compact(number)
@@ -83,6 +122,9 @@ def validate(number):
         raise InvalidChecksum()
     if number[-1] != calc_check_digit2(number):
         raise InvalidChecksum()
+    if get_birth_date(number) > datetime.date.today():
+        raise InvalidComponent(
+            'The birth date information is valid, but this person has not been 
born yet.')
     return number
 
 
diff --git a/tests/test_no_fodselsnummer.doctest 
b/tests/test_no_fodselsnummer.doctest
index abec35e..bca21cf 100644
--- a/tests/test_no_fodselsnummer.doctest
+++ b/tests/test_no_fodselsnummer.doctest
@@ -2,6 +2,7 @@ test_no_fodselsnummer.doctest - more detailed doctests for 
stdnum.no.fodselsnumm
 
 Copyright (C) 2018 Ilya Vihtinsky
 Copyright (C) 2018 Arthur de Jong
+Copyright (C) 2020 Leon Sandøy
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -40,13 +41,13 @@ These numbers should be detected as male or female.
 
 The last two check digits are validated independently of each other.
 
->>> fodselsnummer.is_valid('42485176432')
+>>> fodselsnummer.is_valid('26111593816')
 True
->>> fodselsnummer.is_valid('42485176431')  # only change last digit
+>>> fodselsnummer.is_valid('26111593817')  # only change last digit
 False
->>> fodselsnummer.is_valid('42485176412')  # only change first digit
+>>> fodselsnummer.is_valid('26111593826')  # only change first digit
 False
->>> fodselsnummer.is_valid('42485176416')  # change first, correct second
+>>> fodselsnummer.is_valid('26111593875')  # change first, correct second
 False
 
 
@@ -62,50 +63,56 @@ Traceback (most recent call last):
 InvalidFormat: ...
 
 
-These have been found online and should all be valid numbers.
+The birth date can be extracted from the number:
+
+>>> fodselsnummer.get_birth_date('11111598403')
+datetime.date(2015, 11, 11)
+>>> fodselsnummer.get_birth_date('151086-95088')
+datetime.date(1986, 10, 15)
+>>> fodselsnummer.get_birth_date('180615 92527')
+datetime.date(2015, 6, 18)
+>>> fodselsnummer.get_birth_date('10 04 87 44 732')
+datetime.date(1987, 4, 10)
+>>> fodselsnummer.get_birth_date('13-04-99-58441')
+datetime.date(1899, 4, 13)
+
+
+Invalid dates are rejected:
+
+>>> fodselsnummer.validate('19575770838')
+Traceback (most recent call last):
+  ...
+InvalidComponent: The number does not contain valid birth date information.
+>>> fodselsnummer.get_birth_date('45014054018')
+Traceback (most recent call last):
+  ...
+InvalidComponent: The birthdate century cannot be determined
+>>> fodselsnummer.get_birth_date('82314251342')
+Traceback (most recent call last):
+  ...
+InvalidComponent: This number is an FH-number, and does not contain birth date 
information by design.
+>>> fodselsnummer.validate('19102270846')
+Traceback (most recent call last):
+       ...
+InvalidComponent: The birth date information is valid, but this person has not 
been born yet.
+
+
+These should all be valid numbers.
 
 >>> numbers = '''
 ...
-... 11027794191
-... 11051996811
-... 19575770838
-... 21918484865
-... 24396859900
-... 27213364885
-... 27389446152
-... 30383131118
-... 30777674125
-... 31042639152
-... 34831582121
-... 39043009846
-... 40070897972
-... 40673759612
-... 42115114470
-... 42485176432
-... 42957044500
-... 44207789809
-... 45014054018
-... 46929323343
-... 50067834221
-... 56403643756
-... 56653047547
-... 63282310041
-... 68413152112
-... 70031073454
-... 70341666064
-... 70624830529
-... 71494457037
-... 71946503120
-... 75442702381
-... 79189404641
-... 79318270827
-... 82938389280
-... 83814827871
-... 89829529360
-... 92782833709
-... 95700625908
-... 96517753502
-... 98576936818
+... 100487 45526
+... 10048744732
+... 10048745364
+... 111115984-03
+... 151086-95088
+... 18-06-15-92527
+... 231140-38547
+... 23114048690
+... 26 11 15 940 14
+... 26111593816
+... 26111594286
+... 26111594448
 ...
 ... '''
 >>> [x for x in numbers.splitlines() if x and not fodselsnummer.is_valid(x)]

-----------------------------------------------------------------------

Summary of changes:
 stdnum/no/fodselsnummer.py          | 56 ++++++++++++++++++---
 tests/test_no_fodselsnummer.doctest | 97 ++++++++++++++++++++-----------------
 2 files changed, 101 insertions(+), 52 deletions(-)


hooks/post-receive
-- 
python-stdnum