Skip to content

Commit b7ee636

Browse files
committed
Validate Finnish identity code birth dates
1 parent 6ffd862 commit b7ee636

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

‎src/validators/i18n/fi.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Finland."""
22

33
# standard
4+
from datetime import date
45
from functools import lru_cache
56
import re
67

@@ -22,7 +23,7 @@ def _ssn_pattern(ssn_check_marks: str):
2223
(?P<date>(0[1-9]|[1-2]\d|3[01])
2324
(0[1-9]|1[012])
2425
(\d{{2}}))
25-
[ABCDEFYXWVU+-]
26+
(?P<century>[ABCDEFYXWVU+-])
2627
(?P<serial>(\d{{3}}))
2728
(?P<checksum>[{check_marks}])$""".format(check_marks=ssn_check_marks),
2829
re.VERBOSE,
@@ -95,6 +96,9 @@ def fi_ssn(value: str, /, *, allow_temporal_ssn: bool = True):
9596
if not (result := re.fullmatch(_ssn_pattern(ssn_check_marks), value)):
9697
return False
9798
gd = result.groupdict()
99+
century = 1800 if gd["century"] == "+" else 2000 if gd["century"] in "ABCDEF" else 1900
100+
birth_date = gd["date"]
101+
date(century + int(birth_date[4:]), int(birth_date[2:4]), int(birth_date[:2]))
98102
checksum = int(gd["date"] + gd["serial"])
99103
return (
100104
int(gd["serial"]) >= 2

‎tests/i18n/test_fi.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,26 @@ def test_returns_failed_validation_on_invalid_ssn(value: str):
7979
def test_returns_failed_validation_on_temporal_ssn_when_not_allowed():
8080
"""Test returns failed validation on temporal-ssn when not allowed."""
8181
assert isinstance(fi_ssn("010190-900P", allow_temporal_ssn=False), ValidationError)
82+
83+
84+
@pytest.mark.parametrize(
85+
("birth_date", "century", "valid"),
86+
[
87+
("310299", "-", False),
88+
("310499", "-", False),
89+
("290201", "A", False),
90+
("290200", "+", False),
91+
("290200", "-", False),
92+
("290200", "Y", False),
93+
("290200", "A", True),
94+
("290200", "F", True),
95+
("290204", "-", True),
96+
("290204", "U", True),
97+
],
98+
)
99+
def test_ssn_validates_birth_date_in_its_century(birth_date: str, century: str, valid: bool):
100+
"""Reject impossible birth dates even when the control character is correct."""
101+
serial = "010"
102+
checksum = "0123456789ABCDEFHJKLMNPRSTUVWXY"[int(birth_date + serial) % 31]
103+
value = birth_date + century + serial + checksum
104+
assert bool(fi_ssn(value)) is valid

0 commit comments

Comments
 (0)