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;
// BooleanNumerik::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'); // nullValue object API
Section titled “Value object API”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(). |
Extracted data
Section titled “Extracted data”| 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. |
Utility
Section titled “Utility”| 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. |
Examples
Section titled “Examples”$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(); // trueCentury encoding
Section titled “Century encoding”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 |
Failure reasons
Section titled “Failure reasons”| 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). |
Validation algorithm
Section titled “Validation algorithm”Weights: 1, 3, 7, 9, 1, 3, 7, 9, 1, 3
- Reject inputs longer than 32 characters. Strip whitespace. Assert exactly 11 digits.
- 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.
- Compute checksum: multiply each of the first 10 digits by its weight, sum the products, take
mod 10, subtract from10, takemod 10again. The result must equal digit 11. - 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