El tiempo es relativo. Especialmente en el código
Imagine: está escribiendo una aplicación para reservar billetes de avión. Un usuario de Nueva York selecciona un vuelo a las 14:00 hora local, y el servidor en Londres lo guarda como DateTime.Now. ¿Qué podría salir mal? Absolutamente todo. En el mejor de los casos, el billete será tres horas antes; en el peor, el usuario perderá el vuelo y dejará una reseña furiosa.
Trabajar con zonas horarias en C# es ese tema que muchos desarrolladores posponen "para después". Pero es precisamente lo que separa el código amateur del profesional. Hoy entenderemos cómo hacer que su aplicación se lleve bien con los husos horarios, sin volverse loco con el horario de verano y sin perder ni un solo usuario.
DateTime vs DateTimeOffset: el primer combate con la realidad
Muchos principiantes en C# usan DateTime para todo. Y está bien, hasta que se enfrentan a usuarios globales. El problema es que DateTime por sí mismo no sabe en qué zona horaria se encuentra. Es solo un "momento en el tiempo" sin contexto.
// Mal: tiempo sin contextoDateTime localTime = DateTime.Now; // 14:00 — ¿pero dónde?DateTime utcTime = DateTime.UtcNow; // mejor, pero aún no es ideal
// Bien: tiempo con desplazamientoDateTimeOffset withOffset = DateTimeOffset.Now; // 14:00 +03:00Console.WriteLine(withOffset); // 06.04.2025 14:00:00 +03:00DateTimeOffset es su primer paso hacia una gestión consciente del tiempo. Almacena no solo la fecha y la hora, sino también el desplazamiento respecto a UTC. Esto ayuda cuando necesita transferir datos entre sistemas o simplemente mostrar al usuario la hora local correcta.
¿Cuándo se necesita realmente DateTime?
¿Sinceramente? Casi nunca para almacenar momentos en el tiempo. Si está escribiendo código para una aplicación de escritorio local, puede usarlo. Pero tan pronto como aparezca una base de datos, una API o varios usuarios, cambie a DateTimeOffset o DateTime con la indicación explícita de DateTimeKind.Utc.
TimeZoneInfo: su atlas personal de husos horarios
En .NET existe la potente clase TimeZoneInfo. Permite trabajar con cualquier zona horaria que conozca el sistema operativo. Y hay cientos, desde las estándar UTC+0 hasta zonas exóticas como "Lord Howe Island" con un desplazamiento de 30 minutos.
// Obtener la lista de todas las zonas disponiblesReadOnlyCollection<TimeZoneInfo> allZones = TimeZoneInfo.GetSystemTimeZones();foreach (var zone in allZones.Take(5)){ Console.WriteLine($"{zone.Id}: {zone.DisplayName}");}// Salida (ejemplo):// UTC: (UTC) Coordinated Universal Time// Russian Standard Time: (UTC+03:00) Moscow, St. Petersburg// Eastern Standard Time: (UTC-05:00) Eastern Time (US & Canada)Conversión de hora entre zonas
Supongamos que un usuario de Moscú quiere saber a qué hora comenzará un seminario web según la hora de Londres. Así es como se hace correctamente:
// Hora original — hora de MoscúDateTime moscowTime = new DateTime(2025, 6, 15, 18, 0, 0);TimeZoneInfo moscowZone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
// ConversiónDateTime londonTime = TimeZoneInfo.ConvertTime(moscowTime, moscowZone, londonZone);Console.WriteLine($"En Moscú: {moscowTime}, en Londres: {londonTime}");// En Moscú: 15.06.2025 18:00:00, en Londres: 15.06.2025 16:00:00Tenga en cuenta: indicamos explícitamente las zonas de origen y destino. Esto elimina la ambigüedad. TimeZoneInfo.ConvertTime tendrá en cuenta la diferencia horaria por sí mismo, incluyendo el horario de verano/invierno.
Horario de verano (DST): el principal enemigo del desarrollador
Dos veces al año, el mundo se vuelve loco. En marzo, los relojes se adelantan; en octubre, se atrasan. Y si su aplicación no está preparada para el DST, los usuarios verán una hora "extra" o una hora perdida. C# maneja esto automáticamente