Skip to content

Commit

Permalink
✨ Add property gender.
Browse files Browse the repository at this point in the history
  • Loading branch information
tancred committed Sep 30, 2024
1 parent 36c2798 commit 7e256d7
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ The Swedish Tax Agency ("Skatteverket" in Swedish) has a [description of the for

* When a person reaches the honorable age of 100 years, a plus sign (`+`) is used for the separator in their PNR, i.e., `yyMMdd+nnnc`.

* The last digit of the birth number
(i.e, the penultimate digit, you know, the one followed by the checksum)
reveals the gender of the person.
Even and odd numbers
denote females and males, respectively.

* Although the standard form of a PNR is the eleven character version described so far, other forms are also in common use. This library handles PNRs on the following forms. Note that the last two forms include the century, so no deduction is necessary, neither is the plus separator allowed; unsurprisingly these forms are popular, if not required in many apps and sites today.

* 10 characters: `yyMMddnnnc` (standard form but lacking separator)
Expand Down
12 changes: 11 additions & 1 deletion Sources/SwedishPNR/SwedishPNR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ public struct SwedishPNR {
public let normalized: String
public let birthDateComponents: DateComponents
public let birthDate: Date
public let gender: Gender

public func age(at reference: Date = Date()) -> Int {
return calculateAge(for: birthDate, at: reference, in: makeSwedishCalendar())
}

public enum Gender {
case female
case male
}
}


Expand Down Expand Up @@ -70,11 +76,15 @@ public struct Parser {

let bday = swedishCalendar.date(from: birthDateComponents)!

let penultimateDigitIdx = normalized.index(normalized.startIndex, offsetBy: 11)
let penultimateDigit = Int(normalized[penultimateDigitIdx..<normalized.index(after: penultimateDigitIdx)], radix: 10)!

return SwedishPNR(
input: String(input),
normalized: normalized,
birthDateComponents: birthDateComponents,
birthDate: bday)
birthDate: bday,
gender: penultimateDigit.isMultiple(of: 2) ? .female : .male)
}

private func deduceCenturyFromBirthDate(_ birthDate: DateComponents, _ reference: Date, _ isCentennial: Bool) throws -> DateComponents {
Expand Down
16 changes: 16 additions & 0 deletions Tests/SwedishPNRTests/SwedishPNRTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,22 @@ final class SwedishPNRTests: XCTestCase {
XCTAssertEqual(try SwedishPNR.parse(input: "10000101-0008", relative: ref).age(at: ref), 1122)
}

func testGender() throws {
let ref = formatterForSweden!.date(from: "2024-09-30")!

XCTAssertEqual(try SwedishPNR.parse(input: "240101-1008", relative: ref).gender, .female)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1024", relative: ref).gender, .female)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1040", relative: ref).gender, .female)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1065", relative: ref).gender, .female)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1081", relative: ref).gender, .female)

XCTAssertEqual(try SwedishPNR.parse(input: "240101-1016", relative: ref).gender, .male)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1032", relative: ref).gender, .male)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1057", relative: ref).gender, .male)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1073", relative: ref).gender, .male)
XCTAssertEqual(try SwedishPNR.parse(input: "240101-1099", relative: ref).gender, .male)
}

func testDefaultRefTime() throws {
let pnr = try SwedishPNR.parse(input: "20000101-0008")

Expand Down

0 comments on commit 7e256d7

Please sign in to comment.