Module: SwedishPIN

Defined in:
lib/swedish_pin.rb,
lib/swedish_pin/errors.rb,
lib/swedish_pin/parser.rb,
lib/swedish_pin/version.rb,
lib/swedish_pin/generator.rb,
lib/swedish_pin/personnummer.rb

Overview

Validate, parse, and generate Swedish Personal Identity Numbers.

In Swedish these are called Personnummer. There is also a variant called “coordination number” (Samordningsnummer). Both of these are supported using the same API; see Personnummer#coordination_number?.

To get started, look at SwedishPIN.valid? and SwedishPIN.parse.

Defined Under Namespace

Classes: Generator, InvalidChecksum, InvalidDate, InvalidFormat, ParseError, Parser, Personnummer

Constant Summary collapse

VERSION =

The version number of this library.

"1.0.0"

Class Method Summary collapse

Class Method Details

.generate(birthday = nil, sequence_number = nil) ⇒ Object

Generates a valid Personnummer given certain inputs. Inputs not provided will be randomized.

This is mainly useful in order to generate test data or “Lorem Ipsum”-like values for use in demonstrations. Note that valid PINs might actually correspond to a real person, so don't use these generated PINs for anything that has a real effect.

Examples:

FactoryBot sequence

FactoryBot.define do
  sequence(:swedish_pin) do |n|
    # Will generate every PIN for a full day, then flip over to the next
    # day and start the sequence over.
    sequence_number = n % 1000
    date = Date.civil(1950, 1, 1) + (n / 1000)
    SwedishPIN.generate(date, sequence_number)
  end
end

Test data

user = User.new(name: "Jane Doe", pin: SwedishPIN.generate)

Parameters:

  • birthday (Date, Time, nil) (defaults to: nil)

    The birthday of the person the PIN identifies, or nil for a random date in the past.

  • sequence_number (String, Integer, nil) (defaults to: nil)

    The sequence number that correspond to the three digits after the birthday, or nil to pick a random one.

Raises:

  • (ArgumentError)

    if given numbers are outside of the valid range.


83
84
85
# File 'lib/swedish_pin.rb', line 83

def self.generate(birthday = nil, sequence_number = nil)
  Generator.new(birthday).generate(sequence_number)
end

.luhn(digits) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Implementation of Luhn algorithm.

Parameters:

  • digits (String)

    String of digits to calculate a control digit for.

Returns:

  • (Integer)

    Control digit.


93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/swedish_pin.rb', line 93

def self.luhn(digits)
  sum = 0

  (0...digits.length).each do |i|
    v = digits[i].to_i
    v *= 2 - (i % 2)
    if v > 9
      v -= 9
    end
    sum += v
  end

  ((sum.to_f / 10).ceil * 10 - sum.to_f).to_i
end

.parse(string, now = Time.now) ⇒ SwedishPIN::Personnummer

Parses a string of a personnummer and returns a Personnummer or raises an error.

Some numbers will have to relate to the current time in order to be parsed correctly. For example, the PIN 201231-… could be in many different years, including 1820, 1920, 2020, and so on. This library will guess that the year is in the most recent guess that are in the past. So during the year 2020 it would guess 2020, and in 2019 it will guess 1920.

Parameters:

  • string (String)

    The number to parse.

  • now (Time) (defaults to: Time.now)

    Provide a different “parse time” context.

Returns:

Raises:

  • (SwedishPIN::ParseError)

    When the provided string was not valid.

  • (ArgumentError)

    When the provided value was not a String.


36
37
38
39
40
41
42
43
44
45
# File 'lib/swedish_pin.rb', line 36

def self.parse(string, now = Time.now)
  result = Parser.new(string, now).parse
  Personnummer.new(
    year: result.fetch(:year),
    month: result.fetch(:month),
    day: result.fetch(:day),
    sequence_number: result.fetch(:sequence_number),
    control_digit: result.fetch(:control_digit)
  )
end

.valid?(string, now = Time.now) ⇒ true, false

Checks if a provided string is a valid Personnummer.

Parameters:

  • string (String)

    The number to parse.

  • now (Time) (defaults to: Time.now)

    Provide a different “parse time” context. See parse.

Returns:

  • (true, false)

    if the string was valid


52
53
54
55
56
# File 'lib/swedish_pin.rb', line 52

def self.valid?(string, now = Time.now)
  Parser.new(string, now).valid?
rescue ArgumentError
  false
end