Class: Henkilotunnus::Hetu

Inherits:
Object
  • Object
show all
Defined in:
lib/henkilotunnus/hetu.rb

Constant Summary collapse

GENDERS =
['female', 'male']
CENTURIES =
{ '+' => 1800, '-YXWVU' => 1900, 'ABCDEF' => 2000 }
NEW_CENTURY_SIGNS =
'YXWVUBCDEF'
PERSON_NUMBER_RANGE =
0..999
CHECKSUM_CHARS =
'0123456789ABCDEFHJKLMNPRSTUVWXY'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pin) ⇒ Hetu

Returns a new instance of Hetu.



30
31
32
# File 'lib/henkilotunnus/hetu.rb', line 30

def initialize(pin)
  @pin = format(pin || '')
end

Instance Attribute Details

#pinObject (readonly)

Returns the value of attribute pin.



28
29
30
# File 'lib/henkilotunnus/hetu.rb', line 28

def pin
  @pin
end

Class Method Details

.compute_checksum(raw_dob, person_number) ⇒ Object



24
25
26
# File 'lib/henkilotunnus/hetu.rb', line 24

def self.compute_checksum(raw_dob, person_number)
  CHECKSUM_CHARS[ (raw_dob + person_number).to_i % 31 ]
end

.generate(opts = {}) ⇒ Object



13
14
15
16
17
18
19
20
21
22
# File 'lib/henkilotunnus/hetu.rb', line 13

def self.generate(opts={})
  dob = opts.fetch(:date, Time.at(rand(Date.new(1800, 1, 1).to_time.to_i...Date.today.to_time.to_i)).to_date)
  raw_dob = dob.strftime("%d%m%y")
  person_number = opts.fetch(:person_number, rand(PERSON_NUMBER_RANGE)).to_s.rjust(3, "0")
  century_str = CENTURIES.key(dob.year - (dob.year % 100))
  sign_str_len = century_str.size - 1
  century_sign = century_str[rand(0..sign_str_len)]

  new(raw_dob + century_sign + person_number + compute_checksum(raw_dob, person_number))
end

.valid?(pin) ⇒ Boolean

Returns:

  • (Boolean)


9
10
11
# File 'lib/henkilotunnus/hetu.rb', line 9

def self.valid?(pin)
  new(pin).valid?
end

Instance Method Details

#ageObject



66
67
68
69
70
71
# File 'lib/henkilotunnus/hetu.rb', line 66

def age
  dob = date_of_birth
  # TODO: Should use EEST timezone for everything as Finland has only a single timezone (but with DST!).
  now = Time.now.utc.to_date
  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end

#centuryObject



81
82
83
84
85
86
87
# File 'lib/henkilotunnus/hetu.rb', line 81

def century
  CENTURIES.keys.each do |string|
    if string.include? century_sign
      return CENTURIES[string]
    end
  end
end

#century_signObject



38
39
40
# File 'lib/henkilotunnus/hetu.rb', line 38

def century_sign
  pin[6]
end

#checksumObject



89
90
91
# File 'lib/henkilotunnus/hetu.rb', line 89

def checksum
  pin[10]
end

#date_of_birthObject



73
74
75
76
77
78
79
# File 'lib/henkilotunnus/hetu.rb', line 73

def date_of_birth
  dob = raw_dob
  day = dob[0..1].to_i
  month = dob[2..3].to_i
  year = century + dob[4..5].to_i
  Date.new(year, month, day)
end

#female?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/henkilotunnus/hetu.rb', line 62

def female?
  gender == 'female'
end

#genderObject



46
47
48
49
50
51
52
# File 'lib/henkilotunnus/hetu.rb', line 46

def gender
  if gender_neutral?
    raise 'gender methods cannot be used with gender neutral identity codes. Use gender_neutral? to check.'
  else
    GENDERS[person_number.to_i % 2]
  end
end

#gender_neutral?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/henkilotunnus/hetu.rb', line 54

def gender_neutral?
  (NEW_CENTURY_SIGNS.include? century_sign) && (Time.now.year > 2026)
end

#male?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/henkilotunnus/hetu.rb', line 58

def male?
  gender == 'male'
end

#person_numberObject



42
43
44
# File 'lib/henkilotunnus/hetu.rb', line 42

def person_number
  pin[7..9]
end

#to_sObject



93
94
95
# File 'lib/henkilotunnus/hetu.rb', line 93

def to_s
  pin
end

#valid?Boolean

Returns:

  • (Boolean)


34
35
36
# File 'lib/henkilotunnus/hetu.rb', line 34

def valid?
  valid_format? && valid_checksum? && valid_person_number?
end