System.DateTime représente une date et une heure de la journée, de l'an 1 à l'an 9999, avec une précision de 100 nanosecondes (un « tick »). System.TimeSpan représente une durée : la différence entre deux valeurs DateTime. Tous deux sont des types valeur immuables (des structs), donc chaque opération renvoie une nouvelle valeur.
Sortie :
2026-09-24 00:00:00
2026-09-24 14:30:00
14:30:05.250
2026 9 24
14:30
Thursday
267
2026-09-24 00:00
14:30:00
Chaque exemple de cette page affiche les dates avec une chaîne de format explicite. Le ToString() par défaut suit la culture courante (9/24/2026 2:30:00 PM aux États-Unis, 24.09.2026 14:30:00 en Allemagne), donc sa sortie dépend de la machine. Les codes de format se trouvent sur format DateTime.
Une date invalide lève une exception : new DateTime(2026, 2, 30) déclenche une ArgumentOutOfRangeException, tout comme le mois 13 ou l'heure 24.
Now, UtcNow et Today
Trois propriétés statiques lisent l'horloge :
Exemple de sortie :
Now: 2026-09-24 18:20:41 (Local)
UtcNow: 2026-09-24 16:20:41 (Utc)
Today: 2026-09-24 00:00:00
Dans cet exemple, le fuseau local a deux heures d'avance sur UTC, donc les deux premières lignes diffèrent de deux heures ; sur une machine réglée en UTC, elles sont identiques. La propriété Kind indique si une valeur est Local, Utc ou Unspecified (la valeur par défaut des dates que vous construisez vous-même). Utilisez DateTime.UtcNow pour tout ce que vous stockez, journalisez, comparez ou envoyez à un autre système : il ne saute pas au passage à l'heure d'été ou d'hiver, et il désigne le même instant sur chaque serveur. Convertissez en heure locale seulement pour montrer une valeur à une personne.
Pour mesurer la durée d'exécution d'un code, utilisez System.Diagnostics.Stopwatch plutôt que de soustraire deux valeurs DateTime.Now ; sa résolution est bien plus fine et il n'est pas affecté par les réglages de l'horloge.
Ajouter et soustraire du temps
AddDays, AddHours, AddMinutes, AddSeconds, AddMonths et AddYears renvoient un nouveau DateTime. Passez un nombre négatif pour revenir en arrière. Comme DateTime est immuable, le résultat doit être affecté :
Sortie :
2026-01-31
2026-02-03 09:00
2026-01-30 21:00
2026-02-28
2027-01-31
10:30
29
True
AddMonths se cale sur le dernier jour du mois quand le jour n'existe pas : le 31 janvier plus un mois donne le 28 février (ou le 29 lors d'une année bissextile), pas le 3 mars. Ajouter deux fois un mois et ajouter deux mois peuvent donc donner des dates différentes.
Soustraire des dates : TimeSpan
Soustraire un DateTime d'un autre donne un TimeSpan :
Sortie :
3.20:30:00
Days: 3, Hours: 20, Minutes: 30
TotalDays: 3.85
TotalHours: 92.5
TotalMinutes: 5550
Nights: 4
C'est la partie de l'API où l'on se trompe le plus souvent. Days, Hours, Minutes et Seconds sont les composantes de la durée (3 jours, 20 heures, 30 minutes). TotalDays, TotalHours et TotalMinutes sont la durée entière dans une seule unité, sous forme de double. « Combien d'heures le client est-il resté ? » correspond à TotalHours (92,5), pas à Hours (20).
La dernière ligne montre un point voisin : 3,85 jours se sont écoulés, mais le client est resté 4 nuits. Comparer les parties .Date compte les jours calendaires, ce que veulent en général la facturation et les affichages du type « dans n jours ».
Créer et formater des valeurs TimeSpan
Sortie :
02:15:00
01:30:00
1.12:00:00
True
03:45:00
True
02:15
36h 0m
00:00:00
TimeSpan prend en charge +, -, les comparaisons, Duration() (valeur absolue) et Negate(). Les formats personnalisés comme @"hh\:mm" exigent une barre oblique inverse devant les caractères littéraux, et hh y affiche seulement la composante des heures (0 à 23) ; pour les durées de plus d'un jour, construisez le texte à partir de TotalHours comme dans l'avant-dernière ligne.
Comparer des dates
DateTime prend en charge ==, !=, <, >, <= et >=, ainsi que CompareTo et DateTime.Compare. Pour ne comparer que la date en ignorant l'heure, comparez les propriétés .Date :
Sortie :
True
True
1
True
2026-09-01
Pour « cet horodatage tombe-t-il le 30 septembre ? », comparez avec < au début du jour suivant, comme ci-dessus. Écrire check <= end exclurait tout ce qui suit minuit le dernier jour, car end vaut 2026-09-30 00:00:00.
Les comparaisons ne regardent que les ticks, pas Kind : une valeur Local et une valeur Utc qui s'affichent de la même façon sont considérées égales alors qu'il s'agit d'instants différents. Une raison de plus de stocker les heures en UTC.
Jour de la semaine et début de semaine
DayOfWeek est un enum qui va de Sunday (0) à Saturday (6). Des calculs sur cet enum permettent de trouver les jours de semaine et les limites de semaine :
Sortie :
Thursday
4
Weekend: False
Week starts 2026-09-21 (Monday)
Next Friday: 2026-09-25
2026-09-01 to 2026-09-30
Les noms de jours affichés par DayOfWeek.ToString() sont toujours en anglais. Pour un nom localisé, formatez la date avec "dddd" et une culture.
Analyser des dates depuis des chaînes
Quand vous connaissez le format de l'entrée, utilisez ParseExact ou TryParseExact avec CultureInfo.InvariantCulture. La chaîne de format utilise les mêmes codes que le formatage :
Sortie :
2026-09-24 00:00
2026-09-24 18:05
'2026-02-28' -> Saturday, February 28
'2026-02-30' -> invalid
'28.02.2026' -> invalid
'' -> invalid
2026-02-28
2026-09-24 10:00 Utc
DateTime.Parse(text) sans format essaie de deviner avec la culture courante. "03/04/2026" est le 4 mars sur une machine américaine et le 3 avril sur une machine britannique, et une date qui s'analyse bien sur votre portable peut lever une FormatException sur un serveur. Réservez Parse aux saisies d'un utilisateur local ; utilisez ParseExact avec la culture invariante pour les fichiers, les API et les bases de données. ParseExact lève FormatException quand le texte ne correspond pas ; TryParseExact renvoie false à la place.
Calculer un âge
Soustraire des dates de naissance et diviser par 365 est faux autour des anniversaires et des années bissextiles. Comparez les années, puis corrigez si l'anniversaire de cette année n'est pas encore passé :
Sortie :
36
35
18
70 days to go
DateTimeOffset
Un DateTime n'enregistre pas son fuseau horaire au-delà du vague indicateur Kind. DateTimeOffset stocke la valeur avec son décalage par rapport à UTC, il désigne donc toujours un instant exact :
Sortie :
2026-09-24 14:00 +02:00
2026-09-24 12:00
2026-09-24 12:30
00:30:00
21:00 +09:00
Utilisez DateTimeOffset (ou des valeurs DateTime en UTC) pour les horodatages : quand une commande a été passée, quand un message a été envoyé. Les bases de données et les sérialiseurs JSON le gèrent bien. Pour convertir entre des fuseaux horaires nommés avec leurs règles d'heure d'été, utilisez TimeZoneInfo.ConvertTime ; les identifiants de fuseau diffèrent selon le système d'exploitation sur les anciennes versions de .NET ("Europe/Paris" sous Linux, "Romance Standard Time" sous Windows), et .NET 6 et plus acceptent les deux.
DateOnly et TimeOnly (.NET 6 et plus)
Beaucoup de valeurs sont une date sans heure (un anniversaire, une échéance) ou une heure sans date (des horaires d'ouverture). .NET 6 a ajouté deux types pour elles :
// .NET 6 and later
DateOnly birthday = new DateOnly(1990, 9, 24);
DateOnly due = DateOnly.FromDateTime(DateTime.Today).AddDays(14);
int daysLeft = due.DayNumber - DateOnly.FromDateTime(DateTime.Today).DayNumber;
TimeOnly opens = new TimeOnly(9, 0);
TimeOnly closes = new TimeOnly(17, 30);
bool isOpen = TimeOnly.FromDateTime(DateTime.Now).IsBetween(opens, closes);
Ils éliminent une catégorie de bugs où une heure ou un fuseau parasite décale une date d'un jour. Le code plus ancien, et le code qui cible .NET Framework ou Unity, utilise DateTime en laissant l'heure à minuit.
Erreurs courantes
- Ignorer le résultat de
AddDays.DateTimeest immuable ; affectez la valeur renvoyée. - Utiliser
Hoursau lieu deTotalHours. Les composantes ne sont pas la durée totale. - Stocker
DateTime.Now. Stockez en UTC et convertissez pour l'affichage. - Appeler
ToString()sans format dans des logs, des fichiers ou des tests, où la sortie dépend de la culture de la machine. - Analyser des données de machine avec
DateTime.Parse. UtilisezParseExactet la culture invariante. - Confondre
mmetMMdans les chaînes de format (minutes et mois). Voir format DateTime.
Questions fréquentes
Quelle est la différence entre DateTime.Now et DateTime.UtcNow ?
DateTime.Now est l'heure actuelle dans le fuseau horaire local de l'ordinateur, avec Kind à Local. DateTime.UtcNow est l'heure actuelle en UTC, avec Kind à Utc, et il est aussi plus rapide car il évite la conversion de fuseau. Stockez et comparez les horodatages en UTC, et convertissez en heure locale uniquement pour l'affichage.
Comment obtenir la différence entre deux dates en C# ?
Soustrayez-les : TimeSpan gap = end - start;. Lisez ensuite gap.TotalDays, gap.TotalHours ou gap.TotalMinutes pour la durée entière sous forme de double, ou gap.Days pour la partie en jours entiers. Pour des mois ou des années calendaires, il n'existe pas de propriété intégrée, car les mois ont des longueurs différentes ; comparez vous-même les champs année et mois.
Quelle est la différence entre TimeSpan.Hours et TotalHours ?
Hours est seulement la composante des heures, de 0 à 23, une fois les jours entiers retirés. TotalHours est la durée entière exprimée en heures, sous forme de double. Pour une durée de 1 jour et 3 heures, Hours vaut 3 et TotalHours vaut 27. Utiliser Hours là où TotalHours était voulu est un bug très courant.
Comment analyser une date depuis une chaîne en C# ?
Quand vous connaissez le format, utilisez DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture), ou DateTime.TryParseExact pour obtenir false au lieu d'une FormatException sur une entrée invalide. DateTime.Parse devine le format d'après la culture courante, donc 03/04/2026 signifie le 4 mars aux États-Unis et le 3 avril au Royaume-Uni.
Pourquoi AddDays ne modifie-t-il pas mon DateTime ?
DateTime est un type valeur immuable. AddDays, AddHours et les autres méthodes renvoient un nouveau DateTime et laissent l'original inchangé, vous devez donc affecter le résultat : due = due.AddDays(7);.
Quand utiliser DateTimeOffset plutôt que DateTime ?
Utilisez DateTimeOffset pour les horodatages qui doivent désigner un instant exact, comme le moment où une commande a été passée ou où une entrée de log a été écrite, surtout si les données circulent entre serveurs et fuseaux horaires. Il stocke le décalage par rapport à UTC avec la valeur. DateTime convient pour les horodatages uniquement en UTC et pour les dates sans fuseau horaire pertinent.