php validera personnummer ÅÅÅÅMMDD-XXXX

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Maj 2009

php validera personnummer ÅÅÅÅMMDD-XXXX

ValidateSSN():

function ValidateSSN($pnr){ $pnr = trim($pnr); $rejection = NULL; $pnr = preg_replace("/[^0-9]/", "", $pnr); if (strlen($pnr)!=12){ if (strlen($pnr) == 10){ $pnr = "19".$pnr; } else{ $rejection = Rejections("3"); goto post; } } //echo $pnr; $pnr = substr($pnr, 2); if (checkPnr($pnr)) { $svar = "rätt"; if (strlen($pnr)!=13){ if (strlen($pnr) == 10){ $pnr = "19".substr($pnr,0,6)."-".substr($pnr,6,4); }elseif(strlen($pnr) == 12){ $pnr = substr($pnr,0,8)."-".substr($pnr,8,4); }else{ $rejection = Rejections("3"); goto post; } } } else { $svar = "fel"; //echo 'FEL!'; $rejection = Rejections("4"); } //} }

checkpnr():

function checkPnr($pnr) { /*if ( !preg_match("/^\d{8}\-\d{4}$/", $pnr) ) { return false; } $pnr = str_replace("-", "", $pnr);*/ $n = 2; for ($i=0; $i<9; $i++) { $tmp = $pnr[$i] * $n; ($tmp > 9) ? $sum += 1 + ($tmp % 10) : $sum += $tmp; ($n == 2) ? $n = 1 : $n = 2; } return !( ($sum + $pnr[9]) % 10); }

denna kod som jag länkat använder jag för att validera ÅÅMMDDXXXX, men nu vill jag validera ÅÅÅÅMMDD-XXXX.
normalt skickar jag in ÅÅÅÅMMDD-XXXX därför måste jag göra om allt innan jag skickar den till checkpnr(), men är inte alltid säkert att jag gör det. dock spelar det ingen roll.
jag vet att funktionen returnerar ÅÅÅÅMMDD-XXXX, men jag vill att den använder det, inte ÅÅMMDD-XXXX.
kan någon hjälpa mig med att antingen göra så den funkar eller hjälpa med en ny?

Min dator: Silent Base 600 | 1700X @ 3.9Ghz | MSI Gaming X 1080TI | RM750X | 512Gb M2 | 16Gb 3200mhz Ram | S34E790C @ 3440x1440
Tjejens dator: Define r4 | i5 3570k @ 4.2ghz | GTX Titan | 750w Supernova | 240gb SSD | 32gb ram
Citera/Tagga för svar!

Trädvy Permalänk
Webbinvecklare 🧔
Anton Fast
Plats
Stockholm
Registrerad
Okt 2010

@Christley:

Bara för att vara lite petig med namngivning så validering av SSN BÖR vara en faktiskt validering (aka ett korrekt personnummer) och inte bara "inga bokstäver"

Vidare, spalta upp ditt problem till en tydligare frågeställning och gör därefter din kod steg för steg fast i bara kommentarer, typ såhär

/** * Function to validateSSN (number only) and see if it's correct * * @param int $ssn * * @return bool */ function validateSSN($ssn) { $result = false; // Trim any spaces surrounding text // Regex pattern that checks if starting with 19/20 (or any), has 6 following digits, a (or no) dash "-" and 4 ending digits // Like: (19|20)?\d{6}-?{4} // Check if input matced regex return $result; }

Typ något sånt tänker jag,
den där regexen skrev jag i huvudet så inte säkert att den fungerar, men jag tror programmeringstekniken framgår.
Tydliggör vad du vill att funktionen ska göra, och överarbeta inte kod om du inte prompt måste.

Tex:
if (strlen($pnr)!=13){
Kollar ju bara om strängen inte är 13 tecken, det spelar ju inte roll om den är kortare eller längre, när den i själva verket vore helt felaktig om den vore längre, right?

Lycka till
(Vill du testa regexpatterns rekommenderar jag https://regex101.com/ om du inte har det i en IDE eller på annat håll)

Dator, MOBO: Asus X99-A, CPU: Intel I7 6800k (3.4GHz), GPU: Asus GTX 1080 Strix, RAM: 4x8GB Corsair Vengeance LPX 2400MHz, OS-HDD: Intel 750 PCIe 400GB, PSU: EVGA SuperNOVA G2 850W

Trädvy Permalänk
Medlem
Plats
Göteborg
Registrerad
Apr 2002

@Klorixx skall du validera noga bör du väl i så fall även räkna på kontrollsiffra?

CPU: i7 6700k + Fractal Design S24 GPU: ASUS GeForce GTX 1070 8GB DUAL OC RAM: Kingston 16GB 2133MHz CL13 MB: MSI GAMING M7 PSU: EVGA Supernova G2 850W, 80+ Gold SSD: Samsung SM951 256GB M.2 NVMe + Samsung EVO 850 250GB M.2 Chassi: Fractal Design S OS: W10 Pro Skrämar: Acer XB270HU + 2x Dell U2412M
NAS: Synology DS415+ (4x WD RED 6 TB) Console: Xbox One

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Maj 2009
Skrivet av Klorixx:

@Christley:

Bara för att vara lite petig med namngivning så validering av SSN BÖR vara en faktiskt validering (aka ett korrekt personnummer) och inte bara "inga bokstäver"

Vidare, spalta upp ditt problem till en tydligare frågeställning och gör därefter din kod steg för steg fast i bara kommentarer, typ såhär

Typ något sånt tänker jag,
den där regexen skrev jag i huvudet så inte säkert att den fungerar, men jag tror programmeringstekniken framgår.
Tydliggör vad du vill att funktionen ska göra, och överarbeta inte kod om du inte prompt måste.

Tex:
if (strlen($pnr)!=13){
Kollar ju bara om strängen inte är 13 tecken, det spelar ju inte roll om den är kortare eller längre, när den i själva verket vore helt felaktig om den vore längre, right?

Lycka till
(Vill du testa regexpatterns rekommenderar jag https://regex101.com/ om du inte har det i en IDE eller på annat håll)

koden har jag skrivit för 2 år sedan och då kunde jag knappt php, det är först nu jag återkommer till den.
och jag fattar inte regex överhuvudtaget. tycker det är onödigt klumpigt och svårt.
och saken är att jag inte kan spalta upp det i kommentarer. för då blir det bara
-> ta personnummer
-> räkna om det är korrekt.

om det följer med något, om det är för långt eller liknande så kommer den ändå aldrig validera korrekt så därför har jag aldrig brytt mig om att lägga till en validering. det är ändå bara jag som använder programmet.
och din validering är endast om det är korrekt uppbyggt. det räknar inte om det är ett korrekt personnummer. så 19900000-0000 hade gått igenom

Min dator: Silent Base 600 | 1700X @ 3.9Ghz | MSI Gaming X 1080TI | RM750X | 512Gb M2 | 16Gb 3200mhz Ram | S34E790C @ 3440x1440
Tjejens dator: Define r4 | i5 3570k @ 4.2ghz | GTX Titan | 750w Supernova | 240gb SSD | 32gb ram
Citera/Tagga för svar!

Trädvy Permalänk
Webbinvecklare 🧔
Anton Fast
Plats
Stockholm
Registrerad
Okt 2010
Skrivet av Tino:

@Klorixx skall du validera noga bör du väl i så fall även räkna på kontrollsiffra?

Absolut, det var precis det jag försökte komma fram till med mitt inlägg men insåg att det blev otydligt och kladdigt i koden.
Men jag menar att ska man "validera" ett personnummer bör det räknas ut att det är korrekt, medans detta bara validerar ett format.

Skrivet av Christley:

[...] för då blir det bara
-> ta personnummer
-> räkna om det är korrekt.
[...]

Och om dina kommentarer bara blir så skulle jag säga att det är där du bör träna isf. "lös problemet" är ju aldrig en lösning. Fortsätt hela tiden vidarutveckla. Läs högt för dig själv, blogga eller skriv på forum som nu (vet flertalet, inkl. jag själv som kommer på lösningen av att försöka tydligt beskriva ett problem).

Skrivet av Christley:

[...]din validering är endast om det är korrekt uppbyggt. det räknar inte om det är ett korrekt personnummer. så 19900000-0000 hade gått igenom

Yes, jag missade att du gjorde det i din kod, utan jag tolkade det mest som lite kluddrig kod (all kod utan kommentarer hoppar jag för det mesta över mycket i) som kollade att formatet var korrekt.

Min poäng med det hela är: tydliggör vad du vill ha gjort. Visa exempel. vad SKA fungera kontra vad ska INTE fungera.

Dator, MOBO: Asus X99-A, CPU: Intel I7 6800k (3.4GHz), GPU: Asus GTX 1080 Strix, RAM: 4x8GB Corsair Vengeance LPX 2400MHz, OS-HDD: Intel 750 PCIe 400GB, PSU: EVGA SuperNOVA G2 850W

Trädvy Permalänk
Medlem
Registrerad
Aug 2011
Skrivet av Christley:

koden har jag skrivit för 2 år sedan och då kunde jag knappt php, det är först nu jag återkommer till den.
och jag fattar inte regex överhuvudtaget. tycker det är onödigt klumpigt och svårt.
och saken är att jag inte kan spalta upp det i kommentarer. för då blir det bara
-> ta personnummer
-> räkna om det är korrekt.

om det följer med något, om det är för långt eller liknande så kommer den ändå aldrig validera korrekt så därför har jag aldrig brytt mig om att lägga till en validering. det är ändå bara jag som använder programmet.
och din validering är endast om det är korrekt uppbyggt. det räknar inte om det är ett korrekt personnummer. så 19900000-0000 hade gått igenom

^(19|20)?[0-9]{2}[01][0-9][0-3][0-9]-[0-9]{4}$

Inte provat, men tycker det borde fungera.

Trädvy Permalänk
Medlem
Plats
Göteborg
Registrerad
Jun 2011
Skrivet av kronwalled:

^(19|20)?[0-9]{2}[01][0-9][0-3][0-9]-[0-9]{4}$

Inte provat, men tycker det borde fungera.

Ditt regex godkänner månad 19 och dag 39. Det funkar ju lite beroende på hur noga man vill/behöver vara. Helst skall man väl se till att februari bara kan ha 29 dagar de år det är skottår... Men det är kanske lite overkill?

Skickades från m.sweclockers.com

Trädvy Permalänk
Inaktiv
Registrerad
Mar 2015
Skrivet av Lakritsugglan:

Ditt regex godkänner månad 19 och dag 39. Det funkar ju lite beroende på hur noga man vill/behöver vara. Helst skall man väl se till att februari bara kan ha 29 dagar de år det är skottår... Men det är kanske lite overkill?

Skickades från m.sweclockers.com

Aldrig prövat regex, men man kan väl köra lite av en fulvariant och göra en per månad, sedan dra ut månaden från servern och då köra den regexen som är lämplig?

Trädvy Permalänk
Medlem
Plats
Västra Frölunda
Registrerad
Maj 2004

Jag har starkt för mig att validering av personnummer varit på tapeten innan och att någon kom då med det briljanta förslaget att använda språkets egna sträng-till-datum-funktion för att validera den delen. Då behöver man inte själv hålla kolla på om det är skottår o.dyl. Antingen går det att tolka som ett datum eller inte...

Och bara för att vara extra jobbig, så tänkte jag påpeka att det inte bara är minus-tecken som är tillåtet mellan datum- och nummer-delen av ett personnummer, utan även plus-tecken är giltigt. Detta används dock bara av personer som passerat den respektabla åldern av 100 år!

as far as we can tell, the massacre went well...

Trädvy Permalänk
Medlem
Plats
i din garderob
Registrerad
Sep 2007
Skrivet av KaffeMedKotlett:

Aldrig prövat regex, men man kan väl köra lite av en fulvariant och göra en per månad, sedan dra ut månaden från servern och då köra den regexen som är lämplig?

Februari har 29 dagar om årtalet (gregoriansk kalender) är delbart med 4 men inte 100 om det även inte är delbart med 400. Det är omöjligt att beskriva en sådan match med regex.

Skrivet av jovnas:

Jag har starkt för mig att validering av personnummer varit på tapeten innan och att någon kom då med det briljanta förslaget att använda språkets egna sträng-till-datum-funktion för att validera den delen. Då behöver man inte själv hålla kolla på om det är skottår o.dyl. Antingen går det att tolka som ett datum eller inte...

#15817915

Bilanaloger är som Volvo — varenda svenne kör med dem

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002

// https://www.skatteverket.se/privat/folkbokforing/personnummer... // https://sv.wikipedia.org/wiki/Personnummer_i_Sverige class Personnummer { private $year = null, $month = null, $day = null, $bn = null; public function __construct($year, $month, $day, $bn) { $this->year = $year; $this->month = $month; $this->day = $day; $this->bn = $bn; } public static function validateString($personnummer) { return self::createFromString($personnummer) !== null; } public static function createFromString($personnummer) { // Ett personnummer består av 6 siffror (yymmdd), ett skiljetecken som är antingen ett bindestreck // eller ett plustecken (i de fall personen är 100 eller äldre), ett tre siffror långt // födelsenummer och en kontrollsiffra. // // Som tidigare påpekats så kan vi inte använda regjuljära uttryck för att validera numret // eftersom att vi även måste kontrollera att det är ett giltigt datum samt verifiera // att kontrollsiffran stämmer, men vi kan använda det för att kontrollera textformatet // och dela upp det i användbara delar: if(preg_match("/^ (\d{2}) # 1: År (\d{2}) # 2: Månad (\d{2}) # 3: Dag ([+-]) # 4: Skiljetecken (\d{3}) # 5: Födelsenummer (\d{1}) # 6: Kontrollsiffra $/x", $personnummer, $m)) { $year = (int)$m[1]; $month = (int)$m[2]; $day = (int)$m[3]; $sep = $m[4]; $bn = (int)$m[5]; $check = (int)$m[6]; // Innan vi kan kontrollera datumet så måste vi normalisera födelseåret: $year = self::normalizeYear($year, $sep === "+"); // Sedan kan vi kontrollera att datumet är giltigt, // PHP har en standardfunktion för syftet, använd den: if(checkdate($month, $day, $year)) { // Sedan verifierar vi kontrollsiffran: if($check === self::calcCheckDigit($year, $month, $day, $bn)) { return new self($year, $month, $day, $bn); } } } return null; } private static function normalizeYear($birthYear, $hundredOrOlder = false) { $currentYear = (int)date("Y"); $currentCentury = floor($currentYear / 100) * 100; $currentYear = $currentYear % 100; if($hundredOrOlder) { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 200; } else { return $birthYear + $currentCentury - 100; } } else { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 100; } else { return $birthYear + $currentCentury; } } } private static function calcCheckDigit($year, $month, $day, $bn) { // Skapa en sträng med tvåsiffriga datumkomponenter och födelsenumret: $digits = sprintf( '%02d%02d%02d%03d', substr((string)$year, 2, 2), $month, $day, $bn ); $sum = 0; // För varje tecken... for($i = 0, $c1 = strlen($digits); $i < $c1; ++$i) { // Multiplicera siffran omväxlande med 1 eller 2: $t = (string)((int)$digits[$i] * ($i % 2 === 0 ? 2 : 1)); // För varje siffra i resultatet: for($n = 0, $c2 = strlen($t); $n < $c2; ++$n) { // Addera till summan: $sum += (int)$t[$n]; } } // Hämta resten och subtrahera den från 10: $check = 10 - $sum % 10; // Om resten är 10 så blir kontrollsiffran 0: return $check === 10 ? 0 : $check; } public function getYear() { return $this->year; } public function getMonth() { return $this->month; } public function getDay() { return $this->day; } public function getBirthNumber() { return $this->bn; } public function getGender() { return $this->bn % 2; } public function getCheckDigit() { return self::calcCheckDigit($this->year, $this->month, $this->day, $this->bn); } public function toString() { return sprintf( '%s%02d%02d%s%03d%d', substr((string)$this->year, 2, 2), $this->month, $this->day, ((int)date("Y") - $this->year < 100 ? "-" : "+"), $this->bn, $this->getCheckDigit() ); } public function __toString() { return $this->toString(); } } // Tester: var_dump( Personnummer::validateString("640823-3234"), // Exempel från Skatteverket Personnummer::validateString("811218-9876"), // Exempel från Wikipedia Personnummer::validateString("160207-0003"), // Idag Personnummer::validateString("160207+0003"), // Idag, för 100 år sedan Personnummer::validateString("040229-0001"), // På skottdagen 2004 Personnummer::validateString("100101+0006"), // Mer än hundra år gammal, född på 1900-talet Personnummer::validateString("900101+0009"), // Mer än hundra år gammal, född på 1800-talet Personnummer::validateString("000101-0008"), // 1:e januari 2000 Personnummer::validateString("000101+0008"), // 1:e januari 1900 Personnummer::validateString("640823-3233"), // Skatteverket (ogiltig kontrollsiffra) Personnummer::validateString("811218-9875"), // Wiki (ogitlig kontrollsiffra) Personnummer::validateString("030229-0002") // På skottdagen 2003 (giltig kontrollsiffra, ogiltigt datum) ); // Skapa nytt personnummer: $nyttPN = new Personnummer(1980, 2, 7, 000); var_dump($nyttPN->toString());

Exempel i PHP

Abstractions all the way down.

Trädvy Permalänk
Medlem
Registrerad
Aug 2011
Skrivet av Lakritsugglan:

Ditt regex godkänner månad 19 och dag 39. Det funkar ju lite beroende på hur noga man vill/behöver vara. Helst skall man väl se till att februari bara kan ha 29 dagar de år det är skottår... Men det är kanske lite overkill?

Skickades från m.sweclockers.com

Så sant, kände mig tvungen att fila lite på det, tycker denna borde fungera (dock endast notepad för närvarande så någon parantes kan ha gått åt skogen):

^(19|20)?[0-9]{2}((0[13578]|1[02])(0[1-9]|[1-2][0-9]|3[01])|(0[469]|10)(0[1-9]|[1-2][0-9]|30)|02(?(?<=((19|20)(04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)|2000)02)(0[1-9]|[1-2][0-9])|(0[1-9]|1[0-9]|2[0-8])))-[0-9]{4}$

Kräver att användaren fyller i de första siffrorna i födelseåret (dvs 1988 inte 88) annars defaultar den till icke skottår's 28 dagars februari. Stod mellan det, eller än mer nestat regex, eller behandla år 1900 felaktigt som skottår (vilket ändå blir en gissningslek om användaren endast fyller i 00).

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Maj 2009

FYI people, jag har löst detta och "stängt" tråden tidigare, men en mod har av någon anledning återställt mitt inlägg och sen tagit bort att jag har löst detta redan

@Tunnelsork:
även fast jag löst det så tänker jag testa detta, ser för bra ut för att inte kolla in de

Min dator: Silent Base 600 | 1700X @ 3.9Ghz | MSI Gaming X 1080TI | RM750X | 512Gb M2 | 16Gb 3200mhz Ram | S34E790C @ 3440x1440
Tjejens dator: Define r4 | i5 3570k @ 4.2ghz | GTX Titan | 750w Supernova | 240gb SSD | 32gb ram
Citera/Tagga för svar!

Trädvy Permalänk
Medlem
Registrerad
Jun 2011
Skrivet av kronwalled:

^(19|20)?[0-9]{2}((0[13578]|1[02])(0[1-9]|[1-2][0-9]|3[01])|(0[469]|10)(0[1-9]|[1-2][0-9]|30)|02(?(?<=((19|20)(04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)|2000)02)(0[1-9]|[1-2][0-9])|(0[1-9]|1[0-9]|2[0-8])))-[0-9]{4}$

help

MSI Z77A-GD65 | Intel Core i5 3570K @ 4,2 GHz | Palit Geforce GTX 1070 JetStream | 16 GB Corsair Vengeance 1600 MHz

You must come with me, young ones, for I am the grim reaper.

Trädvy Permalänk
Medlem
Registrerad
Aug 2011
Skrivet av L'ombra:

Kan man lungt säga, inte stiligt, finns ingen mening att använda det över ovan eleganta lösning för php-specifika ändamål. Men om man bara ska validera att input är giltig, oavsett språk (så länge stöd för lookbehind finns)...

Med stöd för +/- oavsett om århundrade anges, måste dock uppdateras årligen om man inte är villig att bygga ihop den med strings mha Date funktionalitet. Inte för att jag tror någon har nytta för det, men men.

^(19(?=((0[0-9]|1[0-6])[0-9]{4}\+))|(19(?=((1[7-9]|[2-9][0-9])[0-9]{4}-))|20(?=((0[0-9]|1[0-6])[0-9]{4}-))))?[0-9]{2}((0[13578]|1[02])(0[1-9]|[1-2][0-9]|3[01])|(0[469]|10)(0[1-9]|[1-2][0-9]|30)|02(?(?<=(04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)02|(?<=(0002))(?=(0[1-9]|[1-2][0-9])\+))(0[1-9]|[1-2][0-9])|(0[1-9]|1[0-9]|2[0-8])))[-+][0-9]{4}$

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Maj 2009
Skrivet av Tunnelsork:

// https://www.skatteverket.se/privat/folkbokforing/personnummer... // https://sv.wikipedia.org/wiki/Personnummer_i_Sverige class Personnummer { private $year = null, $month = null, $day = null, $bn = null; public function __construct($year, $month, $day, $bn) { $this->year = $year; $this->month = $month; $this->day = $day; $this->bn = $bn; } public static function validateString($personnummer) { return self::createFromString($personnummer) !== null; } public static function createFromString($personnummer) { // Ett personnummer består av 6 siffror (yymmdd), ett skiljetecken som är antingen ett bindestreck // eller ett plustecken (i de fall personen är 100 eller äldre), ett tre siffror långt // födelsenummer och en kontrollsiffra. // // Som tidigare påpekats så kan vi inte använda regjuljära uttryck för att validera numret // eftersom att vi även måste kontrollera att det är ett giltigt datum samt verifiera // att kontrollsiffran stämmer, men vi kan använda det för att kontrollera textformatet // och dela upp det i användbara delar: if(preg_match("/^ (\d{2}) # 1: År (\d{2}) # 2: Månad (\d{2}) # 3: Dag ([+-]) # 4: Skiljetecken (\d{3}) # 5: Födelsenummer (\d{1}) # 6: Kontrollsiffra $/x", $personnummer, $m)) { $year = (int)$m[1]; $month = (int)$m[2]; $day = (int)$m[3]; $sep = $m[4]; $bn = (int)$m[5]; $check = (int)$m[6]; // Innan vi kan kontrollera datumet så måste vi normalisera födelseåret: $year = self::normalizeYear($year, $sep === "+"); // Sedan kan vi kontrollera att datumet är giltigt, // PHP har en standardfunktion för syftet, använd den: if(checkdate($month, $day, $year)) { // Sedan verifierar vi kontrollsiffran: if($check === self::calcCheckDigit($year, $month, $day, $bn)) { return new self($year, $month, $day, $bn); } } } return null; } private static function normalizeYear($birthYear, $hundredOrOlder = false) { $currentYear = (int)date("Y"); $currentCentury = floor($currentYear / 100) * 100; $currentYear = $currentYear % 100; if($hundredOrOlder) { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 200; } else { return $birthYear + $currentCentury - 100; } } else { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 100; } else { return $birthYear + $currentCentury; } } } private static function calcCheckDigit($year, $month, $day, $bn) { // Skapa en sträng med tvåsiffriga datumkomponenter och födelsenumret: $digits = sprintf( '%02d%02d%02d%03d', substr((string)$year, 2, 2), $month, $day, $bn ); $sum = 0; // För varje tecken... for($i = 0, $c1 = strlen($digits); $i < $c1; ++$i) { // Multiplicera siffran omväxlande med 1 eller 2: $t = (string)((int)$digits[$i] * ($i % 2 === 0 ? 2 : 1)); // För varje siffra i resultatet: for($n = 0, $c2 = strlen($t); $n < $c2; ++$n) { // Addera till summan: $sum += (int)$t[$n]; } } // Hämta resten och subtrahera den från 10: $check = 10 - $sum % 10; // Om resten är 10 så blir kontrollsiffran 0: return $check === 10 ? 0 : $check; } public function getYear() { return $this->year; } public function getMonth() { return $this->month; } public function getDay() { return $this->day; } public function getBirthNumber() { return $this->bn; } public function getGender() { return $this->bn % 2; } public function getCheckDigit() { return self::calcCheckDigit($this->year, $this->month, $this->day, $this->bn); } public function toString() { return sprintf( '%s%02d%02d%s%03d%d', substr((string)$this->year, 2, 2), $this->month, $this->day, ((int)date("Y") - $this->year < 100 ? "-" : "+"), $this->bn, $this->getCheckDigit() ); } public function __toString() { return $this->toString(); } } // Tester: var_dump( Personnummer::validateString("640823-3234"), // Exempel från Skatteverket Personnummer::validateString("811218-9876"), // Exempel från Wikipedia Personnummer::validateString("160207-0003"), // Idag Personnummer::validateString("160207+0003"), // Idag, för 100 år sedan Personnummer::validateString("040229-0001"), // På skottdagen 2004 Personnummer::validateString("100101+0006"), // Mer än hundra år gammal, född på 1900-talet Personnummer::validateString("900101+0009"), // Mer än hundra år gammal, född på 1800-talet Personnummer::validateString("000101-0008"), // 1:e januari 2000 Personnummer::validateString("000101+0008"), // 1:e januari 1900 Personnummer::validateString("640823-3233"), // Skatteverket (ogiltig kontrollsiffra) Personnummer::validateString("811218-9875"), // Wiki (ogitlig kontrollsiffra) Personnummer::validateString("030229-0002") // På skottdagen 2003 (giltig kontrollsiffra, ogiltigt datum) ); // Skapa nytt personnummer: $nyttPN = new Personnummer(1980, 2, 7, 000); var_dump($nyttPN->toString());

Exempel i PHP

har äntligen fått användning av en personnummer verifierare igen och måste säga att denna lösningen är guld. otroligt stark. tack som fan att du postade den

Min dator: Silent Base 600 | 1700X @ 3.9Ghz | MSI Gaming X 1080TI | RM750X | 512Gb M2 | 16Gb 3200mhz Ram | S34E790C @ 3440x1440
Tjejens dator: Define r4 | i5 3570k @ 4.2ghz | GTX Titan | 750w Supernova | 240gb SSD | 32gb ram
Citera/Tagga för svar!

Trädvy Permalänk
Medlem
Plats
Järfälla
Registrerad
Jan 2004

För er som är intresserade, luhn algoritmen kallas den: https://sv.wikipedia.org/wiki/Luhn-algoritmen

Trädvy Permalänk
Medlem
Plats
Linköping
Registrerad
Aug 2001

Om tråden ändå ligger såhär pass högt uppe i forumet som den ju nu gör just nu så kan jag ju påpeka att man inte nödvändigtvis ska vara så värst nitisk med vad man godkänner i födelsedelen av personnumret som en del inlägg ovan är. Som ett exempel; Det finns ju även av Skatteverket tilldelade samordningsnummer, som har 60 adderat på datumet, och det finns en hel del situationer då även personer med sådana istället för fullvärdiga personnummer kan ha nytta av att kunna matas in i diverse system.

(Och när jag ändå håller på - använd inte personnummer som primärnycklar i nån databas eller så om det går att undvika, de byts alldeles för ofta för att det ska vara så värst roligt att bygga på rutiner för det i efterhand)

The power of GNU compiles you!
"Often statistics are used as a drunken man uses lampposts -- for support rather than illumination."
#fooblog @ freenode.org, #lysator @ freenode.org

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002
Skrivet av Christley:

har äntligen fått användning av en personnummer verifierare igen och måste säga att denna lösningen är guld. otroligt stark. tack som fan att du postade den

Kul att det kom till användning!

Skrivet av kode:

Om tråden ändå ligger såhär pass högt uppe i forumet som den ju nu gör just nu så kan jag ju påpeka att man inte nödvändigtvis ska vara så värst nitisk med vad man godkänner i födelsedelen av personnumret som en del inlägg ovan är. Som ett exempel; Det finns ju även av Skatteverket tilldelade samordningsnummer, som har 60 adderat på datumet, och det finns en hel del situationer då även personer med sådana istället för fullvärdiga personnummer kan ha nytta av att kunna matas in i diverse system.

TIL. Det verkar dock vara en smal sak att testa för även sådana; om dagen > 60 så räkna bort det innan datumet valideras, resten av processen ser ut att vara densamma. Samordningsnummer beskrivs här:
https://www.skatteverket.se/foretagochorganisationer/myndighe...

Finns det fler, liknande undantag?

Här är ett exempel för PHP 7.1 som accepterar samordningsnummer:

<?php declare(strict_types=1); // https://www.skatteverket.se/privat/folkbokforing/personnummer... // https://www.skatteverket.se/foretagochorganisationer/myndighe... // https://sv.wikipedia.org/wiki/Personnummer_i_Sverige class Personnummer { private $year = 0, $month = 0, $day = 0, $bn = 0; public function __construct(int $year, int $month, int $day, int $bn) { $this->year = $year; $this->month = $month; $this->day = $day; $this->bn = $bn; } public static function validateString(string $personnummer) : bool { return self::createFromString($personnummer) !== null; } public static function createFromString(string $personnummer) : ?Personnummer { // Ett personnummer består av 6 siffror (yymmdd), ett skiljetecken som är antingen ett bindestreck // eller ett plustecken (i de fall personen är 100 eller äldre), ett tre siffror långt // födelsenummer och en kontrollsiffra. // // Som tidigare påpekats så kan vi inte använda regjuljära uttryck för att validera numret // eftersom att vi även måste kontrollera att det är ett giltigt datum samt verifiera // att kontrollsiffran stämmer, men vi kan använda det för att kontrollera textformatet // och dela upp det i användbara delar: if(preg_match("/^ (\d{2}) # 1: År (\d{2}) # 2: Månad (\d{2}) # 3: Dag ([+-]) # 4: Skiljetecken (\d{3}) # 5: Födelsenummer (\d{1}) # 6: Kontrollsiffra $/x", $personnummer, $m)) { $year = (int)$m[1]; $month = (int)$m[2]; $day = (int)$m[3]; $sep = $m[4]; $bn = (int)$m[5]; $check = (int)$m[6]; // Innan vi kan kontrollera datumet så måste vi normalisera födelseåret: $year = self::normalizeYear($year, $sep === "+"); // Sedan kan vi kontrollera att datumet är giltigt, // PHP har en standardfunktion för syftet, använd den. // // Om datumet > 60 så är det ett samordningsnummer; // subtrahera 60 från datumet innan kontrollen. if(checkdate($month, $day > 60 ? $day - 60 : $day, $year)) { // Sedan verifierar vi kontrollsiffran: if($check === self::calcCheckDigit($year, $month, $day, $bn)) { return new self($year, $month, $day, $bn); } } } return null; } private static function normalizeYear(int $birthYear, bool $hundredOrOlder = false) : int { $currentYear = (int)date("Y"); $currentCentury = (int)floor($currentYear / 100) * 100; $currentYear = $currentYear % 100; if($hundredOrOlder) { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 200; } else { return $birthYear + $currentCentury - 100; } } else { if($birthYear > $currentYear) { return $birthYear + $currentCentury - 100; } else { return $birthYear + $currentCentury; } } } private static function calcCheckDigit(int $year, int $month, int $day, int $bn) : int { // Skapa en sträng med tvåsiffriga datumkomponenter och födelsenumret: $digits = sprintf( '%02d%02d%02d%03d', substr((string)$year, 2, 2), $month, $day, $bn ); $sum = 0; // För varje tecken... for($i = 0, $c1 = strlen($digits); $i < $c1; ++$i) { // Multiplicera siffran omväxlande med 1 eller 2: $t = (string)((int)$digits[$i] * ($i % 2 === 0 ? 2 : 1)); // För varje siffra i resultatet: for($n = 0, $c2 = strlen($t); $n < $c2; ++$n) { // Addera till summan: $sum += (int)$t[$n]; } } // Hämta resten och subtrahera den från 10: $check = 10 - $sum % 10; // Om resten är 10 så blir kontrollsiffran 0: return $check === 10 ? 0 : $check; } public function getYear() : int { return $this->year; } public function getMonth() : int { return $this->month; } public function getDay() : int { return $this->day; } public function getBirthNumber() : int { return $this->bn; } public function getGender() : int { return $this->bn % 2; } public function getCheckDigit() : int { return self::calcCheckDigit($this->year, $this->month, $this->day, $this->bn); } public function toString() : string { return sprintf( '%s%02d%02d%s%03d%d', substr((string)$this->year, 2, 2), $this->month, $this->day, ((int)date("Y") - $this->year < 100 ? "-" : "+"), $this->bn, $this->getCheckDigit() ); } public function __toString() : string { return $this->toString(); } } // Tester: header("Content-Type: text/plain"); // Validera personnummer: var_dump( // Alla dessa ska passera... Personnummer::validateString("640823-3234"), // Exempel från Skatteverket Personnummer::validateString("811218-9876"), // Exempel från Wikipedia Personnummer::validateString("160207-0003"), // Idag Personnummer::validateString("160207+0003"), // Idag, för 100 år sedan Personnummer::validateString("040229-0001"), // På skottdagen 2004 Personnummer::validateString("100101+0006"), // Mer än hundra år gammal, född på 1900-talet Personnummer::validateString("900101+0009"), // Mer än hundra år gammal, född på 1800-talet Personnummer::validateString("000101-0008"), // 1:e januari 2000 Personnummer::validateString("000101+0008"), // 1:e januari 1900 Personnummer::validateString("701063-2391"), // Samordningsnummer null, // Men inga av dessa... Personnummer::validateString("640823-3233"), // Skatteverket (ogiltig kontrollsiffra) Personnummer::validateString("811218-9875"), // Wiki (ogitlig kontrollsiffra) Personnummer::validateString("030229-0002") // På skottdagen 2003 (giltig kontrollsiffra, ogiltigt datum) ); // Generera personnummer: $a = new Personnummer(1964, 8, 23, 323); // Exempel från Skatteverket $b = new Personnummer(1970, 10, 63, 239); // Samordningsnummer var_dump([ $a->toString(), $b->toString() ]);

Dold text

Abstractions all the way down.