Localizzare Data e Ora

Un tema con cui prima o poi qualsiasi progetto deve fare i conti è la gestione di date e orari, time-zone e “ore legali” varie. La dritta fondamentale numero uno è: salvate nel database solo date in formato UTC – questo perché solo in questo modo vi sarà possibile gestire conversioni e sconversioni.

Cosa serve

Nell’ipotesi di un’applicazione web, immaginiamo di avere diverse tabelle contenenti una campo “Time” che identifica l’ora in cui è stato effettuato l’inserimento della riga. Nell’ipotesi in cui fino ad oggi abbiamo stampato l’orario nel modo:

@item.Time.ToString()

Abbiamo bisogno di:

  • Verificare che tutti gli orari salvati sul DB siano del tipo UTC
  • Creare un modulo che consenta all’utente di scegliere la propria TimeZone
  • Modificare il modo in cui vengono stampate le diverse ore

Il primo problema si può risolvere anche con un semplice “Cerca e Sostituisci” per passare da DateTime.Now a DateTime.UtcNow.

Un Controller per selezionare il TimeZone

Ha senso immagazzinare il TimeZone scelto dall’utente nel DB piuttosto che in un cookie. Potete inserire una colonna nella della tabella degli utenti o creare una tabella a parte se dovete immagazzinare più dati specifici per la localizzazione (ad esempio valuta preferita, lingua, formato per le date…).
Potete ottenere l’elenco dei diversi TimeZone disponibili definendo una proprietà di sola lettura come questa:

		public static List<TimeZoneInfo> TimeZones { get
			{
				return TimeZoneInfo.GetSystemTimeZones().ToList<TimeZoneInfo>();
			}
		}

A questo punto potete stampare l’elenco generando una DropDownList. Nell’esempio qui sotto ipotizzo un Controller che espone un ViewModel con la proprietà “TimezoneId”, in cui devo inserire proprio il TimeZone scelto dall’utente.

@Html.DropDownListFor(m => m.TimezoneId, new SelectList(Config.TimeZones, "Id", "DisplayName"))

La procedura di salvataggio ovviamente dipende dal vostro progetto.

Visualizzazione dell’orario aggiornato

Piuttosto che scrivere un metodo in una classe apposita, io suggerisco di scrivere un Extension Method per l’oggetto DateTime. E’ importante segnalare al sistema che la data di ingresso sia del tipo UTC:

		public static DateTime Localize(this DateTime time)
		{
			time = DateTime.SpecifyKind(time, DateTimeKind.Utc);
			Account account = AgentAccount.Find(HttpContext.Current.User.Identity.Name);
			TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(account.TimeZone);
			DateTime t = TimeZoneInfo.ConvertTime(time, tz);
			return t;
		}

Nell’esempio sopra ho ipotizzato una classe “Account” che mappa la tabella degli utenti.
Ora per stampare l’orario corretto sarà sufficiente:

@item.Time.Localize().ToString()