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í:

  1. spočti zbytek po dělení prvních devíti číslic a čísla 11
  2. je-li zbytek 10, musí být poslední číslice 0
  3. 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 (gist):

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;

	if ($c === '') {
		$year += $year < 54 ? 1900 : 1800;
	} else {
		// kontrolní číslice
		$mod = ($year . $month . $day . $ext) % 11;
		if ($mod === 10) $mod = 0;
		if ($mod !== (int) $c) {
			return false;
		}

		$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;
	}

	// kontrola data
	if (!checkdate($month, $day, $year)) {
		return false;
	}

	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.

Upozornění: např. 950101/123 je sice platné RČ osoby narozené v roce 1895, ale pravděpodobnější je, že chybí poslední číslice a číslo pak platné není.

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

  1. 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

  2. spočítáme zbytek po dělení jedenácti: zbytek = soucet % 11
  3. pro poslední osmou číslici c musí platit:
    • je-li zbytek 0, pak c = 1
    • je-li zbytek 1, pak c = 0
    • v ostatních případech je c = 11 - zbytek

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 === 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ářů.