@@ -117,9 +117,9 @@ export function validateNationalIdentityNumber(
117117 return false ;
118118 }
119119
120- const [ _ , century , year , month , day , separator , rest ] = match ;
120+ const [ _ , centuryStr , yearStr , monthStr , dayStr , separator , rest ] = match ;
121121
122- if ( century && options . format === 'short' ) {
122+ if ( centuryStr && options . format === 'short' ) {
123123 return false ;
124124 }
125125
@@ -128,38 +128,49 @@ export function validateNationalIdentityNumber(
128128 }
129129
130130 // when verifying the value, we must always use the short format, discaring the century
131- // if included it would generate a different checksum
132- const isValid = mod10 ( `${ year } ${ month } ${ day } ${ rest } ` ) ;
131+ // if we include the century it would generate a different checksum
132+ const isValid = mod10 ( `${ yearStr } ${ monthStr } ${ dayStr } ${ rest } ` ) ;
133133 if ( ! isValid ) {
134134 return false ;
135135 }
136136
137- // // this allows us to handle both YYYYMMDD and YYMMDD when extracting the date
138- // const offset = isLongFormat ? 2 : 0;
139- // // copy/inspiration from NAV https://github.com/navikt/fnrvalidator/blob/77e57f0bc8e3570ddc2f0a94558c58d0f7259fe0/src/validator.ts#L108
140- // let year = Number(value.substring(0, 2 + offset));
141- // const month = Number(value.substring(2 + offset, 4 + offset));
142- // let day = Number(value.substring(4 + offset, 6 + offset));
143- //
144-
145- const month2 = Number ( month ) ;
146- let day2 = Number ( day ) ;
147- let year2 = Number ( century ? century + year : year ) ;
148- // for a samordningsnummer the day is increased by 60. Eg the 31st of a month would be 91, or the 3rd would be 63.
149- // thus we need to subtract 60 to get the correct day of the month
150- if ( day2 > 60 ) {
151- day2 = day2 - 60 ;
137+ let year = 0 ;
138+ switch ( true ) {
139+ // if we have the long format version, we already have the full year
140+ case centuryStr :
141+ year = Number ( centuryStr + yearStr ) ;
142+ break ;
143+ // otherwise, we can use the separator to determine the century of the personnummer
144+ // if the separator is '+', the person is over a 100 years old
145+ // we can then get
146+ case separator : {
147+ const date = new Date ( ) ;
148+ const baseYear =
149+ separator === '+' ? date . getUTCFullYear ( ) - 100 : date . getUTCFullYear ( ) ;
150+ year = baseYear - ( ( baseYear - yearStr ) % 100 ) ;
151+ break ;
152+ }
153+ // if it's the short format, without a separator, we need to special handle the year for the date validation.
154+ // 1900 isn't a leap year, but 2000 is. Since JS two digits years to the Date constructor is an offset from the year 1900
155+ // we need to special handle that case. For other cases it doesn't really matter if the year is 1925 or 2025.
156+ case yearStr === '00' :
157+ year = 2000 ;
158+ break ;
159+ // short version without separator
160+ default :
161+ year = Number ( yearStr ) ;
152162 }
153163
154- // 1900 isn't a leap year, but 2000 is. Since JS two digits years to the Date constructor is an offset from the year 1900
155- // we need to special handle that case. For other cases it doesn't really matter if the year is 1925 or 2025.
156- if ( ! separator && ! century && year === '00' ) {
157- year2 = 2000 ;
158- } else if ( century ) {
159- year2 = Number ( century + year ) ;
164+ const month = Number ( monthStr ) ;
165+
166+ let day = Number ( dayStr ) ;
167+ // for a samordningsnummer the day is increased by 60. Eg the 31st of a month would be 91, or the 3rd would be 63.
168+ // thus we need to subtract 60 to get the correct day of the month
169+ if ( day > 60 ) {
170+ day = day - 60 ;
160171 }
161172
162- return isValidDate ( year2 , month2 , day2 ) ;
173+ return isValidDate ( year , month , day ) ;
163174}
164175
165176// just reexport the no method for API feature parity
0 commit comments