Unix-tidsstämpel: sekunder vs millisekunder och buggarna de orsakar

Om du har jobbat med webbapplikationer ett tag har du sannolikt stött på en tidsstämpelbugg som först inte verkade ha någon rimlig förklaring. Ett datum som plötsligt blir 1970. En tid som visas 52 år i framtiden. Ett API som underkänner en tidsstämpel som ser helt rimlig ut.

Det är stor chans att orsaken är sekunder-vs-millisekunder‑mismashen — en av de vanligaste buggarna vid datumhantering i mjukvara, och en av de lättaste att missa.

Kärnproblemet

Unix-tidsstämplar ska räkna sekunder sedan 1 januari 1970 (Unix-epoken). Det är originalstandarden, och det är vad i princip alla serverspråk och system använder:

  • Pythons time.time() returnerar sekunder
  • PHP:s time() returnerar sekunder
  • De flesta SQL-databaser lagrar och returnerar sekunder
  • Unix-skalets kommando date +%s returnerar sekunder
  • De flesta REST API-dokumentationer, när de säger “Unix timestamp”, menar sekunder

JavaScript gör annorlunda. Date.now() och new Date().getTime() returnerar millisekunder — 1000 gånger motsvarande Unix-tidsstämpel.

Det betyder att ett värde som 1712534400 representerar 8 april 2024 i sekunder. Men 1712534400000 representerar samma ögonblick i millisekunder — och om du råkar använda millisekundvärdet där sekunder förväntas, eller tvärtom, får du datum som blir helt fel.

Hur buggarna faktiskt ser ut

Scenario 1: JavaScript-millisekunder skickas till ett API som förväntar sekunder

En frontend skickar Date.now() — säg 1712534400000 — direkt till en backend som lagrar det som Unix-tidsstämpel i sekunder. Backend lagrar 1712534400000 som tidsstämpel. Om du konverterar det tillbaka till ett datum hamnar du någonstans runt år 56000.

Det här är buggen “datumet hamnar långt i framtiden”.

Scenario 2: Sekundvärde används i JavaScript

En backend returnerar en Unix-tidsstämpel i sekunder — säg 1712534400. En JavaScript-utvecklare skickar den direkt till new Date(1712534400). JavaScript tolkar det som 1 712 534 400 millisekunder sedan epoken — cirka 19,8 dagar efter epoken. UI:t visar ett datum i januari 1970.

Det här är buggen “datumet hamnar 1970”.

Scenario 3: JWT-utgångstid sätts med fel enhet

En JSON Web Token utfärdas med ett exp-claim som är tänkt att gå ut om 24 timmar. Utvecklaren räknar Date.now() + 86400 — med avsikt att lägga till 86 400 sekunder (24 timmar). Men Date.now() är i millisekunder och 86 400 är i sekunder. Resultatet blir en tidsstämpel bara cirka 86 400 millisekunder (86,4 sekunder) i framtiden. Varje token går ut på under 2 minuter i stället för 24 timmar.

Scenario 4: Databastidsstämpel läses tillbaka fel

En PostgreSQL-kolumn lagrar en Unix-tidsstämpel som ett heltal (sekunder). Värdet hämtas och används som det är i JavaScript, vilket ger ett datum i 1970. Fix: multiplicera med 1000 innan du skickar in det i new Date().

Du kan använda Unix-tidsstämpelkonverteraren för att snabbt kontrollera om ett tal är i sekunder eller millisekunder och vilket datum det motsvarar — praktiskt vid felsökning.

Så avgör du sekunder vs millisekunder

Snabbaste sättet: räkna siffrorna.

  • 10 siffror (t.ex. 1712534400) → sekunder. Representerar ett datum i rimlig nutid eller närliggande dåtid/framtid.
  • 13 siffror (t.ex. 1712534400000) → millisekunder.

