9 min de lecture

Timestamp Unix : secondes ou millisecondes ?

Distinguez les timestamps Unix en secondes et millisecondes, évitez les dates de 1970 et convertissez chaque unité sans ambiguïté.

Tout développeur a déjà rencontré cette anomalie déroutante : vous enregistrez un horodatage dans votre base de données, vous le transmettez à votre interface web, et au lieu d'afficher la date d'aujourd'hui, l'écran affiche 20 janvier 1970 ou 31 décembre 1969. Dans l'autre sens, un service backend peut rejeter la valeur comme hors plage.

Ces anomalies proviennent d'une source unique et omniprésente de confusion en ingénierie logicielle : la divergence entre les timestamps Unix exprimés en secondes et ceux exprimés en millisecondes.

Bien que les deux formats mesurent le temps écoulé depuis l'époque Unix (le 1er janvier 1970 à minuit UTC), les systèmes d'exploitation historiques, les bibliothèques C et de nombreuses APIs comptent ce temps en secondes, tandis que JavaScript et Java utilisent couramment les millisecondes. Transmettre un timestamp à 10 chiffres à une fonction qui en attend 13 induit un facteur d'erreur de 1 000.

Vous pouvez analyser, convertir et formater vos horodatages dans les deux échelles instantanément à l'aide du Convertisseur de Timestamp de ToolPorta. Ce guide décortique le fonctionnement de l'époque Unix, détaille les fondements mathématiques séparant les horodatages à 10 et 13 chiffres, analyse les causes racines des bugs les plus fréquents en production, fournit un algorithme de détection heuristique automatique et détaille les meilleures pratiques pour concevoir des APIs temporelles robustes.

Qu'est-ce qu'un timestamp Unix ?#

Un timestamp Unix (également appelé temps POSIX ou temps Epoch) est un entier représentant la durée totale écoulée depuis l'époque Unix :

TEXT
1Jeudi  janvier  à :: Temps Universel Coordonné (UTC)

Par convention, les secondes intercalaires sont ignorées ; chaque journée en temps Unix correspond rigoureusement à 86 400 secondes.

Le temps Unix est fondamentalement indépendant des fuseaux horaires. Un timestamp représente un instant absolu et universel sur l'ensemble du globe. Qu'un utilisateur consulte cet instant depuis Tokyo, Paris, Londres ou Montréal, la valeur numérique sous-jacente est rigoureusement identique. Les fuseaux horaires, les passages à l'heure d'été/hiver et le formatage du calendrier ne sont que des opérations de présentation destinées à la lecture humaine.

La différence fondamentale : 10 chiffres vs 13 chiffres#

La distinction essentielle entre les deux formats repose sur l'unité de granularité retenue :

AttributSecondes Unix (s)Millisecondes Unix (ms)
Unité de mesureSeconde (1 s)Milliseconde (0,001 s)
Longueur standard10 chiffres (ère contemporaine)13 chiffres (ère contemporaine)
Exemple pour 2026-10-01 00:00:00 UTC17908128001790812800000
Ordre de grandeurEnviron 1,7 × 10^9Environ 1,7 × 10^12
Opération de conversionMultiplier par 1 000 pour obtenir les msDiviser par 1 000 pour obtenir les s
PrécisionRésolution à la secondeRésolution sub-seconde (milliseconde)
Écosystèmes dominantsLinux, C/POSIX, Python time.time(), MySQL, PHPJavaScript/Node.js, Java, Android, MongoDB

L'échelle à 10 chiffres : les secondes Unix#

Pendant plus de cinq décennies, les systèmes d'exploitation Unix, les bibliothèques C standard (time_t) et les normes POSIX ont mesuré le temps exclusivement en secondes. À notre époque, tout timestamp Unix en secondes comporte exactement 10 chiffres décimaux, débutant par 1 (par exemple, 1704067200 correspond au 1er janvier 2024 à 00:00:00 UTC).

Les horodatages en secondes conserveront cette longueur de 10 chiffres jusqu'au 20 novembre 2286, date à laquelle le compteur atteindra 10000000000 (11 chiffres).

L'échelle à 13 chiffres : les millisecondes Unix#

