Skip to content

PESEL

PESEL (Powszechny Elektroniczny System Ewidencji Ludności) is Poland’s universal 11-digit citizen identifier. It encodes the holder’s birth date, birth century, gender, and an ordinal serial number.

use SlashLab\Numerik\Numerik;
// Boolean
Numerik::pesel()->isValid('92060512186'); // true
// Rich result
$result = Numerik::pesel()->validate('92060512186');
$result->isValid; // true
// Parse to value object
$pesel = Numerik::pesel()->parse('92060512186');
// Null on failure instead of exception
$pesel = Numerik::pesel()->tryParse('bad-input'); // null

parse() and tryParse() return a SlashLab\Numerik\ValueObjects\Pesel instance.

Method Return type Description
getRaw() string The original input, untouched.
getNormalized() string Whitespace-stripped digits.
__toString() string Same as getNormalized().
Method Return type Description
getBirthDate() DateTimeImmutable Birth date decoded from the PESEL.
getGender() Gender Gender::Male or Gender::Female.
getOrdinalNumber() int The 4-digit ordinal serial (digits 7–10). The last digit also encodes gender: odd = male, even = female.
getCentury() int The birth century as a base year, e.g. 1900, 2000, 2100.
Method Return type Description
isMale() bool true when gender is Gender::Male.
isFemale() bool true when gender is Gender::Female.
getAge() int Full years elapsed from birth date to today.
isAdult() bool true when getAge() >= 18.
$pesel = Numerik::pesel()->parse('92060512186');
$pesel->getRaw(); // '92060512186'
$pesel->getNormalized(); // '92060512186'
$pesel->getBirthDate()->format('Y-m-d'); // '1992-06-05'
$pesel->getGender(); // Gender::Female
$pesel->isFemale(); // true
$pesel->getOrdinalNumber(); // 1218 (digits 7–10)
$pesel->getCentury(); // 1900
$pesel->getAge(); // calculated from today
$pesel->isAdult(); // true

The month digits in a PESEL encode both the real month and the birth century:

Stored month range Real month Birth century
01–12 01–12 1900–1999
21–32 01–12 2000–2099
41–52 01–12 2100–2199
61–72 01–12 2200–2299
81–92 01–12 1800–1899
Reason Value When
InvalidLength invalid_length Input is not exactly 11 digits after normalisation.
InvalidCharacters invalid_characters Non-digit characters remain after stripping whitespace.
InvalidMonth invalid_month Month encoding does not match any known century range.
InvalidDate invalid_date The decoded date is not a real calendar date.
FutureDate future_date The decoded birth date is in the future (strict mode only).
InvalidChecksum invalid_checksum Checksum digit does not match the computed value.
AllSameDigit all_same_digit All 11 digits are identical (strict mode only).

Weights: 1, 3, 7, 9, 1, 3, 7, 9, 1, 3

  1. Reject inputs longer than 32 characters. Strip whitespace. Assert exactly 11 digits.
  2. Decode birth date using the century encoding table above. Assert the date is a real calendar date. In strict mode, also reject birth dates in the future.
  3. Compute checksum: multiply each of the first 10 digits by its weight, sum the products, take mod 10, subtract from 10, take mod 10 again. The result must equal digit 11.
  4. In strict mode, reject inputs where all 11 digits are identical.

See Algorithms for the full reference.

If this saved you time → ☕ Buy me a coffee