System.DateTime rappresenta una data e un'ora del giorno, dall'anno 1 all'anno 9999, con una precisione di 100 nanosecondi (un "tick"). System.TimeSpan rappresenta una durata: la differenza tra due valori DateTime. Entrambi sono tipi valore immutabili (struct), quindi ogni operazione restituisce un nuovo valore.
Output:
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
Ogni esempio di questa pagina stampa le date con una stringa di formato esplicita. Il ToString() predefinito segue la cultura corrente (9/24/2026 2:30:00 PM negli Stati Uniti, 24.09.2026 14:30:00 in Germania), quindi il suo output dipende dalla macchina. I codici di formato sono nella pagina sul formato DateTime.
Una data non valida lancia un'eccezione: new DateTime(2026, 2, 30) solleva una ArgumentOutOfRangeException, così come il mese 13 o l'ora 24.
Now, UtcNow e Today
Tre proprietà statiche leggono l'orologio:
Output di esempio:
Now: 2026-09-24 18:20:41 (Local)
UtcNow: 2026-09-24 16:20:41 (Utc)
Today: 2026-09-24 00:00:00
In questo esempio il fuso orario locale è due ore avanti rispetto a UTC, quindi le prime due righe differiscono di due ore; su una macchina impostata su UTC coincidono. La proprietà Kind registra se un valore è Local, Utc o Unspecified (il valore predefinito per le date che costruisci tu). Usa DateTime.UtcNow per tutto ciò che memorizzi, registri nei log, confronti o invii a un altro sistema: non salta quando inizia o finisce l'ora legale e indica lo stesso momento su ogni server. Converti in ora locale solo quando mostri un valore a una persona.
Per misurare quanto tempo impiega il codice, usa System.Diagnostics.Stopwatch invece di sottrarre due valori DateTime.Now; ha una risoluzione molto più fine e non risente delle regolazioni dell'orologio.
Aggiungere e sottrarre tempo
AddDays, AddHours, AddMinutes, AddSeconds, AddMonths e AddYears restituiscono un nuovo DateTime. Passa un numero negativo per tornare indietro. Dato che DateTime è immutabile, il risultato va assegnato:
Output:
2026-01-31
2026-02-03 09:00
2026-01-30 21:00
2026-02-28
2027-01-31
10:30
29
True
AddMonths si ferma all'ultimo giorno del mese quando il giorno non esiste: 31 gennaio più un mese dà 28 febbraio (o 29 in un anno bisestile), non 3 marzo. Aggiungere un mese due volte e aggiungere due mesi può quindi dare date diverse.
Sottrarre date: TimeSpan
Sottrarre un DateTime da un altro dà un TimeSpan:
Output:
3.20:30:00
Days: 3, Hours: 20, Minutes: 30
TotalDays: 3.85
TotalHours: 92.5
TotalMinutes: 5550
Nights: 4
Questa è la parte dell'API su cui si sbaglia più spesso. Days, Hours, Minutes e Seconds sono le componenti dell'intervallo (3 giorni, 20 ore, 30 minuti). TotalDays, TotalHours e TotalMinutes sono l'intera durata in una sola unità, come double. "Quante ore è rimasto l'ospite?" è TotalHours (92.5), non Hours (20).
L'ultima riga mostra un punto collegato: sono trascorsi 3.85 giorni, ma l'ospite è rimasto 4 notti. Confrontare le parti .Date conta i giorni di calendario, che di solito è ciò che serve per la fatturazione e per le indicazioni del tipo "mancano X giorni".
Creare e formattare valori TimeSpan
Output:
02:15:00
01:30:00
1.12:00:00
True
03:45:00
True
02:15
36h 0m
00:00:00
TimeSpan supporta +, -, i confronti, Duration() (valore assoluto) e Negate(). I formati personalizzati come @"hh\:mm" richiedono una barra rovesciata prima dei caratteri letterali, e lì hh mostra solo la componente delle ore (da 0 a 23), quindi per durate superiori a un giorno costruisci il testo a partire da TotalHours come nella penultima riga.
Confrontare date
DateTime supporta ==, !=, <, >, <= e >=, più CompareTo e DateTime.Compare. Per confrontare solo la data ignorando l'ora, confronta le proprietà .Date:
Output:
True
True
1
True
2026-09-01
Per "questo timestamp cade entro il 30 settembre?", confronta con l'inizio del giorno successivo usando <, come sopra. Scrivere check <= end escluderebbe tutto ciò che viene dopo la mezzanotte dell'ultimo giorno, perché end è 2026-09-30 00:00:00.
I confronti guardano solo i tick, non Kind: un valore Local e un valore Utc che vengono stampati uguali sono considerati uguali anche se sono momenti diversi. Un altro motivo per memorizzare le ore in UTC.
Giorno della settimana e inizio della settimana
DayOfWeek è un enum che va da Sunday (0) a Saturday (6). L'aritmetica su di esso permette di trovare giorni feriali e confini della settimana:
Output:
Thursday
4
Weekend: False
Week starts 2026-09-21 (Monday)
Next Friday: 2026-09-25
2026-09-01 to 2026-09-30
I nomi dei giorni stampati da DayOfWeek.ToString() sono sempre in inglese. Per un nome localizzato, formatta la data con "dddd" e una cultura.
Analizzare date da stringhe
Quando conosci il formato dell'input, usa ParseExact o TryParseExact con CultureInfo.InvariantCulture. La stringa di formato usa gli stessi codici della formattazione:
Output:
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) senza formato prova a indovinare usando la cultura corrente. "03/04/2026" è il 4 marzo su una macchina statunitense e il 3 aprile su una britannica, e una data che viene analizzata senza problemi sul tuo portatile può lanciare una FormatException su un server. Tieni Parse per l'input digitato da un utente locale; usa ParseExact con la cultura invariante per file, API e database. ParseExact lancia FormatException quando il testo non corrisponde; TryParseExact restituisce invece false.
Calcolare un'età
Sottrarre le date di nascita e dividere per 365 è sbagliato vicino ai compleanni e negli anni bisestili. Confronta gli anni, poi correggi se il compleanno di quest'anno non è ancora arrivato:
Output:
36
35
18
70 days to go
DateTimeOffset
Un DateTime non registra in quale fuso orario si trova, al di là del vago flag Kind. DateTimeOffset memorizza il valore insieme al suo offset da UTC, quindi identifica sempre un momento esatto:
Output:
2026-09-24 14:00 +02:00
2026-09-24 12:00
2026-09-24 12:30
00:30:00
21:00 +09:00
Usa DateTimeOffset (o valori DateTime in UTC) per i timestamp: quando è stato fatto un ordine, quando è stato inviato un messaggio. Database e serializzatori JSON lo gestiscono bene. Per convertire tra fusi orari con nome e regole sull'ora legale, usa TimeZoneInfo.ConvertTime; gli ID dei fusi differiscono a seconda del sistema operativo nelle versioni più vecchie di .NET ("Europe/Paris" su Linux, "Romance Standard Time" su Windows), mentre .NET 6 e successivi accettano entrambi.
DateOnly e TimeOnly (.NET 6 e successivi)
Molti valori sono una data senza ora (un compleanno, una scadenza) o un'ora senza data (gli orari di apertura). .NET 6 ha aggiunto due tipi per loro:
// .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);
Eliminano una categoria di bug in cui un'ora o un fuso orario di troppo sposta una data di un giorno. Il codice più vecchio, e quello per .NET Framework o Unity, usa DateTime con l'ora lasciata a mezzanotte.
Errori comuni
- Scartare il risultato di
AddDays.DateTimeè immutabile; assegna il valore restituito. - Usare
Hoursinvece diTotalHours. Componenti contro durata totale. - Memorizzare
DateTime.Now. Memorizza in UTC e converti per la visualizzazione. - Chiamare
ToString()senza formato in log, file o test, dove l'output dipende dalla cultura della macchina. - Analizzare dati generati da macchine con
DateTime.Parse. UsaParseExacte la cultura invariante. - Confondere
mmeMMnelle stringhe di formato (minuti e mesi). Vedi il formato DateTime.
Domande frequenti
Che differenza c'è tra DateTime.Now e DateTime.UtcNow?
DateTime.Now è l'ora corrente nel fuso orario locale del computer, con Kind impostato a Local. DateTime.UtcNow è l'ora corrente in UTC, con Kind impostato a Utc, ed è anche più veloce perché salta la conversione di fuso orario. Memorizza e confronta i timestamp in UTC, e converti in ora locale solo per la visualizzazione.
Come ottengo la differenza tra due date in C#?
Sottraile: TimeSpan gap = end - start;. Poi leggi gap.TotalDays, gap.TotalHours o gap.TotalMinutes per l'intera durata come double, oppure gap.Days per la parte in giorni interi. Per mesi o anni di calendario non esiste una proprietà predefinita, perché i mesi hanno lunghezze diverse; confronta tu i campi anno e mese.
Che differenza c'è tra TimeSpan.Hours e TotalHours?
Hours è solo la componente delle ore, da 0 a 23, dopo aver tolto i giorni interi. TotalHours è l'intera durata espressa in ore, come double. Per un intervallo di 1 giorno e 3 ore, Hours vale 3 e TotalHours vale 27. Usare Hours dove serviva TotalHours è un bug molto comune.
Come analizzo una stringa di data in C#?
Quando conosci il formato, usa DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture), oppure DateTime.TryParseExact per ottenere false invece di una FormatException con un input non valido. DateTime.Parse indovina il formato dalla cultura corrente, quindi 03/04/2026 significa 4 marzo negli Stati Uniti e 3 aprile nel Regno Unito.
Perché AddDays non modifica il mio DateTime?
DateTime è un tipo valore immutabile. AddDays, AddHours e gli altri metodi restituiscono un nuovo DateTime e lasciano invariato l'originale, quindi devi assegnare il risultato: due = due.AddDays(7);.
Quando usare DateTimeOffset invece di DateTime?
Usa DateTimeOffset per i timestamp che devono identificare un momento esatto, come quando è stato fatto un ordine o scritta una voce di log, soprattutto se i dati passano tra server e fusi orari diversi. Memorizza l'offset da UTC insieme al valore. DateTime va bene per i timestamp solo in UTC e per le date senza un fuso orario significativo.