Dacă ai lucrat cât de cât la aplicații web, probabil ai întâlnit un bug de timp care la început nu are niciun sens. O dată care apare ca 1970. O oră afișată ca fiind cu 52 de ani în viitor. Un API care respinge un timestamp care pare perfect rezonabil.
De multe ori, vinovatul este diferența dintre secunde și milisecunde — unul dintre cele mai comune bug-uri legate de date și, totodată, unul dintre cele mai ușor de ratat.
Problema de bază
Unix timestamp ar trebui să numere secunde de la 1 ianuarie 1970 (Unix epoch). Acesta este standardul original și este ceea ce folosesc practic toate limbajele și sistemele de pe server:
- Python
time.time()întoarce secunde - PHP
time()întoarce secunde - Majoritatea bazelor de date SQL stochează și întorc secunde
- Comanda Unix
date +%sîntoarce secunde - În documentația majorității API-urilor REST, „Unix timestamp” înseamnă secunde
JavaScript face altceva. Date.now() și new Date().getTime() întorc milisecunde — de 1000 de ori valoarea timestamp-ului în secunde.
Asta înseamnă că 1712534400 reprezintă 8 aprilie 2024 în secunde. Dar 1712534400000 reprezintă același moment în milisecunde — și dacă folosești din greșeală milisecunde acolo unde se așteaptă secunde (sau invers), obții date complet greșite.
Cum arată bug-urile în practică
Scenariul 1: JavaScript trimite milisecunde către un API care așteaptă secunde
Frontend-ul trimite Date.now() — de exemplu 1712534400000 — direct către un backend care îl stochează ca Unix timestamp în secunde. Backend-ul salvează 1712534400000 ca „secunde”. Dacă îl convertești înapoi în dată, ajungi pe la anul 56000.
Acesta este bug-ul „data apare în viitorul îndepărtat”.
Scenariul 2: Valoare în secunde folosită direct în JavaScript
Backend-ul întoarce un Unix timestamp în secunde — de exemplu 1712534400. Un dezvoltator JS îl pasează direct la new Date(1712534400). JavaScript îl interpretează ca 1.712.534.400 milisecunde de la epoch — adică aproximativ 19,8 zile după epoch. UI-ul afișează o dată din ianuarie 1970.
Acesta este bug-ul „data apare ca 1970”.
Scenariul 3: Expirarea JWT setată în unitatea greșită
Un JSON Web Token este emis cu un claim exp care ar trebui să expire în 24 de ore. Dezvoltatorul calculează Date.now() + 86400 — intenționând să adauge 86.400 de secunde (24 ore). Dar Date.now() este în milisecunde, iar 86.400 este în secunde. Rezultatul este un timestamp cu doar ~86.400 milisecunde (86,4 secunde) în viitor. Token-urile expiră în mai puțin de 2 minute în loc de 24 de ore.
Scenariul 4: Timestamp din baza de date citit greșit
O coloană PostgreSQL stochează un Unix timestamp ca întreg (secunde). Valoarea este citită și folosită în JavaScript „ca atare”, ceea ce produce o dată în 1970. Rezolvare: înmulțește cu 1000 înainte să o trimiți în new Date().
Poți folosi convertorul de Unix timestamp ca să verifici rapid dacă numărul pe care îl ai în față este în secunde sau milisecunde și ce dată reprezintă — foarte util la debugging.
Cum îți dai seama rapid: secunde sau milisecunde?
Cea mai rapidă metodă: numără cifrele.
- 10 cifre (ex:
1712534400) → timestamp în secunde (dată plauzibilă în prezent/trecut apropiat/viitor apropiat) - 13 cifre (ex:
1712534400000) → timestamp în milisecunde
Unix timestamp în secunde pentru date între 2000 și 2050 este aproximativ între 946.684.800 și 2.524.608.000 — adică mereu 10 cifre. Dacă vezi un întreg cu 13 cifre într-un context de timp, aproape sigur este în milisecunde.
Regula funcționează pentru date curente. Date foarte îndepărtate (după anul 2286) ar avea timestamp în secunde cu 11 cifre, dar pentru aplicații obișnuite e un caz rar pe care îl poți ignora.
Fix-ul în fiecare limbaj
JavaScript (convertește în secunde înainte să trimiți la backend): `javascript const timestampSeconds = Math.floor(Date.now() / 1000); `
JavaScript (convertește secunde de la backend pentru a le folosi în Date): `javascript const date = new Date(timestampSeconds * 1000); `
Python (deja în secunde): `python import time timestamp_seconds = int(time.time()) `
PHP (deja în secunde): `php $timestamp_seconds = time(); `
SQL (PostgreSQL, extrage secunde): `sql SELECT EXTRACT(EPOCH FROM NOW())::int; `
Conversie milisecunde → secunde (oricare limbaj): ` împarte la 1000, apoi fă floor/trunchiază `
Expirarea JWT: varianta corectă
Claim-urile JWT (exp, iat, nbf) trebuie să fie în secunde conform RFC 7519. În JavaScript:
const now = Math.floor(Date.now() / 1000); // secunde
const exp = now + 86400; // peste 24 ore, în secunde
Nu: `javascript const exp = Date.now() + 86400; // greșit: bază în milisecunde + offset în secunde `
Și nu: `javascript const exp = Date.now() + (86400 * 1000); // greșit: exp ar fi în milisecunde (~28 zile în viitor) `
Un JWT cu un exp de 13 cifre va fi respins de multe biblioteci JWT ca fiind invalid sau „prea în viitor”. Instrumentul Unix timestamp te ajută să verifici ce dată reprezintă un anumit exp.
Stocarea timestamp-urilor în baze de date
Când îți proiectezi schema, fii consecvent cu unitatea:
- Dacă stochezi secunde, documentează clar. Nume de coloane precum
created_at_unixsau un comentariu „Unix timestamp în secunde” elimină ambiguitatea. - Dacă ORM-ul/framework-ul tău stochează milisecunde (unele ORMs din JavaScript fac asta), clarifică și asta.
- Evită amestecul — nu stoca unele timestamp-uri în secunde și altele în milisecunde în același sistem.
Tipul nativ PostgreSQL TIMESTAMPTZ este adesea o alegere mai bună decât întregi brutali, pentru că gestionează conversia de fus orar și formatarea nativ. Dar când folosești întregi pentru timestamp, alege o unitate și ține-te de ea.
O notă despre microsecunde
Unele sisteme merg și mai departe și folosesc microsecunde (milionimi de secundă). Python time.time_ns() întoarce nanosecunde. Sisteme de tranzacționare de mare frecvență, tool-uri de profiling și unele componente interne de baze de date pot folosi precizie la microsecundă.
Dacă vezi un întreg de 16 cifre într-un context de timp, s-ar putea să fie microsecunde. Un întreg de 19 cifre poate fi nanosecunde. Aceeași euristică se aplică: numărul de cifre îți spune ordinul de mărime.
Pentru timestamp-uri în aplicații web, milisecundele sunt mai mult decât suficient. Microsecundele contează doar în sisteme critice de performanță, cu frecvență foarte mare.
Regula practică
De fiecare dată când un timestamp trece o graniță între limbaje sau sisteme — frontend JavaScript către backend Python/PHP/Go/Java, aplicație către baza de date, aplicație către un API extern — pune întrebarea: cine folosește secunde și cine folosește milisecunde? Fă conversia la limită, documenteaz-o și testează cu o dată cunoscută ca să verifici că rezultatul este plauzibil.
Testul este simplu: convertește timestamp-ul pe care îl generezi și verifică dacă data are sens. Dacă vezi 1970 sau 55000, știi imediat că ceva nu e în regulă.


