Introducción: ¿Por qué te duele la cabeza con las fechas?
Si alguna vez has escrito código en C# y te has enfrentado a la tarea de transferir una fecha y hora entre un servidor en Moscú y un cliente en Nueva York, seguramente habrás notado errores extraños: «¿Por qué tengo mañana?», «¿De dónde salieron las 3 horas?». ¿Te suena familiar?
En .NET hay dos herramientas principales para trabajar con fechas: DateTime y DateTimeOffset. A primera vista son similares: ambas almacenan fecha y hora. Pero la diferencia entre ellas es clave, y una elección incorrecta puede provocar errores catastróficos en la lógica de negocio.
En este tutorial analizaremos DateTime vs DateTimeOffset con ejemplos reales, entenderemos cuándo usar cada una y olvidaremos para siempre la confusión con las zonas horarias. ¡Allá vamos!
¿Qué es DateTime? (Y por qué es una trampa)
DateTime es una estructura que almacena fecha y hora. Existe en .NET desde la primera versión. Pero tiene una particularidad: no almacena información sobre la zona horaria. O mejor dicho, la almacena, pero solo como una bandera Kind:
DateTimeKind.Unspecified— zona no especificada (por defecto).DateTimeKind.Utc— hora UTC.DateTimeKind.Local— hora local del sistema.
El problema es que esta bandera es solo información. No convierte la hora automáticamente. Imagina que guardas en la base de datos un DateTime con Kind = Local en un servidor en Moscú. Luego lees esa fecha en un servidor en Vladivostok. ¿Qué ocurre? Correcto: el programa verá que Kind = Local y pensará que es la hora local de Vladivostok. Pero en realidad es la de Moscú. Una catástrofe absoluta.
// Ejemplo del problema con DateTimeDateTime moscowTime = new DateTime(2025, 3, 15, 14, 0, 0, DateTimeKind.Local);Console.WriteLine(moscowTime); // 15.03.2025 14:00:00 (se asume que es MSK)
// Si este código se ejecuta en un servidor con zona horaria UTC+10 (Vladivostok)// ¡DateTime se interpretará como la hora local de Vladivostok!// Lo que provocará un error de 7 horas.¿Qué es DateTimeOffset? (El salvador de los desarrolladores)
DateTimeOffset es una estructura más inteligente que apareció en .NET Framework 3.5. Almacena fecha, hora y el desplazamiento respecto a UTC (Offset). El desplazamiento es el número de horas y minutos en que tu hora local difiere de UTC.
Por ejemplo: 2025-03-15 14:00:00 +03:00 — es la hora en Moscú (UTC+3). Si pasas este valor a un servidor en Vladivostok (UTC+10), .NET entenderá: «Vale, esto es +03, por lo tanto UTC = 11:00. Ahora puedo recalcular fácilmente a la hora local: 21:00». Sin magia, matemática pura.
// Ejemplo con DateTimeOffsetDateTimeOffset moscowOffset = new DateTimeOffset(2025, 3, 15, 14, 0, 0, TimeSpan.FromHours(3));Console.WriteLine(moscowOffset); // 15.03.2025 14:00:00 +03:00
// Ahora, incluso en un servidor en Vladivostok, sabemos el desplazamiento exacto// y podemos mostrar la hora correctamente al usuarioDiferencias clave: DateTime vs DateTimeOffset
Resumamos todo en una tabla clara. Esto te ayudará a decidir rápidamente qué elegir en cada situación.
1. Almacenamiento de información de zona horaria
- DateTime: Almacena solo la bandera Kind (Unspecified, Utc, Local). No almacena el desplazamiento en sí.
- DateTimeOffset: Almacena el desplazamiento exacto respecto a UTC (por ejemplo, +03:00, -05:00).
2. Comparación de valores
- DateTime: Compara por el valor «crudo». Si tienes 10:00 UTC y 10:00 MSK (UTC+3), entonces
DateTime.Equals()dirá que son iguales. Aunque en realidad son momentos diferentes en el tiempo. - DateTimeOffset: Compara por el momento en el tiempo (UTC). 10:00 UTC y 13:00 MSK serán