1. Introduzione
Se pensi che i fusi orari siano solo "aggiungere/togliere qualche ora", molto presto sentirai tutto il dolore della maggior parte dei programmatori sul pianeta. Fusi orari, cambi d'orario tra ora legale/solare, eccezioni strane quando provi a "trasformare l'orario in un'altra regione" — tutto questo può portare a bug difficili da trovare e ancora più difficili da spiegare agli utenti.
La cosa più assurda: nel mondo non esiste e non è mai esistita una "comprensione universale" del tempo — per esempio, due paesi vicini possono gestire il cambio all'ora legale in modo diverso, e a volte le decisioni vengono prese a metà anno (sì, succede davvero!). Esempio: nel 2011 Samoa ha "saltato" un giorno intero e cambiato lato rispetto alla linea del cambio data.
Ma C# e .NET ti danno strumenti potenti per almeno provarci. Oggi conosceremo il nostro salvatore — il tipo TimeZoneInfo e le sue funzioni che aiuteranno te e la tua app.
Quando e perché ti serve lavorare con i fusi orari
- Quando utenti/server si trovano in regioni diverse del mondo
- Quando salvi data e ora nel database in UTC, ma mostri all'utente nel suo orario locale
- Per calcolare orari di eventi, promemoria, eventi online ("Il webinar inizia alle 19:00 a Berlino — e nel tuo fuso?")
- Quando sviluppi sistemi aziendali/globali (tipo contabilità o CRM per business internazionali)
Panoramica del tipo TimeZoneInfo
TimeZoneInfo è una classe creata apposta in .NET per rappresentare le info su un fuso orario: offset rispetto a UTC, nome, supporto per l'ora legale ecc.
Diversamente dalla vecchia classe TimeZone, che conosce solo il fuso "locale" e "UTC", TimeZoneInfo ti dà accesso completo a tutti i fusi orari registrati nel sistema (e anche a quelli creati a mano).
2. Ottenere info sui fusi orari
Fuso locale e UTC
Ottenere info sul fuso orario locale è facilissimo:
// Locale (cioè dove gira il tuo programma)
TimeZoneInfo local = TimeZoneInfo.Local;
// Sempre UTC
TimeZoneInfo utc = TimeZoneInfo.Utc;
Console.WriteLine(local.DisplayName); // Per esempio: (UTC+01:00) Berlino, Vienna
Console.WriteLine(utc.DisplayName); // (UTC) Coordinated Universal Time
Tutti i fusi orari disponibili
Spesso serve mostrare all'utente la lista di tutti i fusi orari possibili (tipo quando si registra a un servizio):
foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
{
Console.WriteLine($"{tz.Id} | {tz.DisplayName}");
}
Questo stampa una lista tipo:
- Central European Standard Time | (UTC+01:00) Berlino, Vienna
- Pacific Standard Time | (UTC-08:00) Pacific Time (US & Canada)
- ecc.
Visualizzazione: proprietà principali di TimeZoneInfo
| Proprietà | Descrizione |
|---|---|
|
Identificatore di sistema del fuso orario (usato per la ricerca) |
|
Descrizione per l'utente (con orari e città) |
|
Nome dell'orario "standard" |
|
Nome dell'orario durante l'ora legale |
|
Offset rispetto a UTC (tipo, +01:00 per Berlino) |
|
True se il fuso supporta il cambio tra ora legale/solare |
3. Conversione dell'orario tra fusi orari
Ecco perché serve tutto questo! Devi:
- Convertire l'orario da una zona all'altra (tipo, il server salva in UTC, ma mostri all'utente nel suo fuso)
- Gestire correttamente il cambio tra ora legale/solare
Sintassi generale
DateTime utcNow = DateTime.UtcNow;
// Per esempio, convertiamo quest'orario nel fuso dell'Europa centrale
TimeZoneInfo europeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTime europeTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, europeZone);
Console.WriteLine($"UTC now: {utcNow}"); // 2024-06-20 10:30:00
Console.WriteLine($"Europe now: {europeTime}"); // 2024-06-20 11:30:00
Importante: Usa sempre UTC per i calcoli e il salvataggio interno, e solo "in uscita" converti nel fuso giusto!
Come trovare l'identificatore del fuso orario?
Su Windows gli identificatori (Id) dei fusi sono specifici (tipo "Central European Standard Time"), mentre su Linux/Unix di solito sono IANA/Olson ("Europe/Berlin", "America/New_York").
Puoi ottenere la lista degli identificatori sul tuo sistema con TimeZoneInfo.GetSystemTimeZones().
4. Conversione tra zone arbitrarie
Supponiamo che tu abbia un orario di Londra (GMT/UTC+0) e vuoi convertirlo in Tokyo (UTC+9):
DateTime londonTime = new DateTime(2024, 6, 20, 12, 0, 0, DateTimeKind.Unspecified);
TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
TimeZoneInfo tokyoZone = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
// Primo passo: converti l'orario in UTC (se è locale)
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(londonTime, londonZone);
// Secondo passo: converti UTC nell'orario di Tokyo
DateTime tokyoTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tokyoZone);
Console.WriteLine($"London: {londonTime} | UTC: {utc} | Tokyo: {tokyoTime}");
Gestire i cambi tra ora legale e solare
TimeZoneInfo tiene conto dei cambi tra ora legale/solare — se il fuso lo supporta.
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime dateWinter = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Unspecified);
DateTime dateSummer = new DateTime(2024, 7, 1, 12, 0, 0, DateTimeKind.Unspecified);
Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateWinter, eastern)); // Offset UTC-5
Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateSummer, eastern)); // Offset UTC-4 — ora legale!
Anche se gli USA (nell'esempio sopra) decidessero di abolire l'ora legale (e ogni anno ci provano!), il database dei fusi nella tua OS verrà comunque aggiornato e il tuo codice funzionerà bene.
5. Consigli pratici ed errori comuni
Eccezioni e trappole
- Nei momenti in cui si cambia orario (tipo le 2:30 di notte nel giorno del cambio tra ora legale/solare), certi orari possono essere "inesistenti" o "ripetuti":
Per esempio, nel giorno del cambio "indietro" le 2:30 succedono due volte — e sono "diverse"! - Attenzione: la lista dei fusi può cambiare con gli aggiornamenti della OS! Esempio: alcuni paesi aboliscono/introducono l'ora legale.
GO TO FULL VERSION