Jak ověřit platné IČ a rodné číslo?
Víte, že rodné číslo nemusí být dělitelné 11? Že algoritmus ministerstva vnitra pro ověřování IČ je špatný? A že jej používá celá řada aplikací? A co na to Jan Tleskač?
Je užitečné, pokud aplikace (nejen webové) umí ověřit platnost různých identifikátorů, jako je třeba e-mail, rodné číslo, IČ a podobně. Naopak průseroidní je, když ověřovací algoritmus obsahuje chybu a digitální nepřítel pak tvrdošíjně odmítá vaše nacionále. Ukecat se nenechá, třísknout ho nemůžete.
Zrovna ohledně ověřování rodných čísel a IČ se obávám, že chybných bude většina implementací. Problém je v nedostatečném zdokumentování algoritmů, takže programátoři často spoléhají na neověřené informace. Jako skutečně kvalitní studnici vědění bych doporučil:
Hned si jej stáhněte!
Ověření rodného čísla
Začnu parafrází z pédéefka:
Do roku 1985 bylo přiděleno cca 1000 rodných čísel, která nejsou dělitelná 11. Není vyloučeno, že se v minimálním počtu vyskytly i po tomto roce.
Ale pozor, není to tím, že by úředníci na matrice byli trulanti! Dělitelnost jedenácti totiž bůh nikdy nesliboval. Algoritmus je ve skutečnosti následující:
- spočti zbytek po dělení prvních devíti číslic a čísla 11
- je-li zbytek 10, musí být poslední číslice 0
- jinak poslední číslice musí být rovna zbytku
Tedy 780123/3540 je korektní rodné číslo, ačkoliv není
dělitelné jedenácti. No, nechtěl bych je mít, dovedu si představit ten
všudypřítomný opruz ![]()
Implementace v PHP:
function verifyRC($rc)
{
// "be liberal in what you receive"
if (!preg_match('#^\s*(\d\d)(\d\d)(\d\d)[ /]*(\d\d\d)(\d?)\s*$#', $rc, $matches)) {
return FALSE;
}
list(, $year, $month, $day, $ext, $c) = $matches;
// do roku 1954 přidělovaná devítimístná RČ nelze ověřit
if ($c === '') {
return $year < 54;
}
// kontrolní číslice
$mod = ($year . $month . $day . $ext) % 11;
if ($mod === 10) $mod = 0;
if ($mod !== (int) $c) {
return FALSE;
}
// kontrola data
$year += $year < 54 ? 2000 : 1900;
// k měsíci může být připočteno 20, 50 nebo 70
if ($month > 70 && $year > 2003) $month -= 70;
elseif ($month > 50) $month -= 50;
elseif ($month > 20 && $year > 2003) $month -= 20;
if (!checkdate($month, $day, $year)) {
return FALSE;
}
// cislo je OK
return TRUE;
}
Funkce se řídí heslem „buďte přísní v tom, co posíláte, a buďte velkorysí v tom, co přijímáte.“ Uživatel tak může zadat RČ i s lomítkem nebo mezerami.
Ještě poznámka – stejný systém rodných čísel používají i na Slovensku, takže kód mohou použít i bratia programátori.
Ověření IČ
Před lety jsem napsal webovou aplikaci pro jistou firmu, kde jsem použil ověřování platnosti IČO (tehdy ještě IČO, dnes už IČ). Vycházel jsem z algoritmu publikovaného na stránkách ministerstva vnitra. Jaké bylo překvapení, když si klient zkušebně zadal své vlastní IČO a aplikace mu oznámila, že je neplatné.
Dodnes toto IČO (25596641) používám k otestování aplikací, jestli také nepoužívají vadný algoritmus. A řeknu vám, většina neprojde.
Takže, jak se ověřuje IČ? Například 69663963
- první až sedmou číslici vynásobíme čísly 8, 7, 6, 5, 4, 3, 2 a
součiny sečteme:
soucet = 6*8 + 9*7 + 6*6 + 6*5 + 3*4 + 9*3 + 6*2 = 228 - spočítáme zbytek po dělení jedenácti:
zbytek = soucet % 11 - pro poslední osmou číslici
cmusí platit:- je-li zbytek 0 nebo 10, pak
c = 1 - je-li zbytek 1, pak
c = 0 - v ostatních případech je
c = 11 - zbytek
- je-li zbytek 0 nebo 10, pak
Implementace v PHP:
function verifyIC($ic)
{
// "be liberal in what you receive"
$ic = preg_replace('#\s+#', '', $ic);
// má požadovaný tvar?
if (!preg_match('#^\d{8}$#', $ic)) {
return FALSE;
}
// kontrolní součet
$a = 0;
for ($i = 0; $i < 7; $i++) {
$a += $ic[$i] * (8 - $i);
}
$a = $a % 11;
if ($a === 0) $c = 1;
elseif ($a === 10) $c = 1;
elseif ($a === 1) $c = 0;
else $c = 11 - $a;
return (int) $ic[7] === $c;
}
Přepis do JavaScriptu už nechám na vás. Můžete ho pastnout do komentářů.
Komentáře
» přidat
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře ani hlasovat

