Skip to content

Instantly share code, notes, and snippets.

@ollipal
Last active March 5, 2023 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ollipal/d27c33e011e45a6773870fea29279d91 to your computer and use it in GitHub Desktop.
Save ollipal/d27c33e011e45a6773870fea29279d91 to your computer and use it in GitHub Desktop.
Finnish personal identity code (henkilötunnus/hetu) generator in Python
"""
MIT License
Copyright (c) 2023 Olli Paloviita
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import datetime
from calendar import monthrange
from typing import Iterator
def hetus(
future_hetus: bool = False,
temporary_hetus: bool = False,
rare_hetus: bool = True,
) -> Iterator[str]:
"""Finnish personal identity code (henkilötunnus/hetu) generator
Yields Finnish personal identity codes one by one, starting from the most common
century symbols (-A+) and then the new century symbols introduced in 2023
(BCDEFYXVWU). Finnish personal identity codes are functionally very similar to
social security numbers. More info: https://fi.wikipedia.org/wiki/Henkil%C3%B6tunnus
Args:
future_hetus: Include future hetus that no one can have yet
temporary_hetus: Include temporary hetus with numbers 900-999
rare_hetus: Include hetus with rare century symbols (BCDEFYXVWU)
Returns:
Personal identity code string iterator
"""
years_since_2000 = datetime.date.today().year - 2000
def century_symbol_to_number(century_symbol):
if century_symbol in "+":
return 1800
elif century_symbol in "-YXWVU":
return 1900
elif century_symbol in "ABCDEF":
return 2000
def years_in_century(century_symbol):
return (
years_since_2000 + 1
if not future_hetus and century_symbol_to_number(century_symbol) == 2000
else 100
)
def days_in_month(century_symbol, year_short, month):
year = century_symbol_to_number(century_symbol) + year_short
return monthrange(year, month)[1]
for century_symbol in "-A+BCDEFYXVWU" if rare_hetus else "-A+":
for year_short in range(0, years_in_century(century_symbol)):
for month in range(1, 12 + 1):
for day in range(1, days_in_month(century_symbol, year_short, month) + 1):
date = f"{day:02}{month:02}{year_short:02}"
for num in range(2, 1000 if temporary_hetus else 900):
check = "0123456789ABCDEFHJKLMNPRSTUVWXY"[int(f"{date}{num:03}") % 31]
yield f"{date}{century_symbol}{num:03}{check}"
if __name__ == "__main__":
# print 276000000+ hetus
for hetu in hetus():
print(hetu)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment