From 91a42e6cda2809516e1e676066da5db3d4f066ec Mon Sep 17 00:00:00 2001 From: Mika Sundland Date: Mon, 9 Dec 2019 16:00:53 +0100 Subject: [PATCH 1/2] Add extension method for generating Norwegian national identity numbers. --- README.md | 2 + .../ExtensionTests/NorwegianExtensionTest.cs | 110 ++++++++++++++++ .../Extensions/Norway/ExtensionsForNorway.cs | 119 ++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 Source/Bogus.Tests/ExtensionTests/NorwegianExtensionTest.cs create mode 100644 Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs diff --git a/README.md b/README.md index 1bd283ef..30d37323 100644 --- a/README.md +++ b/README.md @@ -483,6 +483,8 @@ public void Using_FakerT_Inheritance() * **`using Bogus.Extensions.Italy;`** * `Bogus.Person.CodiceFiscale()` - Codice Fiscale * `Bogus.DataSets.Finance.CodiceFiscale()` - Codice Fiscale +* **`using Bogus.Extensions.Norway;`** + * `Bogus.Person.Fødselsnummer()` - Norwegian national identity number * **`using Bogus.Extensions.Portugal;`** * `Bogus.Person.Nif()` - Número de Identificação Fiscal (NIF) * `Bogus.DataSets.Company.Nipc()` - Número de Identificação de Pessoa Colectiva (NIPC) diff --git a/Source/Bogus.Tests/ExtensionTests/NorwegianExtensionTest.cs b/Source/Bogus.Tests/ExtensionTests/NorwegianExtensionTest.cs new file mode 100644 index 00000000..53ea97f8 --- /dev/null +++ b/Source/Bogus.Tests/ExtensionTests/NorwegianExtensionTest.cs @@ -0,0 +1,110 @@ +using Bogus.DataSets; +using Bogus.Extensions.Norway; +using FluentAssertions; +using Xunit; + +namespace Bogus.Tests.ExtensionTests +{ + public class NorwegianExtensionTest : SeededTest + { + private void IsLegalIndividualNumber(int readIndNo, int birthYear, Person p) + { + // Check that birth year is in the correct range given individual number. + if (0 <= readIndNo && readIndNo <= 499) + { + birthYear.Should().BeInRange(0, 99); + } + else if (750 <= readIndNo && readIndNo <= 999) + { + birthYear.Should().BeInRange(0, 39); + } + else if (500 <= readIndNo && readIndNo <= 749) + { + if (0 <= birthYear && birthYear <= 39) + { + birthYear.Should().BeInRange(0, 39); + } + else + { + birthYear.Should().BeInRange(54, 99); + } + } + + // Check odd/even individual number given gender. + if (p.Gender == Name.Gender.Female) + { + (readIndNo % 2 == 0).Should().BeTrue(); + } + else + { + (readIndNo % 2 == 0).Should().BeFalse(); + } + } + + private void IsLegalChecksum(string readFødselsnummer) + { + string readCs = readFødselsnummer.Substring(9, 2); + + int d1 = int.Parse(readFødselsnummer.Substring(0, 1)); + int d2 = int.Parse(readFødselsnummer.Substring(1, 1)); + int m1 = int.Parse(readFødselsnummer.Substring(2, 1)); + int m2 = int.Parse(readFødselsnummer.Substring(3, 1)); + int y1 = int.Parse(readFødselsnummer.Substring(4, 1)); + int y2 = int.Parse(readFødselsnummer.Substring(5, 1)); + int i1 = int.Parse(readFødselsnummer.Substring(6, 1)); + int i2 = int.Parse(readFødselsnummer.Substring(7, 1)); + int i3 = int.Parse(readFødselsnummer.Substring(8, 1)); + + int cs1 = 11 - (((3 * d1) + (7 * d2) + (6 * m1) + (1 * m2) + (8 * y1) + (9 * y2) + (4 * i1) + (5 * i2) + (2 * i3)) % 11); + int cs2 = 11 - (((5 * d1) + (4 * d2) + (3 * m1) + (2 * m2) + (7 * y1) + (6 * y2) + (5 * i1) + (4 * i2) + (3 * i3) + (2 * cs1)) % 11); + + if (cs1 == 11) + { + cs1 = 0; + } + + if (cs2 == 11) + { + cs2 = 0; + } + + $"{cs1}{cs2}".Should().Be(readCs); + } + + private void IsLegalFødselsnummer(string readFødselsnummer, Person p) + { + readFødselsnummer.Should().HaveLength(11); + + int birthYear = int.Parse(readFødselsnummer.Substring(4, 2)); + int indNo = int.Parse(readFødselsnummer.Substring(6, 3)); + + IsLegalIndividualNumber(indNo, birthYear, p); + IsLegalChecksum(readFødselsnummer); + } + + [Fact] + public void can_create_norwegian_fødselsnummer() + { + var f = new Faker("nb_NO"); + var person = f.Person; + + string fødselsnummer = person.Fødselsnummer(); + + IsLegalFødselsnummer(fødselsnummer, person); + } + + [Fact] + public void can_create_correct_checksum_1() + { + // Test fødselsnummer from DSF. + IsLegalChecksum("31080700442"); + } + + [Fact] + public void can_create_correct_checksum_2() + { + // Test fødselsnummer from DSF. + IsLegalChecksum("10050050489"); + } + } +} diff --git a/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs new file mode 100644 index 00000000..47f445e4 --- /dev/null +++ b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs @@ -0,0 +1,119 @@ +using System; + +namespace Bogus.Extensions.Norway +{ + /// + /// API extensions specific for a geographical location. + /// + public static class ExtensionsForNorway + { + /// + /// Norwegian national identity number (fødselsnummer) + /// + public static string Fødselsnummer(this Person p) + { + const string Key = nameof(ExtensionsForNorway) + "Fødselsnummer"; + if (p.context.ContainsKey(Key)) + { + return p.context[Key] as string; + } + + /* + DDMMYYXXXCC + | | | | |--> Checksum + | | | | + | | | | + | | | |-----> Individual number + | | |-------> Year (last two digits) + | |---------> Month + |-----------> Day + + The individual number has to be even for women and odd for men. + + The checksum is calculated with a modulo checksum algorithm. + If either of the checksum numbers are 10, the fødselsnummer gets + rejected, and a new individual number has to be generated. + + https://www.skatteetaten.no/en/person/national-registry/birth-and-name-selection/children-born-in-norway/national-id-number/ + */ + + var r = p.Random; + string birthDate = $"{p.DateOfBirth:ddMMyy}"; + + string individualNumber; + string checksum; + bool isOkChecksum; + + do + { + individualNumber = GenerateIndividualNumber(r, p.Gender, p.DateOfBirth.Year); + isOkChecksum = GenerateChecksum(birthDate, individualNumber, out checksum); + } while (!isOkChecksum); + + string final = $"{p.DateOfBirth:ddMMyy}{individualNumber}{checksum}"; + + p.context[Key] = final; + return final; + } + + private static string GenerateIndividualNumber(Randomizer r, DataSets.Name.Gender gender, int year) + { + int from; + int to; + + if (1854 <= year && year <= 1899) + { + from = 500; + to = 749; + } + else if (1900 <= year && year <= 1999) + { + from = 0; + to = 499; + } + else if (2000 <= year && year <= 2039) + { + from = 500; + to = 999; + } + else + { + throw new ArgumentOutOfRangeException(nameof(year), $"{nameof(year)} must be between 1854 and 2039."); + } + + int individualNumber = gender == DataSets.Name.Gender.Female ? r.Even(from, to) : r.Odd(from, to); + + return individualNumber.ToString("D3"); + } + + private static bool GenerateChecksum(string birthDate, string individualNumber, out string checksum) + { + int d1 = int.Parse(birthDate.Substring(0, 1)); + int d2 = int.Parse(birthDate.Substring(1, 1)); + int m1 = int.Parse(birthDate.Substring(2, 1)); + int m2 = int.Parse(birthDate.Substring(3, 1)); + int y1 = int.Parse(birthDate.Substring(4, 1)); + int y2 = int.Parse(birthDate.Substring(5, 1)); + int i1 = int.Parse(individualNumber.Substring(0, 1)); + int i2 = int.Parse(individualNumber.Substring(1, 1)); + int i3 = int.Parse(individualNumber.Substring(2, 1)); + + int cs1 = 11 - (((3 * d1) + (7 * d2) + (6 * m1) + (1 * m2) + (8 * y1) + (9 * y2) + (4 * i1) + (5 * i2) + (2 * i3)) % 11); + int cs2 = 11 - (((5 * d1) + (4 * d2) + (3 * m1) + (2 * m2) + (7 * y1) + (6 * y2) + (5 * i1) + (4 * i2) + (3 * i3) + (2 * cs1)) % 11); + + if (cs1 == 11) + { + cs1 = 0; + } + + if (cs2 == 11) + { + cs2 = 0; + } + + checksum = $"{cs1}{cs2}"; + + return cs1 < 10 && cs2 < 10; + } + } +} \ No newline at end of file From 5bb038b7c0a417a5f1a50538b0bdfdfa02f2cc71 Mon Sep 17 00:00:00 2001 From: bchavez Date: Mon, 9 Dec 2019 22:15:27 -0800 Subject: [PATCH 2/2] Add more research links on norwegian fodselsnummer. --- Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs index 47f445e4..eed57228 100644 --- a/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs +++ b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs @@ -35,6 +35,12 @@ public static string Fødselsnummer(this Person p) rejected, and a new individual number has to be generated. https://www.skatteetaten.no/en/person/national-registry/birth-and-name-selection/children-born-in-norway/national-id-number/ + + https://nn.wikipedia.org/wiki/F%C3%B8dselsnummer + + https://github.com/deegane/NINTool/blob/master/backend/src/main/java/com/nin/validation/NorwegianNinValidator.kt + + https://github.com/magnuswatn/fodselsnummer/blob/master/fodselsnummer.py */ var r = p.Random;