#1 BlackSUN http://krabice.info nový
Díky za osvětu i za poskytnutí uvedených kódů, myslím, že se budou nejednomu programátorovi hodit.
#2 Pobert pobert@seznam.cz nový
Podobné to je i u bankovních účtů.
informace o algoritmu jsou na stránkách ČNB – příloha vyhlášky 62/2004:
V tuzemském platebním styku musí být první část čísla účtu a základní část čísla účtu
samostatně zajištěny pomocí modulo 11 s váhami uvedenými v následující tabulce:
Algoritmus kontroly čísla ABCDEFGHIJ na modulo 11
Číslice A B C D E F G H I J
Váhy 6 3 7 9 10 5 8 4 2 1
n 10 9 8 7 6 5 4 3 2 1
kde n je pozice číslice v čísle účtu (počítáno zprava)
Váhy jsou získány jako rozdíl n-té mocniny 2 a nejbližšího nižšího násobku 11.
Váhy se k číslicím na jednotlivých pozicích čísla účtu přiřazují zprava. Číslo ABCDEFGHIJ je zajištěno pomocí
modulo 11, pokud je součet S beze zbytku dělitelný 11, přičemž
S = J 1 + I 2 + H 4 + G 8 + F5 + E10 + D9 + C7 + B*3 + A *6
A IBAN má také kotrolní číslice (první 2 čísla za kódem země)
#3 peCan http://pecan.cz nový
Taky jsem kdysi řešil kontrolu RČ a datum narození z něho. Když se na to teď tak dívám, taky to šlo řešit jednodušeji;-)
#4 Adam Šnobl http://snobl.wz.cz nový
Velmi dobře! Kontrolu IČ jsem (v Javě) implementoval dobře, ale o té specialitě u rodného čísla jsem nevěděl. To budu muset přepsat. Díky za upozornění!
#5 Pavel Jaroš http://jaros.ezin.cz/ nový
I já mám rád Zeleného Raoula, ale na internetu by otázka správně měla znít: Co na to RH?
#6 marek http://www.mixa.name nový
osobně se setkávám ještě s jednou vadou na kráse… má web s koncovkou .name , často narazímu zadávání emailu :(
#7 mANY nový
Zcela mimo téma: „Vycházel jsem z algoritmu publikovaného na stránkách ministerstva vnitra.“
#8 Shaman http://www.adaptic.cz nový
Díky za užitečný článek. Skutečně se mi teď hodí.
#9 Morty http://www.koldasoft.cz nový
Jen tak pro zajímavost jsem si vyhledal co je to IČ zač.. zajímavé
Jinak rozhodně přínosný článek.
#10 Piki http://www.piki.zh4a.sk nový
Koho tieto algoritmy na tvorbu čísiel a hlavne prečo napadajú?
#11 Jakub Hejda http://www.clickmedia.cz nový
Tééééda. to, že rodné číslo nemusí být dělitelné 11 beze zbytku jsem věděl. Ale ty ostatní informace jsou pro mne naprosto nové. Děkuji !
#12 zirafka http://blog.zirafka.cz nový
#10 Piki: Jedna se o tzv. samoopravne kody. Uz nejakou dobu se o nich chystam napsat clanek. Takhle bych se do toho uz konecne mohla pustit (-:
#13 LLook http://llook.wz.cz/weblog/ nový
Vždycky jsem si lámal hlavu nad tím, jak byl asi u rodných čísel vyřešen problém Y2K. Teď už to vidím, ve skutečnosti jsou rodná čísla náchylná akorát k problému Y2K54…
#10 Piki: Kontrolní mechanizmy jsou potřeba. Třeba u čísla účtu – představ si situaci, že posíláš peníze na zahraniční účet (drahá operace, která trvá strášně dlouho) a překlepneš se. Nebo příkaz podáváš přímo v bance a překlepne se úřednice… Možností je spousta.
#14 Mormegil nový
No, když už se tady šťourá do těch velice okrajových případů, tak to chce dělat to důsledně (a to nemluvím o tom ? 200 : 1900 v kódu
). Přečtěte si třeba článek na Wikipedii a dozvíte se, že platnými rodnými
čísly jsou také např. 0531135099 či 0681186066.
A taky by vás mohlo zajímat, že na to, jak vypadá RČ, má zákon poněkud jiný názor než ISVS – viz § 13, odstavec 3 zákona 302/2004 Sb. A teď babo raď, že?
#15 David V. nový
Díky moc za článek.
Jak jsem zjistil, tak také v naší aplikaci máme chybu … Grrr
#16 tracy thomas.tracy@email.cz nový
Vtipné je, že podle toho dokumentu je validní i e-mailová adresa a@b (tedy 3 znaky)…
#17 marty marty@hranice.cz nový
marty…
proc script pro RC?
ja to taky programoval loni…
potreboval jsem to do knihy paleni, kde se eviduji RC pro potrebu celniku. A musel jsem nejak kontrolovat zda obsluha zadala spravne RC..
#18 hvge http://hvge.sk nový
#12 zirafka: V tomto pripade sa kod skor len kontroluje. Dufam ze tam ale potom spomenieš aj moj oblubeny Golayov kod (perfect binary Golay code), s ktoreho tabulkami a rucnym pocitanim som sa onehda trapil na skuske z teorie kodovania :)
..inak pekny clanok, dufam ze to RČ plati aj u nas na SVK:)
#19 HoP nový
A víte, že IČ nemusí mít 8 číslic ale i méně?
#20 pixy http://pixy.cz nový
Tak jako jo, Davide, trulant dobrej. Ale co furunkl? Ha?!
#21 DFly nový
no, spravuji system ktery eviduje urctou (dost velkou) skupinu obyvatel CR, a take jsem se musel vyporadat s RC…
To ze modulo 11 funguje az od 10ti mistnych rodnych cisel je jeden z problemu (nekde to mam presne, ale je to od roku cca 1950), nicmene i v techto novych RC jsou nekdy nesystemove problemy, napr RC (ktere jsem i osobne overoval z rodneho listu) ktere neprojde kontrolou %11, nebo shodna RC (vznikalo to v dobe kdy porodnice meli malo cisel a volali do jine v okrese, aby jim dali nejaka prebyvajici, ale nakonec i tato se pouzila a pod. pripady)
Takze nakonec system musi mit nejaky jiny unikatni kod, a po osobnim overeni musi mit flag, kterej deaktivuje hlaseni automaticke kontroly.
#22 Jan Tichý http://www.jantichy.cz nový
#10 Piki: Jsou to algoritmy, které zabezpečují číslo proti nejčastějším chybám, ke kterým dochází. V tomto případě tedy proti překlepu v jedné číslici a proti prohození dvou sousedních číslic.
#23 venca http://www.freeblog.ic.cz nový
hmm…velmi zajmavé
#24 LLook http://llook.wz.cz/weblog/ nový
#16 tracy: A ona snad není validní? Co když zítra ICANN nějaké instituci přidělí doménu B? Je fakt, že podle toho dokumentu by nějaké nevalidní adresy prošly (třeba
.@.), ale zrovnaa@bje OK.#25 David Grudl http://davidgrudl.com nový
#14 Mormegil: to je velmi dobrá připomínka, ale šlo by to sdělit méně kokotsky. Kód jsem upravil.
#19 HoP: nevíme. Máš nějaký příklad? Nebo nejde jen o opomenutí levostranných nul?
#20 pixy: furunkl se udělal našemu pejskovi na nose, takže u mě splněno na 100 %. Co ty a Arthur?
#21 DFly: řadu věcí by ti mohl osvětlit tento článek
#26 DFly nový
#25 David Grudl: ten system jsem delal nekdy pred osmi lety, a shanel jsem spoustu informaci pres kamene ustavy nasich uradu – na webu toho moc nebylo
k rodnym cislum: v tom systemu jsou dve RC na ktere nefunguje %11 a nekonci nulou (nejspis se nekdo uklepl na stroji, nebo podobny omyl – ale je to v rodnem liste, tak to musim brat jako OK)
a shody RC mam overene 4
dokonce by se dle RC mela dat poznat i porodnice, nebo alespon kraj(okres) pro ktere byli urcita cisla prirazena, jenze ono diky tomu predavani prebytecnych cisel tam kde jich byl nedostatek se dal urcit jen kraj – zkusim jeste prohledat svoje archivy, nekde budu mit podklady, podle kterych jsem to delal
nevim jak je to ted s rozdavanim cisel, jestli se berou z nejakeho predgenerovaneho zasobniku, nebo jestli jeste plati to rozhazovani cisel na porodnice
ten system bezi stale, a obcas ho dohleduju
#27 Arthur Dent http://www.misantrop.info nový
Myslím, že bys měl především napsat Ruby verzi, té by se dostalo v komunitě jistě velmi vřelého přivítání.
PS: Prohlížel jsem psa, furunkl žádnej, trulanty dva.
#28 DFly nový
Takze ted jsem na neco koukal, a prvni dve cisla za lomitkem by meli znacit cislo porodnice a treti poradi (zvlast kluci i holky diky +50), ale kolik je na tom pravdy nevim – mozna to platilo drive…
a Praha se taky bude mozna odlisovat, v rodine jsem si neco overoval a ne vsechno sedi (mozna ted se uz porodnice maji jina cisla nez pred 30 lety)
a jestli je v republice do 98 porodnic netusim (typnul bych ze vice)
99 za lomitkem znaci cizince ktery nema klasicke cislo z matriky
#29 karel nový
#28 DFly: Já a můj brácha jsme se narodili ve stejné porodnici a první dvě čísla za lomítkem máme úplně jiná.
Jinak na základě tvého pravopisu, používání interpunkce a diakritiky se mi moc nechce věřit, že jsi před osmi lety dělal nějaký systém pro rodná čísla, tipoval bych Tě na pubertální věk.
#30 DFly nový
#29 karel: hmm, kdyz si to myslis, ja ti to vymlouvat nebudu…
#31 Black Wolf http://www.kandr.name nový
Pěkný a užitečný článek, obzvláště to IČ se mi teď docela hodilo a pomohlo mi.
Ovšem v tom RČ si myslím že je malá chybička – projde tím rč které je teoreticky správné, ale datum narození je v budoucnosti. Myslím že by to projít nemělo, když se jeho potenciální nositel ještě nenarodil;)
#32 David Grudl http://davidgrudl.com nový
#31 Black Wolf: jasně, projde tím i RČ, které má datum narození v minulosti, ale stejně nebylo nikomu přiděleno. Algoritmus ověřuje pouze formální platnost a můžeš si ho sám jakkoliv vylepšít.
#33 Věroš http://www.na-mytince.cz/ nový
#28 DFly: „Oh no a special case!“
Já nejsem cizinec a poslední dvojčíslí mi končí na 99.
#34 Věroš http://www.na-mytince.cz/ nový
#29 karel: Čísla za lomítkem můžou být způsobená tím že jste se narodili před 1993 a po 1993, kdy se měnily čísla u porodnic nebo spoustou jiných důvodů (bordel na matrice, moc dětí narozených v jeden den, apod.)
#35 rony http://spravodaj.madaj.net/ nový
#16 tracy: jojo, na mojom serveri „b“ mam mailbox „a“
#36 zajDee zajdee@centrum.cz nový
#33 Věroš: Ale #28 DFly: psal o prvním dvojčíslí za lomítkem, ne?:-)
Mé rodné číslo naštěstí projde ověřením na modulo 11, ale dokážu si představit problém, kdy někdo dostane číslo, které je úplně mimo ověření modulo 11 i modulo 10.
A to nemluvím o číslech ze starších let, než 1953…
(Pamatuju si, že babička musela někdy před deseti lety dohledávat své správné rodné číslo přes několik matrik, měla v té době dvě – naštěstí potkala pracovité úředníky, takže se povedlo a dnes už má své správné rodné číslo s trojčíslím za lomítkem:-))
#37 Martin Bumba http://evil666.net nový
Zdravím prosím už to někdo přepsal do JS potřeboval bych to v JS a moc se me to nechce prepisovat díky :).
#38 David Grudl http://davidgrudl.com nový
#37 Martin Bumba:
#39 FiJi nový
Současný zákon dělitelnost 11 ale slibuje a i podle popisu na wikipedii to vypadá, že chyba je opravdu spíš v tom algoritmu.
Možná by taky stálo za to doplnit do článku, kde se tam vzalo těch 20 a 70, to myslím taky není obecně známo…
#40 Věroš http://www.na-mytince.cz/ nový
#36 zajDee: Omlouvám se za překlep. – Samozřejmě poslední čtyřčíslí mi začíná 99.
#41 Ondra nový
#14 Mormegil: Moje prababička se narodila v roce 1898 a teď ji nechtěl přijmout praktický lékař, protože novorozence pojišťovna hradí jen lékařům dětským.
#42 HoP nový
#25 David Grudl: Příklad nemám, někdo mi o tom vyprávěl, bohužel sem tomu nevěnoval moc pozornost, protože to nebyla věc, která by mne nějak znepokojovala. Zkusím to zjistit.
#43 HoP nový
#42 HoP: Tak jsem zkusil najít ten příklad a zjistil jsem, že měli v databázi pouze špatně zadané IČ, tzn. že jim vypadla jedna číslice… Takže se omlouvám za mystifikaci, IČ má skutečně 8 číslic, případě je doplněno nulami.
#44 Vlasta aspcreator@yahoo.com nový
Validace RC je zajimavy problem. Jen tak pro zajimavost, co se z RC da vycist.
mesic + 50 = zana
den + 50 = cizinec, zijici v CR
Pro zadavani a validaci RC jsem udelal Reg vyraz, ktery zohlednuje i prestupny rok. Tak to vyzkousejte a dejte vedet, jak se vam to chova.
regex = new Regex(@„^(((^\d?$)|(^\d{2}$)|(^\d{2}(0|1|5|6)$)|(^\d{2}(0|5)[1–9]$)|(^\d{2}(1|6)[0–2]$)|(^\d{2}(01|51)[01235678]$)|(^\d{2}(02|52)[012567]$)|(^\d{2}(03|53)[01235678]$)|(^\d{2}(04|54)[01235678]$)|(^\d{2}(05|55)[01235678]$)|(^\d{2}(06|56)[01235678]$)|(^\d{2}(07|57)[01235678]$)|(^\d{2}(08|58)[01235678]$)|(^\d{2}(09|59)[01235678]$)|(^\d{2}(10|60)[01235678]$)|(^\d{2}(11|61)[01235678]$)|(^\d{2}(12|62)[01235678]$)|(^\d{2}(010|020|030|040|050|060|070|080|090|100|110|120)[1–9]$)|(^\d{2}(015|025|035|045|055|065|075|085|095|105|115|125)[1–9]$)|(^\d{2}(510|520|530|540|550|560|570|580|590|600|610|620)[1–9]$)|(^\d{2}(515|525|535|545|555|565|575|585|595|605|615|625)[1–9]$)|(^\d{2}(011|021|031|041|051|061|071|081|091|101|111|121)[0–9]$)|(^\d{2}(016|026|036|046|056|066|076|086|096|106|116|126)[0–9]$)|(^\d{2}(511|521|531|541|551|561|571|581|591|601|611|621)[0–9]$)|(^\d{2}(516|526|536|546|556|566|576|586|596|606|616|626)[0–9]$)|(^\d{2}(012|032|042|052|062|072|082|092|102|112|122)[0–9]$)|(^\d{2}(017|037|047|057|067|077|087|097|107|117|127)[0–9]$)|(^\d{2}(512|532|542|552|562|572|582|592|602|612|622)[0–9]$)|(^\d{2}(517|537|547|557|567|577|587|597|607|617|627)[0–9]$)|(^\d{2}(013|033|053|073|083|103|123)[01]$)|(^\d{2}(018|038|058|078|088|108|128)[01]$)|(^\d{2}(513|533|553|573|583|603|623)[01]$)|(^\d{2}(518|538|558|578|588|608|628)[01]$)|(^\d{2}(043|063|093|113)[0]$)|(^\d{2}(048|068|098|118)[0]$)|(^\d{2}(543|563|593|613)[0]$)|(^\d{2}(548|568|598|618)[0]$)|(^\d{2}(022|027|522|527)[0–8]$)|(^(00022|00027|00522|00527|04022|04027|04522|04527|08022|08027|08522|08527)[0–9]$)|(^(12022|12027|12522|12527|16022|16027|16522|16527)[0–9]$)|(^(20022|20027|20522|20527|24022|24027|24522|24527|28022|28027|28522|28527)[0–9]$)|(^(32022|32027|32522|32527|36022|36027|36522|36527)[0–9]$)|(^(40022|40027|40522|40527|44022|44027|44522|44527|48022|48027|48522|48527)[0–9]$)|(^(52022|52027|52522|52527|56022|56027|56522|56527)[0–9]$)|(^(60022|60027|60522|60527|64022|64027|64522|64527|68022|68027|68522|68527)[0–9]$)|(^(72022|72027|72522|72527|76022|76027|76522|76527)[0–9]$)|(^(80022|80027|80522|80527|84022|84027|84522|84527|88022|88027|88522|88527)[0–9]$)|(^(92022|92027|92522|92527|96022|96027|96522|96527)[0–9]$))|(^\d{6}[0–9]{1,4}))$“);
#45 David Grudl http://davidgrudl.com nový
Přepis do JavaScriptu mi poslal Filip Oščádal
…to be continued
#46 David Grudl http://davidgrudl.com nový
(pokračování řešení od Filipa Oščádala)
příklad použití:
(test DIČ využívá testů IČO a RČ)
#47 Marian Cerny http://jojo.matfyz.cz/ nový
Pri overovani RC to nie je uplne dotiahnute. Ak je RC 9 miestne a rok < 54, mala by este nasledovat kontrola datumu pomocou checkdate().
#48 Peca petr.dana@gmail.com nový
Nevím, jestli si to tu někdo ještě přečte, ale kdyby náhodou… Asi ten algoritmus pořád není správně, nebo existují firmy s IČ, která nejsou správně vygenerovaná. Např. tohle: 72343976 (http://wwwinfo.mfcr.cz/…es_subjx.cgi?…)