En sekundbaserad Unix-tidsstämpel för datum mellan år 2000 och 2050 ligger ungefär mellan 946 684 800 och 2 524 608 000 — alltid 10 siffror. Ser du ett 13-siffrigt heltal i en tidskontext är det nästan alltid millisekunder.

Regeln fungerar för dagens datum. Långt in i framtiden (efter år 2286) får sekundtidsstämplar 11 siffror, men det är ett kantfall du kan ignorera i praktiken.

Fixen i varje språk

JavaScript (konvertera till sekunder när du skickar till backend): `javascript const timestampSeconds = Math.floor(Date.now() / 1000); `

JavaScript (konvertera sekunder från backend för användning i Date): `javascript const date = new Date(timestampSeconds * 1000); `

Python (redan i sekunder): `python import time timestamp_seconds = int(time.time()) `

PHP (redan i sekunder): `php $timestamp_seconds = time(); `

SQL (PostgreSQL, plocka ut sekunder): `sql SELECT EXTRACT(EPOCH FROM NOW())::int; `

Konvertera en millisekundtidsstämpel till sekunder (vilket språk som helst): ` dividera med 1000, avrunda nedåt/avkorta `

JWT-expiry: rätt sätt

JWT-claims (exp, iat, nbf) måste vara i sekunder enligt RFC 7519. I JavaScript:

const now = Math.floor(Date.now() / 1000);   // seconds
const exp = now + 86400;                      // 24 hours from now, in seconds

Inte: `javascript const exp = Date.now() + 86400; // wrong: mixes milliseconds base with seconds offset `

Och inte: `javascript const exp = Date.now() + (86400 * 1000); // wrong: exp would be ~28 days in the future in milliseconds `

En JWT med ett exp-värde som är 13 siffror kommer att avvisas av de flesta JWT-bibliotek som ogiltigt eller som en tid långt i framtiden. Unix-tidsstämpelverktyget kan hjälpa dig att verifiera vilket datum ett visst exp-värde faktiskt motsvarar.

Att lagra tidsstämplar i databaser

När du designar scheman: var konsekvent med enheten.

  • Om du lagrar sekunder, dokumentera det. Kolumnnamn som created_at_unix eller en kommentar som förtydligar “Unix-tidsstämpel i sekunder” förebygger tvetydighet.
  • Om din ORM eller ditt ramverk lagrar millisekunder (vissa JavaScript-ORM:er gör det), var lika tydlig med det.
  • Undvik att blanda — lagra inte vissa tidsstämplar i sekunder och andra i millisekunder i samma system.

PostgreSQLs inbyggda typ TIMESTAMPTZ är ofta ett bättre val än att lagra råa heltal, eftersom den hanterar tidszonkonvertering och formatering direkt. Men när du väl använder heltal för tidsstämplar: välj en enhet och håll dig till den.

En notis om mikrosekunder

Vissa system går ännu längre och använder mikrosekunder (miljondelar av en sekund). Pythons time.time_ns() returnerar nanosekunder. Högfrekvenshandel, profileringsverktyg och vissa databasinterna använder mikrosekundprecision.

Om du ser ett 16-siffrigt heltal i en tidskontext kan det vara mikrosekunder. Ett 19-siffrigt heltal kan vara nanosekunder. Samma heuristik gäller — antalet siffror säger något om skalan.

För webbapplikationers tidsstämplar är millisekunder så precist som du någonsin behöver. Mikrosekundprecision spelar roll först i prestandakritiska, högfrekventa system.

Den praktiska regeln

Varje gång en tidsstämpel passerar en språk- eller systemgräns — JavaScript-frontend till Python/PHP/Go/Java-backend, applikation till databas, applikation till externt API — fråga: vilken sida använder sekunder och vilken använder millisekunder? Lägg konverteringen vid gränsen, dokumentera den och testa med ett känt datum för att verifiera att resultatet är rimligt.

Testet är enkelt: konvertera tidsstämpeln du genererar och kontrollera att den visar ett plausibelt datum. Om det blir 1970 eller 55000 vet du direkt att något är fel.