From 7dd4bf5818b982c80fd7f5e1c11b76d17b398e71 Mon Sep 17 00:00:00 2001 From: Fons te Winkel Date: Wed, 9 Jul 2025 12:58:42 +0200 Subject: [PATCH 1/4] Added Dutch BSN (like an SSN) in id_number.rb --- lib/faker/default/id_number.rb | 31 ++++++++++++++++++++++ test/faker/default/test_faker_id_number.rb | 12 +++++++++ 2 files changed, 43 insertions(+) diff --git a/lib/faker/default/id_number.rb b/lib/faker/default/id_number.rb index 6fc2248f01..311361f9a5 100644 --- a/lib/faker/default/id_number.rb +++ b/lib/faker/default/id_number.rb @@ -269,6 +269,23 @@ def danish_id_number(formatted: false, birthday: Faker::Date.birthday, gender: n ].join end + ## + # Produces a random Dutch social security number (Burger Service Nummer). + # + # @return [String] + # + # @example + # Faker::IdNumber.dutch_bsn #=> "697116694" + # + # @faker.version next + def dutch_bsn + bsn = '' + + bsn = Faker::Number.numerify('#########') until valid_dutch_bsn?(bsn) + + bsn + end + ## # Produces a random French social security number (INSEE number). # @@ -416,6 +433,20 @@ def danish_control_digits(birthday) end end + def valid_dutch_bsn?(bsn) + length = bsn.length + + # If it's not numeric, or not 8 or 9 digits long, it can never be a valid Dutch BSN. + return false unless /^\d{8,9}$/.match?(bsn) + + sum = 0 + bsn.chars.each_with_index do |c, i| + sum += Integer(c) * (i == length - 1 ? -1 : length - i) + end + + (sum % 11).zero? + end + def _translate(key) parse("id_number.#{key}") end diff --git a/test/faker/default/test_faker_id_number.rb b/test/faker/default/test_faker_id_number.rb index 12ceff83c7..ae8d764711 100644 --- a/test/faker/default/test_faker_id_number.rb +++ b/test/faker/default/test_faker_id_number.rb @@ -274,6 +274,12 @@ def test_danish_id_number_invalid_gender end end + def test_dutch_bsn + sample = @tester.dutch_bsn + + assert_dutch_bsn sample + end + private def south_african_id_number_to_date_of_birth_string(sample) @@ -287,4 +293,10 @@ def assert_valid_south_african_id_number(sample) assert_equal Faker::IdNumber::ZA_RACE_DIGIT, sample[11] assert Date.parse(south_african_id_number_to_date_of_birth_string(sample)) end + + def assert_dutch_bsn(sample) + assert_equal 9, sample.length + assert_kind_of String, sample + refute_nil Integer(sample) + end end From bc26ce8993766db65aa97f8ec7b6d625eb310758 Mon Sep 17 00:00:00 2001 From: Fons te Winkel Date: Wed, 9 Jul 2025 13:57:34 +0200 Subject: [PATCH 2/4] Added an example for generating a Dutch BSN in id_number.md --- Gemfile.lock | 1 + doc/default/id_number.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index a75e13a171..f18a114ba5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,6 +70,7 @@ GEM PLATFORMS arm64-darwin-22 + arm64-darwin-24 x86_64-linux DEPENDENCIES diff --git a/doc/default/id_number.md b/doc/default/id_number.md index 89216c0279..560833af6f 100644 --- a/doc/default/id_number.md +++ b/doc/default/id_number.md @@ -47,6 +47,9 @@ Faker::IdNumber.danish_id_number(formatted: true) #=> "050390-9980" Faker::IdNumber.danish_id_number(birthday: Date.new(1990, 3, 5)) #=> "050390-9980" Faker::IdNumber.danish_id_number(gender: :female) #=> "050390-9980" +# Generate a Dutch ID number (BSN) +Faker::IdNumber.dutch_bsn # => "365371960" + # Generate a valid French Social Security number (INSEE number) Faker::IdNumber.french_insee_number #=> "22510589696868" ``` From 4ed2ff4f3a97787c1daef3be0df5be27f673820b Mon Sep 17 00:00:00 2001 From: Fons te Winkel Date: Wed, 9 Jul 2025 14:05:53 +0200 Subject: [PATCH 3/4] Added an explanation for the validity check --- lib/faker/default/id_number.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/faker/default/id_number.rb b/lib/faker/default/id_number.rb index 311361f9a5..04f2e6f5c0 100644 --- a/lib/faker/default/id_number.rb +++ b/lib/faker/default/id_number.rb @@ -439,6 +439,7 @@ def valid_dutch_bsn?(bsn) # If it's not numeric, or not 8 or 9 digits long, it can never be a valid Dutch BSN. return false unless /^\d{8,9}$/.match?(bsn) + # It must match the BSN-variant of the "Elf Proef" (see https://nl.wikipedia.org/wiki/Elfproef#Burgerservicenummer ) sum = 0 bsn.chars.each_with_index do |c, i| sum += Integer(c) * (i == length - 1 ? -1 : length - i) From 6f9a02cc6c85946b67b74d6837d1fd9291001c8c Mon Sep 17 00:00:00 2001 From: Fons <40026023+Fonzie-byte@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:18:59 +0200 Subject: [PATCH 4/4] chore: Slightly improved the underlying code for dutch_bsn --- lib/faker/default/id_number.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/faker/default/id_number.rb b/lib/faker/default/id_number.rb index 04f2e6f5c0..7437d03f21 100644 --- a/lib/faker/default/id_number.rb +++ b/lib/faker/default/id_number.rb @@ -279,8 +279,6 @@ def danish_id_number(formatted: false, birthday: Faker::Date.birthday, gender: n # # @faker.version next def dutch_bsn - bsn = '' - bsn = Faker::Number.numerify('#########') until valid_dutch_bsn?(bsn) bsn @@ -434,15 +432,14 @@ def danish_control_digits(birthday) end def valid_dutch_bsn?(bsn) - length = bsn.length - # If it's not numeric, or not 8 or 9 digits long, it can never be a valid Dutch BSN. return false unless /^\d{8,9}$/.match?(bsn) # It must match the BSN-variant of the "Elf Proef" (see https://nl.wikipedia.org/wiki/Elfproef#Burgerservicenummer ) sum = 0 + length = bsn.length bsn.chars.each_with_index do |c, i| - sum += Integer(c) * (i == length - 1 ? -1 : length - i) + sum += c.to_i * (i == length - 1 ? -1 : length - i) end (sum % 11).zero?