Avec l'avènement des interfaces graphiques, des mesures de latence réseau et des environnements d'exécution modernes comme Java (1995) et JavaScript (1995), la granularité à la seconde est devenue insuffisante. Les cycles de rafraîchissement d'affichage, les interactions utilisateur et les transactions en base de données exigeaient une précision sub-seconde.

Les fonctions System.currentTimeMillis() de Java et Date.now() de JavaScript renvoient des millisecondes. Comme chaque seconde compte 1 000 millisecondes, la conversion s'effectue en multipliant les secondes par 1 000. Les trois derniers chiffres peuvent alors représenter une précision sub-seconde ; ils ne sont pas nécessairement des zéros.

À l'époque contemporaine, tous les horodatages en millisecondes mesurent 13 chiffres décimaux (par exemple, 1704067200000).

Au-delà des millisecondes : microsecondes et nanosecondes#

Les systèmes distribués à très haute fréquence utilisent des échelles encore plus fines :

  • Microsecondes (µs, souvent 16 chiffres aujourd'hui) : un millionième de seconde. PostgreSQL stocke les timestamps avec une précision en microsecondes, même si ses fonctions d'extraction peuvent renvoyer des secondes décimales.
  • Nanosecondes (ns, souvent 19 chiffres aujourd'hui) : un milliardième de seconde. Go propose time.Now().UnixNano() et Python time.time_ns(). Une horloge monotone comme CLOCK_MONOTONIC mesure une durée, pas un instant Unix.

Deux bugs de production majeurs causés par la confusion d'unités#

Lorsque deux composants d'une architecture échangent des horodatages sans expliciter leur unité de mesure, des dysfonctionnements sévères se manifestent.

Bug 1 : L'affichage inopiné du « 20 janvier 1970 » côté frontend#

Il s'agit du bug temporel le plus répandu du développement web.

Une API backend (développée en Python, Go ou PHP) renvoie un timestamp de création en secondes :

JSON
1{2  "id": "cde_98412",3  "createdAt": 4}

Le développeur frontend récupère cette valeur et la passe directement au constructeur d'objet Date de JavaScript :

JavaScript
1// ERREUR : Le constructeur Date() de JavaScript attend des MILLISECONDES !2const date = new Date();3console.log(date.toISOString());4// Sortie : 1970-01-20T17:21:07.200Z

Pourquoi ce résultat ?

Le développeur souhaitait afficher le 1er janvier 2024. Cependant, comme new Date(valeur) attend des millisecondes, le moteur a interprété 1704067200 comme 1 704 067 200 millisecondes après l'époque Unix.

En convertissant, 1 704 067 200 ms représentent 1 704 067,2 secondes, soit environ 19,72 jours après le début de l'époque Unix. On arrive donc au 20 janvier 1970.

Une date inattendue en janvier 1970 est donc un indice fort qu'une valeur en secondes a été interprétée comme des millisecondes.

Bug 2 : date hors plage ou dépassement côté backend#

L'erreur inverse se produit lorsqu'une application cliente ou un service Java envoie un timestamp à 13 chiffres à un backend qui attend des secondes.

Considérons une requête envoyant 1704067200000 à une application Python :

Python
1import datetime23# ERREUR : fromtimestamp() attend des SECONDES !4timestamp_client = 5try:6    date = datetime.datetime.fromtimestamp(timestamp_client, tz=datetime.timezone.utc)7except (OverflowError, OSError, ValueError) as erreur:8    print(f"Timestamp hors plage : {erreur}")

Dans une colonne SQL INT signée 32 bits, la valeur 1704067200000 dépasse la limite maximale de 2 147 483 647. Selon le moteur et sa configuration, l'insertion est rejetée, tronquée ou signalée comme hors plage ; elle ne doit pas être décrite comme un « crash » garanti.

Panorama des écosystèmes : qui utilise quoi ?#

Les langages de programmation et les bases de données possèdent des conventions bien ancrées :

Écosystème / OutilUnité par défautFonction / MéthodeExemple (2026)
JavaScript / TypeScriptMillisecondesDate.now() / new Date().getTime()1790812800000
Navigateurs webDurée monotone en millisecondesperformance.now()1423.800000 (pas un timestamp Unix)
JavaMillisecondesSystem.currentTimeMillis()1790812800000
PythonSecondes (Flottant)time.time()1790812800.41295
GoSecondes / Nanostime.Now().Unix() / UnixNano()1790812800
PHPSecondestime()1790812800
C / C++Secondestime(NULL)1790812800
RubySecondes (Flottant)Time.now.to_f1790812800.125
MySQL / MariaDBSecondesUNIX_TIMESTAMP()1790812800
PostgreSQLSecondes (Flottant)EXTRACT(EPOCH FROM NOW())1790812800.681
MongoDB ObjectIdDate dérivée de secondesObjectId.getTimestamp()objet Date à la seconde près
API StripeSecondesPropriété created1790812800

La règle générale est simple : les systèmes proches du système d'exploitation, du noyau Linux ou des normes C/POSIX utilisent les secondes. Les environnements orientés navigateur, interface utilisateur ou machine virtuelle JVM utilisent les millisecondes.

Détection défensive d'un timestamp ambigu#

Lorsque votre application traite des données issues de webhooks externes, de fichiers CSV importés ou de microservices hétérogènes, vous ne connaissez pas toujours l'unité de l'entier reçu.

Les ordres de grandeur peuvent aider pour des données contemporaines, mais ils ne prouvent pas l'unité : les dates anciennes, négatives ou très futures sont ambiguës. Une API doit déclarer son unité ; un importeur peut utiliser des plages explicites et rejeter le reste.

TEXT
1Grille de détection par seuils (valable pour les années ~ à ~) :2-----------------------------------------------------------------------3 chiffres  (    à    )         -> SECONDES4 chiffres  (     à     ) -> MILLISECONDES5 chiffres  (^ à ^)                         -> MICROSECONDES6 chiffres  (^ à ^)                         -> NANOSECONDES

Implémentation en JavaScript et TypeScript#

TypeScript
1export interface TimestampNormalise {2  millisecondes: bigint;3  date: Date;4  uniteDetectee: "secondes" | "millisecondes" | "microsecondes" | "nanosecondes";5}67export function normaliserTimestamp(valeur: bigint | number | string): TimestampNormalise {8  const entier = BigInt(valeur);9  const absolu = entier <  ? -entier : entier;10  let millisecondes: bigint;11  let uniteDetectee: TimestampNormalise["uniteDetectee"];1213  if (absolu >=  && absolu <= ) {14    millisecondes = entier * ;15    uniteDetectee = "secondes";16  } else if (absolu >=  && absolu <= ) {17    millisecondes = entier;18    uniteDetectee = "millisecondes";19  } else if (absolu >=  && absolu <= ) {20    millisecondes = entier / ;21    uniteDetectee = "microsecondes";22  } else if (absolu >=  && absolu <= ) {23    millisecondes = entier / ;24    uniteDetectee = "nanosecondes";25  } else {26    throw new Error("Unité ambiguë : fournissez-la explicitement");27  }2829  const valeurDate = Number(millisecondes);30  const date = new Date(valeurDate);31  if (!Number.isSafeInteger(valeurDate) || Number.isNaN(date.getTime())) {32    throw new RangeError("Date hors de la plage prise en charge par JavaScript");33  }34  return { millisecondes, date, uniteDetectee };35}3637// Démonstration :38console.log(normaliserTimestamp().date.toISOString());39// Sortie : "2024-01-01T00:00:00.000Z" (Secondes détectées automatiquement !)4041console.log(normaliserTimestamp().date.toISOString());42// Sortie : "2024-01-01T00:00:00.000Z" (Millisecondes détectées automatiquement !)

Implémentation en Python#

Python
1from datetime import datetime, timezone23def analyser_timestamp_ambigu(ts: int) -> tuple[datetime, str]:4    valeur = int(ts)5    absolu = abs(valeur)67    if  <= absolu <= :8        secondes, unite = valeur, "secondes"9    elif  <= absolu <= :10        secondes, unite = valeur / , "millisecondes"11    elif  <= absolu <= :12        secondes, unite = valeur / , "microsecondes"13    elif  <= absolu <= :14        secondes, unite = valeur / , "nanosecondes"15    else:16        raise ValueError("Unité ambiguë : fournissez-la explicitement")1718    return datetime.fromtimestamp(secondes, tz=timezone.utc), unite1920# Tests :21dt1, u1 = analyser_timestamp_ambigu()22print(f"{dt1.isoformat()} (Détecté : {u1})")23# Sortie : 2024-01-01T00:00:00+00:00 (Détecté : secondes)2425dt2, u2 = analyser_timestamp_ambigu()26print(f"{dt2.isoformat()} (Détecté : {u2})")27# Sortie : 2024-01-01T00:00:00+00:00 (Détecté : millisecondes)

Bonnes pratiques pour l'architecture d'APIs et de bases de données#

Afin d'éviter définitivement les corruptions d'horodatages dans vos systèmes :

  1. Explicitez l'unité dans le nom du champ : Dans vos schémas JSON et vos colonnes SQL, incluez systématiquement l'unité. Préférez created_at_ms ou expiration_epoch_sec à des dénominations ambiguës comme timestamp ou time.
  2. Adoptez la norme ISO 8601 pour vos APIs publiques : Pour vos interfaces REST et GraphQL destinées à des tiers, privilégiez les chaînes au format ISO 8601 (ex. 2026-10-01T12:00:00.000Z). Ces chaînes sont auto-explicatives, lisibles dans les logs réseaux et suppriment tout risque de mauvaise interprétation d'unité.
  3. Utilisez des entiers 64 bits en base de données : N'employez jamais de colonnes INT 32 bits pour stocker des dates. Utilisez systématiquement le type BIGINT (8 octets) pour garantir la compatibilité avec les millisecondes et vous prémunir du bug de l'an 2038.
  4. Imposez le temps UTC sur le serveur : Tous les calculs et stockages temporels doivent être effectués en UTC. N'appliquez jamais de décalage horaire local aux valeurs stockées dans la base de données.

Foire aux questions#

Comment déterminer si un timestamp est en secondes ou en millisecondes ?#

Pour une date positive contemporaine, 10 chiffres indiquent généralement des secondes et 13 chiffres des millisecondes. Il s'agit d'un indice, pas d'une règle universelle : une date ancienne, négative ou très future exige une unité explicite. Un résultat en 1970 obtenu avec new Date() constitue un indice fort que des secondes ont été fournies à la place de millisecondes.

Pourquoi JavaScript utilise-t-il les millisecondes alors qu'Unix utilise les secondes ?#

Unix a été conçu entre 1969 et 1971 pour des systèmes d'exploitation et des systèmes de fichiers où une précision à la seconde était amplement suffisante et où la mémoire était très limitée (registres 32 bits). JavaScript a été conçu en 1995 pour les interfaces de navigation web, les animations et la gestion des événements utilisateurs, contextes où une granularité sub-seconde est indispensable.

Un timestamp Unix peut-il être négatif ?#

Oui. Les timestamps négatifs représentent les dates antérieures au 1er janvier 1970. Par exemple, la valeur -315619200 correspond au 1er janvier 1960 à 00:00:00 UTC. Les entiers signés 64 bits gèrent sans aucune difficulté des dates négatives remontant à des centaines de millions d'années dans le passé.

Les timestamps Unix intègrent-ils les secondes intercalaires ?#

Non. La norme POSIX stipule explicitement que chaque journée civile comprend rigoureusement 86 400 secondes. Lors de l'ajout officiel d'une seconde intercalaire par l'IERS, les horloges Unix répètent généralement une seconde ou appliquent un lissage d'horloge (« leap second smearing ») sur 24 heures pour éviter d'insérer une 61e seconde dans le compteur numérique.

Que se passe-t-il lors de la conversion entre flottants et entiers ?#

Python et PostgreSQL représentent fréquemment les horodatages en secondes décimales (ex. 1704067200.500). Pour obtenir des millisecondes entières, multipliez d'abord par 1 000, puis choisissez explicitement un arrondi ou une troncature, par exemple Math.floor(secondesFlottantes * 1000). Convertir d'abord en entier supprimerait la précision sub-seconde.

Outils ToolPorta complémentaires#

Inspectez, convertissez et validez vos données temporelles en toute confidentialité dans votre navigateur :