datetime en Python : représenter une date et une heure

datetime représente une date par un objet plutôt que par du texte : la comparer, lui ajouter un délai ou l'afficher redeviennent de simples opérations.
7 min de lecture
Believemy logo

Définition

Une date, dans un programme, n'est presque jamais là pour être seulement affichée. On veut savoir si elle est passée, la comparer à une autre, lui ajouter un délai. Tant qu'elle reste du texte, aucune de ces questions n'a de réponse simple : le programme voit une suite de caractères, pas un point dans le calendrier.

datetime est le module de la bibliothèque standard qui résout ce problème. Il représente une date, une heure, ou les deux, sous la forme d'un objet qui connaît sa propre place dans le temps. Aucune installation avec pip n'est nécessaire, un import suffit.

PYTHON
from datetime import datetime, timedelta

maintenant = datetime.now()
livraison = maintenant + timedelta(days=45)   # 45 jours plus tard, bissextiles compris
print(livraison.strftime("%d/%m/%Y"))         # 07/09/2026, par exemple

Tout le service rendu tient dans la deuxième ligne. Avec une chaîne de caractères, ajouter quarante-cinq jours suppose de connaître la longueur de chaque mois et le sort de février. Avec un objet, c'est une addition, et le module se charge du reste.


Les quatre objets à connaître

Le module expose plusieurs classes, mais quatre couvrent presque tous les besoins réels. Dans le tableau qui suit, la colonne du milieu est celle qui décide : le choix de l'objet dépend uniquement de ce que vous avez besoin de retenir.

ObjetCe qu'il contientExemple d'usage
dateUne année, un mois, un jourUne date de naissance, une échéance
timeDes heures, des minutes, des secondesUne heure d'ouverture
datetimeLes deux réunisL'horodatage d'un événement
timedeltaUne durée, et non un instantL'écart entre deux dates

Une arithmétique relie ces objets, et elle explique la plupart des erreurs de débutant : soustraire deux dates donne une durée, donc un timedelta, et ajouter une durée à une date redonne une date. Additionner deux dates, en revanche, ne désigne aucun moment, et Python le refuse.

Ces objets font par ailleurs partie de tout ce qui est immuable en Python. Une opération ne modifie jamais l'objet de départ, elle en renvoie un nouveau : appeler replace sur une date sans récupérer le résultat ne change donc rien du tout, et sans le moindre message d'erreur.


Ce qu'il remplace

Deux habitudes précèdent en général la découverte du module. Chacune fonctionne un temps, puis casse sur un cas précis.

La première consiste à garder les dates en texte. Pour afficher, c'est parfait ; pour comparer, c'est faux. « 31/01/2026 » passe pour supérieur à « 01/02/2026 », parce que deux chaînes se comparent caractère par caractère : le 3 face au 0 tranche avant même qu'on ait regardé le mois. Trier une liste de dates textuelles produit ainsi un ordre sans rapport avec le calendrier.

Une exception explique pourquoi certains ne rencontrent jamais le problème : le format ISO 8601, qui écrit l'année, puis le mois, puis le jour, aligne l'ordre des caractères sur l'ordre du temps. Le hasard est heureux, mais il disparaît dès qu'un fichier arrive au format français.

La seconde habitude consiste à tout mener avec le module time et ses horodatages, ces nombres de secondes écoulées depuis 1970. Le calcul y est juste, ce qui la rend tenace. Mais un nombre à dix chiffres ne se relit pas et ne dit rien du fuseau, là où datetime garde la même précision en restant lisible dans un journal produit par logging.


Écrire une date, puis la relire

Deux opérations reviennent ensuite sans arrêt : montrer la date à un humain, et relire ce qu'un humain ou un fichier a écrit. Deux méthodes aux noms presque identiques s'en chargent. Le f de strftime signifie format : elle part d'un objet et fabrique du texte. Le p de strptime signifie parse : elle fait exactement l'inverse.

PYTHON
from datetime import datetime

# Du texte vers l'objet : le motif décrit le texte reçu
rendez_vous = datetime.strptime("24/07/2026 09:30", "%d/%m/%Y %H:%M")

# De l'objet vers le texte : le motif décrit le texte voulu
print(rendez_vous.strftime("%d %B %Y"))

# Au format ISO : aucun motif à écrire
print(datetime.fromisoformat("2026-07-24T09:30:00"))

Dans les deux sens, le motif doit décrire le texte au caractère près, séparateurs compris. Un tiret là où le fichier porte une barre oblique lève une ValueError, première cause d'échec sur un import de données : le programme avale dix mille lignes sans broncher, puis s'arrête net sur les trois qui sortent du format annoncé.

La dernière ligne montre l'échappatoire. Entre deux programmes, la paire isoformat et fromisoformat supprime la question du motif, puisque la norme fixe l'écriture. Gardez strptime pour les textes dont vous ne choisissez pas le format.

Bon à savoir

Le motif %B rend le nom du mois dans la langue configurée sur la machine qui exécute le code. La même ligne affiche donc « juillet » sur un poste français et « July » sur un serveur réglé en anglais. Quand la langue d'affichage doit être un choix, formatez le numéro du mois et traduisez-le vous-même.


Le piège du fuseau horaire

Reste ce qu'un objet datetime ne dit pas forcément : à quel endroit du monde son heure a été lue. C'est la distinction la plus importante du module, et la seule qui produise des bugs impossibles à reproduire depuis son propre poste. La forme naïve indique neuf heures trente sans dire neuf heures trente où ; la forme consciente porte en plus un décalage, donc un instant unique sur la planète. La méthode now rend la première par défaut.

PYTHON
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

naive = datetime.now()                  # une heure de montre, fuseau inconnu
instant = datetime.now(timezone.utc)    # un instant, sans ambiguïté
affichage = instant.astimezone(ZoneInfo("Europe/Paris"))  # le même instant, vu de Paris

La bonne pratique tient dans ces trois lignes : calculer et stocker en UTC, ne convertir qu'au dernier moment, pour l'écran. La conversion ne déplace pas l'instant, elle change seulement le point de vue.

Attention

Comparer ou soustraire une forme naïve et une forme consciente lève une TypeError qui parle de valeurs offset-naive et offset-aware : Python refuse de deviner le fuseau manquant. Le cas type est une date relue en base de données, consciente, confrontée à une heure locale produite par le programme. La méthode utcnow rendait justement une forme naïve affichant l'heure UTC, et elle est dépréciée depuis Python 3.12.


Quand il ne suffit pas

Une durée en mois n'existe pas dans le module, et ce n'est pas un oubli : timedelta compte des jours et des secondes, parce que « le 31 janvier plus un mois » n'a pas de réponse universelle. Ajouter trente jours produit une échéance fausse la moitié de l'année, quand la bibliothèque dateutil et son relativedelta tranchent selon une convention explicite.

Relire une date écrite librement par un humain ne relève pas davantage de strptime, ni d'une expression régulière rallongée à chaque nouveau cas : le module attend un motif connu à l'avance. Mesurer un temps d'exécution appartient à time.perf_counter, l'horloge murale pouvant reculer après une resynchronisation réseau et rendre une durée négative. Et pour une colonne entière de dates, pandas apporte ses propres types, taillés pour le volume.


Questions fréquentes

Question

Pourquoi écrit-on from datetime import datetime ?

Parce que le module et la classe principale qu'il contient portent le même nom. Un import simple oblige ensuite à écrire datetime.datetime.now(), une redondance que la forme avec from supprime en important directement la classe voulue. La ligne dit seulement ceci : dans le module datetime, prendre l'objet datetime.

Question

Comment ajouter un mois à une date ?

En choisissant d'abord la règle, car aucune ne convient à tous les cas : le 31 janvier plus un mois vaut le 28 février pour un échéancier de facturation, qui ne doit pas déborder sur mars, et le 3 mars pour un compteur de jours. La convention arrêtée, elle s'écrit avec la méthode replace sur le mois, ou se délègue à relativedelta, qui applique la première.

Question

Faut-il stocker les dates en UTC ou en heure locale ?

En UTC, avec le fuseau attaché, en ne convertissant qu'au moment de l'affichage. Une heure locale enregistrée sans décalage devient indécodable la nuit du passage à l'heure d'hiver, où deux heures trente survient deux fois sans que rien ne distingue les deux. Le format ISO 8601 produit par isoformat est d'ailleurs celui qu'attend un échange en JSON. Ces réflexes se prennent en construisant un vrai projet, ce que propose la formation Python.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.