Last active
March 15, 2022 09:10
-
-
Save kgilles/f29df5cc5af25d20240941a0a88aef53 to your computer and use it in GitHub Desktop.
Generate Random Finnish, Norwegian and Swedish Personal Identification Numbers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOTE: Gender is something these countries use to calculate the serial/individual number(making them even or odd) | |
# It is not taken into consideration in this snippet but the generated numbers are still valid | |
require 'date' | |
age = 21 # Whatever you want it to be | |
# Randomize month and day equal to, or lower, than the current month | |
# to make sure the generated birth month and day has occurred in <current_year> | |
t = Time.new | |
year = t.year - age | |
month = t.month - Random.rand(1..t.month) + 1 | |
day = t.day - Random.rand(1..t.day) + 1 | |
# Make sure we don't end up with Feb 29th on a non-leap year(or other invalid dates) | |
# This will happen extremely rarely, but better safe than sorry | |
while not Date.valid_date?(year, month, day) | |
day = t.day - Random.rand(1..t.day) + 1 | |
end | |
birthdate = Date.new(year, month, day) | |
# ================================ | |
# Generate Finnish Personal Number | |
# ================================ | |
date = birthdate.strftime('%d%m%y') | |
individual_number = format('%03d', 2 + rand(899)) # 002-899 | |
# The checksum is calculated as the remainder of the date + individual number divided by 31 | |
# If the remainder is > 9 then the checksum is grabbed from a given character string with the remainder as index | |
remainder = "#{date}#{individual_number}".to_i % 31 | |
if remainder < 10 | |
fi_personal_number = "#{date}#{individual_number}#{remainder}" | |
else | |
char_string = '0123456789ABCDEFHJKLMNPRSTUVWXY' | |
checksum = char_string[remainder] | |
fi_personal_number = "#{date}#{individual_number}#{checksum}" | |
end | |
puts "Finnish: #{fi_personal_number}" | |
# ================================== | |
# Generate Norwegian Personal Number | |
# ================================== | |
def weigh_numbers(number, weight) | |
weighted = 0 | |
number.split('').each_with_index do |digit, indx| | |
weighted += (digit.to_i * weight[indx].to_i) | |
end | |
weighted | |
end | |
date = birthdate.strftime('%d%m%y') | |
checksum1 = 10 | |
checksum2 = 10 | |
while checksum1 == 10 || checksum2 > 9 | |
if year >= 1900 && year <= 1999 | |
individual_number = format('%03d', rand(499)) # 000-499 | |
elsif year >= 2000 && year <= 2039 # 2039 is the current limit | |
individual_number = format('%03d', 500 + rand(999)) # 500-999 | |
end | |
# The nine digits we have so far are weighted to calculate the 10th digit | |
weighted = weigh_numbers("#{date}#{individual_number}", '376189452') | |
checksum1 = 11 - (weighted % 11) | |
checksum1 = 0 if checksum1 == 11 | |
# Then, the now ten digits we have are weighted to calculate the 11th digit | |
weighted = weigh_numbers("#{date}#{individual_number}#{checksum1}", '5432765432') | |
checksum2 = 11 - (weighted % 11) | |
end | |
puts "Norwegian: #{date}#{individual_number}#{checksum1}#{checksum2}" | |
# ================================ | |
# Generate Swedish Personal Number | |
# ================================ | |
date = birthdate.strftime('%y%m%d') # If you want the 4-digit year you'll have to convert it after the calculactions | |
serial = format('%03d', 1 + rand(999)) # 001-999 | |
# To calculate the checksum we start with multiplying every other number with 2 | |
digits = [] | |
"#{date}#{serial}".split('').each_with_index do |number, indx| | |
product = number.to_i * (2 - indx % 2) | |
# "a two digit product, such as 16, would be converted to 1 + 6" | |
product = product.to_s[0].to_i + product.to_s[1].to_i if product.to_s.length == 2 | |
digits << product | |
end | |
# Add all products together into one sum | |
sum = digits.inject(0) { |prv, nxt| prv + nxt } | |
# Subtract the last digit in the sum from 10 to get the checksum | |
# If the last digit is 0, the checksum is automatically 0 | |
checksum = 10 - sum % 10 | |
checksum = 0 if checksum == 10 | |
puts "Swedish: #{date}#{serial}#{checksum}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment