class SwedishPIN::Personnummer

Represents a parsed and valid Personnummer or Samordningsnummer for a particular individual.

Determine if this is a Personnummer or a Samordningsnummer using {coordination_number?}.

@see en.wikipedia.org/wiki/Personal_identity_number_(Sweden) Personnummer on Wikipedia.

Attributes

control_digit[R]
day[R]
month[R]
sequence_number[R]
year[R]

Public Class Methods

new(year:, month:, day:, sequence_number:, control_digit:) click to toggle source

@api private @private

Initializes a new instance from specific values. Please consider using {SwedishPIN.generate} instead of you want custom instances.

# File lib/swedish_pin/personnummer.rb, line 41
def initialize(year:, month:, day:, sequence_number:, control_digit:)
  @year = year
  @month = month
  @coordination_number = day > 60
  @day = (day > 60 ? day - 60 : day)
  @sequence_number = sequence_number
  @control_digit = control_digit
end

Public Instance Methods

age(now = Time.now) click to toggle source

Returns the age of the person this personnummer represents, as an integer of years since birth.

Swedish age could be defined as such: A person will be 0 years old when born, and 1 12 months after that, on the same day or the day after in the case of leap years. This is the same way most western countries count age.

If the {birthday} is in the future, then 0 will be returned.

@param [Time, Date] now The current time. @return [Integer] Number of 12 month periods that have passed since the birthdate; 0 or more.

# File lib/swedish_pin/personnummer.rb, line 159
def age(now = Time.now)
  age = now.year - year - (birthday_passed_this_year?(now) ? 0 : 1)
  [0, age].max
end
birthday() click to toggle source

Return the birthday for the person that is represented by this Personnummer.

@return [Date] the date of birth

# File lib/swedish_pin/personnummer.rb, line 54
def birthday
  Date.civil(year, month, day)
end
coordination_number?() click to toggle source

Returns true if this number is a Samordningsnummer (coordination number). This is a number that is granted to non-Swedish citizens until the time that they become citizens.

Coordination numbers are identical to a PIN, except that the “day” component has 60 added to it (i.e. 28+60=88).

@note The {day} attribute will still return a valid date day, even for coordination numbers. @see sv.wikipedia.org/wiki/Samordningsnummer Samordningsnummer on Wikipedia (Swedish)

# File lib/swedish_pin/personnummer.rb, line 67
def coordination_number?
  @coordination_number
end
female?() click to toggle source

Returns true if the personnummer represents a person that is legally identified as female. @return [true, false]

# File lib/swedish_pin/personnummer.rb, line 174
def female?
  sequence_number.even?
end
format_long() click to toggle source

Formats the personnummer in the unofficial “12-digit” format that includes the century and doesn't change separator depending on when the number is supposed to be shown.

This format is being adopted in a lot of places in favor of the “10-digit” format ({format_short}), but as of 2020 it remains an unofficial format.

Format: yyyymmdd-nnnn

@see format_short

# File lib/swedish_pin/personnummer.rb, line 112
def format_long
  [
    format_date(true),
    "-",
    "%03d" % sequence_number,
    control_digit
  ].join("")
end
format_short(now = Time.now) click to toggle source

Formats the PIN in the official “10-digit” format. This is the “real” Personnummer string.

Format: yymmdd-nnnn or yymmdd+nnnn

The Personnummer specification says that starting from the year of a person's 100th birthday, the separator in their personnummer will change from a - into a +.

That means that every time you display a personnummer you also must consider the time of this action. Something that was read on date A and outputted on date B might not use the same string representation.

For this reason, the real personnummer is usually not what you want to store, only what you want to display in some cases.

This library recommends that you use {format_long} for storage.

@param [Time, Date] now The time when this personnummer is supposed to be displayed. @return [String] the formatted number @see format_long

# File lib/swedish_pin/personnummer.rb, line 92
def format_short(now = Time.now)
  [
    format_date(false),
    short_separator(now),
    "%03d" % sequence_number,
    control_digit
  ].join("")
end
male?() click to toggle source

Returns true if the personnummer represents a person that is legally identified as male. @return [true, false]

# File lib/swedish_pin/personnummer.rb, line 167
def male?
  sequence_number.odd?
end
to_s(length = 10, now = Time.now) click to toggle source

Formats the PIN into a String.

You can provide the desired length to get different formats.

@note The length isn't how long the resulting string will be as the

resulting string will also have a separator included. The formats are
colloquially called "10-digit" and "12-digit", which is why they are
referred to as "length" here.
10 or nil

{format_short}

12

{format_long}

@param [Integer, nil] length The desired format. @param [Time, Date] now The current time. Only used by {format_short}. @raise [ArgumentError] If not provided a valid length. @return [String] @see format_short @see format_long

# File lib/swedish_pin/personnummer.rb, line 139
def to_s(length = 10, now = Time.now)
  case length
  when 10 then format_short(now)
  when 12 then format_long
  else raise ArgumentError, "The only supported lengths are 10 or 12."
  end
end

Private Instance Methods

birthday_passed_this_year?(now) click to toggle source
# File lib/swedish_pin/personnummer.rb, line 201
def birthday_passed_this_year?(now)
  now.month > month || (now.month == month && now.day >= day)
end
format_date(include_century) click to toggle source
# File lib/swedish_pin/personnummer.rb, line 188
def format_date(include_century)
  [
    (include_century ? pad(year / 100) : nil),
    pad(year % 100),
    pad(month),
    pad(coordination_number? ? day + 60 : day)
  ].join("")
end
pad(num) click to toggle source
# File lib/swedish_pin/personnummer.rb, line 197
def pad(num)
  "%02d" % num
end
short_separator(now) click to toggle source
# File lib/swedish_pin/personnummer.rb, line 180
def short_separator(now)
  if year <= (now.year - 100)
    "+"
  else
    "-"
  end
end