What is a Swedish Personnummer?
The Swedish personnummer (personal identity number) is a 12-digit national identifier assigned to every resident of Sweden. It follows the format YYYYMMDD-XXXX, where the first 8 digits represent the date of birth and the last 4 digits encode gender and a Luhn checksum.
For developers building applications for the Swedish market — banking, healthcare, insurance, or government systems — generating valid test personnummer is essential. Invalid formats will be rejected by validation libraries, breaking your test pipelines before they even start.
The problem? You can't just generate random 12-digit numbers. The checksum must pass the Luhn algorithm, the date must be valid, and the gender digit must follow Swedish conventions (odd = male, even = female).
Anatomy of a Personnummer
Let's break down the format with an example: 19850523-1234
| Position | Value | Meaning |
|---|---|---|
| 1–4 | 1985 | Birth year |
| 5–6 | 05 | Birth month |
| 7–8 | 23 | Birth day |
| 9 | - | Separator (+ for age 100+) |
| 10–11 | 12 | Birth number (regional) |
| 12 | 3 | Gender digit (odd=male) |
| 13 | 4 | Luhn checksum digit |
The Luhn checksum is calculated on digits 3–12 (YYMMDD + birth number + gender). This is the same algorithm used to validate credit card numbers — a single wrong digit will fail validation.
The Luhn Algorithm Explained
The Luhn algorithm works by doubling every other digit, summing the results, and checking if the total is divisible by 10. Here's the implementation:
function luhnChecksum(digits: number[]): number {
let sum = 0;
for (let i = 0; i < digits.length; i++) {
let d = digits[i];
if (i % 2 === 0) {
d *= 2;
if (d > 9) d -= 9;
}
sum += d;
}
return (10 - (sum % 10)) % 10;
}
// Example: validate "850523-1234"
const input = [8, 5, 0, 5, 2, 3, 1, 2, 3];
const checkDigit = luhnChecksum(input);
console.log(checkDigit); // Should equal 4Generating Valid Test Data with Antimass Labs
Instead of writing custom generation code, you can generate batches of valid Swedish personnummer directly in your browser using the Antimass Labs Data Generator:
1. Open the Data Generator tool 2. Select Sweden (sv) as your locale 3. Choose the Person schema — it auto-generates personnummer in the correct YYYYMMDD-XXXX format 4. Set your row count (up to 100,000 rows) 5. Export as CSV or JSON
The generator uses Faker.js under the hood with locale-aware formatting. Every personnummer passes Luhn validation and uses realistic birth date ranges. No data leaves your browser — the entire generation runs client-side using WebAssembly-optimized algorithms.
This is particularly useful for: - QA teams testing Swedish banking applications - CI/CD pipelines that need deterministic test fixtures - GDPR compliance testing — no real personal data ever touches your system
Integrating Generated Data into Your Test Suite
Once you've exported your dataset, integrate it into your test suite. Here's an example using the CSV output with a Node.js test:
import { readFileSync } from 'fs';
import { validatePersonnummer } from './validators';
const csv = readFileSync('./test-data/swedish-persons.csv', 'utf-8');
const rows = csv.split('\n').slice(1); // skip header
describe('Swedish Personnummer Validation', () => {
rows.forEach((row, i) => {
const fields = row.split(',');
const pnr = fields[2]; // personnummer column
it(`row ${i}: ${pnr} should be valid`, () => {
expect(validatePersonnummer(pnr)).toBe(true);
});
});
});Common Pitfalls
1. Forgetting the century separator: Personnummer uses - for people under 100, and + for people 100 or older. Your validator must handle both.
2. Coordination numbers (samordningsnummer): Sweden adds 60 to the birth day for coordination numbers (e.g., day 23 becomes 83). These are valid identifiers used for non-residents.
3. Hardcoding test data: Static test fixtures go stale and don't cover edge cases. Use the Data Generator to create fresh batches with different seeds for each test run.
4. Using real personnummer: Never use actual Swedish identity numbers in test environments. The Antimass Labs generator creates fictional but format-valid numbers that will pass validation without risking personal data exposure.