Skip to content

Instantly share code, notes, and snippets.

@puumuki
Last active March 4, 2023 19:07
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save puumuki/11172310 to your computer and use it in GitHub Desktop.
python - henkilotunnus - Finnish social security number generator, validator and information extractor
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#The MIT License (MIT)
#Copyright (c) 2014 Teemu Puukko
#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 re
import argparse, sys, os
from calendar import monthrange
from random import randint
import pygtk, gtk
pygtk.require('2.0')
CHECK_KEYS = "0123456789ABCDEFHJKLMNPRSTUVWXY"
CENTURIES = {'18':'+','19':'-','20':'A'}
def create_hetu(start=1800, end=2014):
year = randint(start, end)
month = randint(1, 12)
day = randint(1, monthrange(year, month)[1])
century_sep = CENTURIES[str(year)[0:2]]
order_num = randint(2, 889)
check_number = "%02d%02d%s%03d" % (day, month, str(year)[0:2],
order_num)
check_number_index = int(check_number)%31
key = CHECK_KEYS[check_number_index]
return "%02d%02d%s%s%03d%s" % (day, month, str(year)[0:2],
century_sep, order_num, key)
def create_argparser():
parser = argparse.ArgumentParser(description='Generate or validate ')
parser.add_argument('-v', '--validate', dest='validate')
parser.add_argument('-i', '--info', dest='info')
parser.add_argument('-g', '--generate', dest='generate', action='store_true')
parser.add_argument('-c', '--clipboard', dest='clipboard', action='store_true')
parser.add_argument('-s', '--startyear', type=int, default=1800, dest='start')
parser.add_argument('-e', '--endyear', type=int, default=2099, dest='end')
return parser
def parse_information(hetu):
KEYS_CENTURIES = dict(zip(CENTURIES.values(),CENTURIES.keys()))
return {
"birth_day": hetu[0:2],
"birth_month": hetu[2:4],
"birth_year": KEYS_CENTURIES[hetu[6]] + hetu[4:6],
"order_number": hetu[7:10],
"is_man": int(hetu[7:10]) % 2 != 0
}
def print_info(hetu):
info = parse_information(hetu)
print("Hetu: %s" %(info.get("hetu"),) )
print("Order number: %s" % (info.get("order_number"),))
print("Birth time: %s.%s.%s" % (info.get("birth_day"), info.get("birth_month"), info.get("birth_year")))
print("Gender: %s " % ('man' if info.get("is_man") else 'female',))
def check_number(hetu):
num = hetu[0:6] + hetu[7:10]
return CHECK_KEYS[int(num) % 31] == hetu[-1]
def validate(hetu):
regexp = "^\d{6}[+-A]\d{3}[a-zA-Z0-9]$"
match = re.search(regexp, hetu)
if match != None and check_number(hetu):
print("Annettu henkilötunnus [%s] on validi." % (hetu,))
else:
print("Annettu henkilötunnus [%s] on epävalidi." % (hetu,))
def in_range(value, minimum, maximum):
return value >= minimum and value <= maximum
def copy_to_clipboard(value):
clipboard = gtk.clipboard_get()
clipboard.set_text(value)
clipboard.store()
def main(argv=sys.argv):
argparser = create_argparser()
if len(argv) <= 1:
hetu = create_hetu(1900, 2000)
copy_to_clipboard(hetu)
print(hetu)
args = argparser.parse_args(argv[1:])
if not in_range(args.start, 1800, 2099):
print("Start year have to be between years 1800 - 2099, given year was %s " % args.start)
return;
if not in_range(args.end, 1800, 2099):
print("End year have to be between years 1800 - 2099, given year was %s " % args.end)
return;
if args.info:
print_info(args.info)
if args.validate:
validate(args.validate)
if args.generate:
hetu = create_hetu(args.start, args.end)
if args.clipboard:
copy_to_clipboard(hetu)
print(hetu)
if __name__ == '__main__':
main()
@puumuki
Copy link
Author

puumuki commented Apr 22, 2014

To generate a finish social security number:
python henkilotunnus.py -g

Start and end years can be passed also, in default the start year is 1800 and the end year is 2014.
python henkilotunnus.py -s 1985 -e 2013 -g

To validate a social security number:
python henkilotunnus.py -v

To cather information from the social security number:
python henkilotunnus.py -i

@jouniheik
Copy link

Googletuksen eka osuma ja heti löyty sitä mitä hain. Kiitos tästä ja jos aikaa ja kiinnostusta löytyy niin voisit tehdä pythonilla myös y-tunnus ja ry numero generaattorit.

@epajarre
Copy link

Pythonia vasta katselemassa, mutta pitäisikö riveillä 49 ja 55 tuo vuosiviipale olla [2:4] (vai onko pythonin toiminta muuttunut jossain välissä)
(translation: should the year be sliced with [2:4] on lines 49 and 55 ?)

@ollipal
Copy link

ollipal commented Mar 4, 2023

Jos tarvetta yksittäisten hetujen generoinnin/validoinnin sijasta kaikkien mahdollisten hetujen läpikäymiseen, löytyy funktio siihen täältä: https://gist.github.com/ollipal/d27c33e011e45a6773870fea29279d91

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment