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.

MethodReturn typeDescription
getRaw()stringThe original input, untouched.
getNormalized()stringWhitespace-stripped digits.
__toString()stringSame as getNormalized().
MethodReturn typeDescription
getBirthDate()DateTimeImmutableBirth date decoded from the PESEL.
getGender()GenderGender::Male or Gender::Female.
getOrdinalNumber()intThe 4-digit ordinal serial (digits 7–10). The last digit also encodes gender: odd = male, even = female.
getCentury()intThe birth century as a base year, e.g. 1900, 2000, 2100.
MethodReturn typeDescription
isMale()booltrue when gender is Gender::Male.
isFemale()booltrue when gender is Gender::Female.
getAge()intFull years elapsed from birth date to today.
isAdult()booltrue 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 rangeReal monthBirth century
01–1201–121900–1999
21–3201–122000–2099
41–5201–122100–2199
61–7201–122200–2299
81–9201–121800–1899
ReasonValueWhen
InvalidLengthinvalid_lengthInput is not exactly 11 digits after normalisation.
InvalidCharactersinvalid_charactersNon-digit characters remain after stripping whitespace.
InvalidMonthinvalid_monthMonth encoding does not match any known century range.
InvalidDateinvalid_dateThe decoded date is not a real calendar date.
FutureDatefuture_dateThe decoded birth date is in the future (strict mode only).
InvalidChecksuminvalid_checksumChecksum digit does not match the computed value.
AllSameDigitall_same_digitAll 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