1. Introduzione al tipo DateTime
Data e ora sono un mattone fondamentale di qualsiasi business logic, non puoi ignorarli nemmeno nei progetti più semplici. Spesso serve sapere quando un utente si è registrato, quando inviare una mail, gestire correttamente gli orari dei voli, i fusi orari — ovunque ci sono date! Ma, come spesso succede, lavorare con data e ora è uno degli argomenti più insidiosi nella programmazione. 😅
C# offre due tipi davvero importanti per lavorare col tempo: DateTime e DateTimeOffset. In breve — uno per scenari semplici, l’altro per problemi legati ai fusi orari e alla memorizzazione precisa del tempo.
Cos’è DateTime?
Il tipo DateTime è una struct definita nello spazio dei nomi System. Serve per memorizzare data e ora con una precisione fino a 100 nanosecondi (un decimo di miliardesimo di secondo).
- È un tipo valore (struct), quindi viene copiato per valore.
- Contiene sia la data che l’ora.
- Permette di salvare solo la data (2025-05-24) oppure data e ora (2025-05-24 15:17:42).
- “Non sa” nulla dei fusi orari, a meno che non venga specificato esplicitamente.
2. Creare un DateTime
Ci sono diversi modi, vediamo i principali.
1. Data e ora attuali
DateTime now = DateTime.Now; // Ora locale del computer
DateTime utcNow = DateTime.UtcNow; // Ora di Greenwich (UTC)
- Now — è “come sul tuo computer”, tiene conto del fuso orario locale di Windows.
- UtcNow — “universale”, cioè Coordinated Universal Time. Molto comodo per salvare orari, ad esempio, nei database o in sistemi che lavorano in tutto il mondo.
2. Costruttori
// Anno, mese, giorno
var date1 = new DateTime(2025, 5, 24);
// Anno, mese, giorno, ore, minuti, secondi
var date2 = new DateTime(2025, 5, 24, 12, 0, 0);
// Anno, mese, giorno, ore, minuti, secondi, millisecondi
var date3 = new DateTime(2025, 5, 24, 12, 0, 0, 500);
I mesi non partono da zero! Quindi gennaio è 1, dicembre è 12.
3. Parsing da stringa
Spesso la data arriva come stringa! Ad esempio, l’utente inserisce la data a mano.
string userInput = "2025-05-24 18:30";
DateTime parsed = DateTime.Parse(userInput); // Può lanciare un’eccezione!
Per un parsing sicuro — usiamo TryParse:
string input = "2025-05-24 18:30";
if (DateTime.TryParse(input, out DateTime result))
{
Console.WriteLine($"Convertito con successo: {result}");
}
else
{
Console.WriteLine("Impossibile convertire la stringa in data.");
}
4. Ottenere la data “senza ora”
DateTime today = DateTime.Today; // Giorno corrente, ma ora = 00:00:00
Estrarre componenti di data e ora
Tutte le proprietà si ottengono al volo:
var dt = new DateTime(2025, 5, 24, 15, 17, 42);
int year = dt.Year; // 2025
int month = dt.Month; // 5 (maggio)
int day = dt.Day; // 24
int hour = dt.Hour; // 15
int minute = dt.Minute; // 17
int second = dt.Second; // 42
DayOfWeek dow = dt.DayOfWeek; // Venerdì
3. Lavorare con il tipo DateTime
Somma e sottrazione di date
Il tempo in programmazione vola, e per non dover calcolare “quante ore mancano alla deadline”, usiamo i metodi della classe:
DateTime now = DateTime.Now;
DateTime tomorrow = now.AddDays(1); // Domani
DateTime afterFiveHours = now.AddHours(5); // Tra 5 ore
DateTime inTenMinutes = now.AddMinutes(10); // Tra 10 minuti
Differenza tra due date: TimeSpan
La differenza tra due date è una grandezza a parte, tipo TimeSpan:
DateTime deadline = new DateTime(2026, 6, 1);
DateTime now = DateTime.Now;
TimeSpan untilDeadline = deadline - now;
Console.WriteLine($"Giorni rimasti alla deadline: {untilDeadline.Days}");
Console.WriteLine($"Ore rimaste alla deadline: {untilDeadline.TotalHours:N2}");
TimeSpan — è una “differenza”, non una “data/ora specifica”!
Controllare se una data è nel futuro/passato
if (deadline > now)
Console.WriteLine("La deadline è ancora davanti!");
else
Console.WriteLine("La deadline è già passata! Consegna subito il lavoro!");
4. Come vengono salvate le date sotto il cofano
Per facilitare somme e sottrazioni tra date, i programmatori hanno deciso di salvare tutte le date come numero di secondi passati dall’inizio della nostra era. Valori del range:
| Proprietà | Valore |
|---|---|
| Data minima | = 0001-01-01 00:00:00 |
| Data massima | = 9999-12-31 23:59:59.9999999 |
| Unità di misura | 1 tick = 100 nanosecondi |
DateTime non salva solo il numero di secondi, ma il numero di “tick” (ticks) da una data speciale — 1 gennaio 0001 (eh sì, i programmatori non sanno dei dinosauri, il loro calendario è più rigido). Un tick = 100 nanosecondi.
5. Tipo DateTimeOffset
A volte il tipo DateTime non basta. Vediamo il caso classico: un’azienda lavora in due paesi, hanno fusi orari diversi, e tutti gli appuntamenti sono fissati “secondo il mio orario”. Come si fa?
L’ora salvata come DateTime spesso è “astratta”: è solo una serie di numeri e tick, finché non dici esplicitamente in quale zona era. Ad esempio, se ricevi "2025-05-24 15:00", non puoi essere sicuro — è l’ora di Nairobi? Baghdad? Berlino? (e se c’è anche l’ora legale — tieniti forte!).
Perciò, per problemi dove serve il momento preciso nel tempo, non solo la “data del calendario”, è nato il secondo tipo — DateTimeOffset. Creare un DateTimeOffset specificando l’offset (ad esempio, Nairobi +3 rispetto a UTC):
var localTime = new DateTimeOffset(2025, 5, 24, 15, 0, 0, TimeSpan.FromHours(3));
Usa DateTimeOffset se:
- Devi sapere il momento preciso (ad esempio, per eventi nei log legati all’ora del server).
- L’app lavora con utenti in diversi fusi orari.
- Devi salvare/trasmettere l’ora con il fuso (ad esempio, per transazioni, eventi, orari).
6. Come creare un DateTimeOffset?
- Momento attuale
DateTimeOffset now = DateTimeOffset.Now; // Ora locale + offset DateTimeOffset utc = DateTimeOffset.UtcNow; // UTC (offset 0) - Creazione esplicita
// Anno, mese, giorno, ora, minuto, secondo, offset (ad esempio, +2 ore) DateTimeOffset custom = new DateTimeOffset(2025, 5, 24, 20, 0, 0, TimeSpan.FromHours(2)); - Conversione da DateTime
DateTime dt = DateTime.Now; DateTimeOffset dto = new DateTimeOffset(dt); // L’offset locale viene preso automaticamente
Estrarre componenti
DateTimeOffset dto = DateTimeOffset.Now;
var datePart = dto.Date; // Solo la data
var timePart = dto.TimeOfDay; // Solo l’ora
var offset = dto.Offset; // Offset rispetto a UTC, ad esempio, 03:00:00
Conversione tra fusi orari
Magia degli offset:
// Ottieni la data in UTC
var now = DateTimeOffset.Now;
var utc = now.ToUniversalTime();
Console.WriteLine(utc); // Ora di Greenwich
// Converti in un altro fuso
var berlinTime = utc.ToOffset(TimeSpan.FromHours(2));
Console.WriteLine(berlinTime); // Per Berlino (CET +2)
7. Quando usare DateTime e quando DateTimeOffset?
Filosofia della scelta
Quando si tratta di qualcosa di semplice e locale — tipo compleanno, data di pubblicazione di un articolo o deadline per un sondaggio entro le 15:00, di solito basta DateTime. In questi casi il tempo assoluto non è critico, conta solo che l’evento sia avvenuto in un certo giorno e ora, senza legarsi al fuso orario.
Ma se serve fissare un momento preciso tenendo conto del fuso, ad esempio, nei log, nelle transazioni o nei sistemi distribuiti — meglio usare DateTimeOffset. Così non ti incasini quando passi l’ora tra sistemi diversi, soprattutto se sono in fusi orari diversi.
| Scenario | Tipo | Perché |
|---|---|---|
| Compleanno | |
Conta solo la data, il fuso non importa (di solito) |
| Ora del login | |
Serve sapere il momento preciso e l’offset |
| Scadenza prodotto | |
Data di fine, il fuso non importa |
| Ora di un evento nel log | |
Meglio sempre salvare con offset |
GO TO FULL VERSION