Para comparar dos valores DateTime en C#, usamos los operadores de comparación (<, >, ==) si buscamos legibilidad, o DateTime.Compare() / CompareTo() cuando necesitamos resultados aptos para ordenar. Todos comparan los ticks subyacentes y dan respuestas idénticas.
Las dos trampas reales están en otra parte: ninguno de estos métodos tiene en cuenta DateTimeKind, así que debemos convertir a UTC antes de comparar valores de zonas horarias distintas, y la precisión por debajo del segundo hace que valores “aparentemente iguales” a menudo no lo sean.
Sin embargo, antes de entrar de lleno en las distintas formas de comparar DateTime, veamos primero qué es la estructura DateTime.
¿Qué es la estructura DateTime en C#?
En C#, la estructura DateTime es un componente fundamental para manejar valores de fecha y hora.
Almacena información precisa de fecha y hora mediante un valor entero de 64 bits (ticks). Además, nos permite manipular objetos DateTime, darles formato y realizar operaciones aritméticas con ellos.
Un tick equivale a 100 nanosegundos y los ticks se cuentan desde la medianoche del 0001-01-01; por eso DateTime.MinValue es esa fecha y DateTime.MaxValue es el último tick del 9999-12-31.
El mismo valor lleva además un indicador Kind que registra si es una hora local, una hora UTC o una hora sin especificar. El indicador viaja con el valor, pero ninguna de las comparaciones siguientes lo lee, y ese único hecho está detrás de la mayoría de los errores de comparación que vemos.
Tenemos métodos como ToString() para presentar valores DateTime en representaciones de cadena concretas, y métodos como Add y Subtract para manipular los objetos DateTime.
Antes de comparar nada, conviene conocer otras dos propiedades, DateTime.Now vs DateTime.UtcNow, ya que elegir una u otra en origen determina si las comparaciones siguientes tienen siquiera sentido.
¿Cómo comparar DateTime en C#?
C# nos ofrece cuatro formas de comparar valores DateTime: los operadores relacionales, el método Equals(), el método Compare() y el método CompareTo(). Todas comparan lo mismo, el valor Ticks, un recuento de intervalos de 100 nanosegundos desde el 0001-01-01, así que siempre coinciden entre sí. Elegimos entre ellas por comodidad, no porque alguna sea más correcta.
Los operadores son los más legibles en las condiciones (if (start < end)). DateTime.Compare(a, b) y a.CompareTo(b) devuelven −1, 0 o +1, que es lo que necesitan la ordenación y las implementaciones de IComparer<T>. Equals() es simplemente == en forma de método.
Lo que ninguno de ellos hace es interpretar DateTimeKind: una hora local y una hora UTC se comparan tick a tick como si estuvieran en la misma zona, así que las comparaciones entre valores de distinto Kind dan respuestas erróneas sin avisar. Microsoft Learn lo dice para DateTime.Compare() en una sola línea: “compara la propiedad Ticks de t1 y t2, pero ignora su propiedad Kind“. La regla que nos protege: normalizar ambos valores con ToUniversalTime(), o almacenar UTC en todas partes y comparar sin preocuparnos. Si la lógica solo trabaja con fechas, comparamos la propiedad Date o convertimos a DateOnly.
Compare(DateTime, DateTime)
El método Compare(DateTime, DateTime) de la estructura DateTime devuelve un valor entero que indica la relación entre los valores de dos objetos DateTime.
Este método devuelve un valor entero según estas condiciones:
- Si el primer objeto
DateTimees anterior al segundo, devuelve un valor negativo - Si el primer objeto
DateTimees posterior al segundo, devuelve un valor positivo - Si los dos objetos
DateTimeson iguales, devuelve cero
Veámoslo con un ejemplo:
var firstDate = new DateTime(2023, 5, 1); var secondDate = new DateTime(2023, 5, 2); var result = DateTime.Compare(firstDate, secondDate);
CompareTo(DateTime)
Al igual que el método Compare(), el método CompareTo(DateTime) devuelve un valor entero que indica la relación entre los valores de dos objetos DateTime.
Este método decide el valor entero que devuelve con los mismos criterios que el método Compare():
var firstDate = new DateTime(2023, 5, 1); var secondDate = new DateTime(2023, 5, 2); var result = firstDate.CompareTo(secondDate);
CompareTo(Object)
El método CompareTo(Object) recibe un objeto como parámetro y devuelve un valor entero que indica la relación entre el valor del objeto DateTime y el del otro objeto.
Este método decide el valor entero que devuelve con los mismos criterios que los métodos Compare() y CompareTo():
var firstDate = new DateTime(2023, 5, 1); object objectDateTime = new DateTime(2023, 5, 3); var result = firstDate.CompareTo(objectDateTime);
Este método intenta convertir el otro objeto en un objeto DateTime y luego compara su valor con el del objeto DateTime original. Si el otro objeto no es un objeto DateTime, el método lanzará una ArgumentException.

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
Este método resulta útil en situaciones en las que hay que comparar un objeto DateTime con otro objeto que contiene un DateTime pero está declarado como object, por ejemplo un elemento de una colección no genérica.
Equals(DateTime)
En C#, el método Equals(DateTime) sirve para determinar si dos objetos DateTime son iguales.
DateTime.Equals(DateTime) compara el mismo valor Ticks que compara ==, así que los dos son equivalentes. Cambiar == por Equals() no resuelve por sí solo las diferencias de precisión. No cambia nada respecto a si dos valores que se imprimen igual resultan iguales al compararlos.
Este método devuelve true cuando ambos objetos representan la misma fecha y hora, y false en caso contrario:
private static void DateTimeComparisonWithEquals(DateTime firstDate, DateTime secondDate)
{
if (firstDate.Equals(secondDate))
{
Console.WriteLine($"{firstDate} is the same as {secondDate}");
}
else
{
Console.WriteLine($"{firstDate} is not the same as {secondDate}");
}
}
Comparación de DateTime con operadores relacionales
En C#, los objetos DateTime se pueden comparar con operadores relacionales como <, <=, > y >=. Estos operadores comparan los componentes de fecha y hora de dos objetos DateTime y devuelven un valor Boolean que indica si la comparación es verdadera o falsa:
private static void DateTimeComparisonWithRelationalOperator(DateTime firstDate, DateTime secondDate)
{
if (firstDate < secondDate)
{
Console.WriteLine($"{firstDate} is earlier than {secondDate}");
}
else if (firstDate > secondDate)
{
Console.WriteLine($"{firstDate} is later than {secondDate}");
}
else
{
Console.WriteLine($"{firstDate} is the same as {secondDate}");
}
}
Los operadores relacionales también se pueden combinar con el método DateTime.Equals() para comprobar si un objeto DateTime está dentro de un rango de fechas determinado. El conjunto completo de operadores de DateTime, más allá de la comparación, lo tratamos en un artículo aparte.
¿Por qué dos valores DateTime que parecen iguales no son iguales?
Como la igualdad compara los ticks de forma exacta, dos valores que se imprimen igual pueden ser distintos. Los culpables habituales: uno de los valores conserva milisegundos que el otro ha perdido (las bases de datos y los serializadores JSON suelen truncar o redondear la precisión por debajo del segundo), uno procede de DateTime.Now y el otro se ha reconstruido a partir de su cadena con formato, o los dos valores tienen distinto DateTimeKind y nunca se normalizaron.
Nuestro ejemplo concreta el primer caso: new DateTime(2021, 05, 06, 12, 0, 0) y new DateTime(2021, 05, 06, 12, 0, 0, 500) se diferencian en 500 milisegundos, así que == devuelve correctamente false aunque ambos se muestren como 12:00:00.
El caso de la base de datos merece una cifra. La antigua columna datetime de SQL Server redondea a incrementos de .000, .003 o .007 segundos, mientras que datetime2 conserva el tick completo de 100 nanosegundos, así que el mismo instante, guardado y leído de nuevo en cada uno de los dos tipos de columna, no resulta igual al compararlo (Microsoft Learn, consultado el 13 de septiembre de 2026).

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
La solución es comparar con la precisión que realmente queremos. Si solo importa la fecha, compara .Date o DateOnly. Para valores que se han guardado y leído de nuevo, compara con una tolerancia: (first - second).Duration() <= TimeSpan.FromMilliseconds(1). Y cuando las horas cruzan zonas horarias, compara valores DateTimeOffset. A diferencia de DateTime, sus operadores tienen en cuenta el desplazamiento (offset).
public static bool IsDatePrecisionSame()
{
var firstDate = new DateTime(2021, 05, 06, 12, 0, 0);
var secondDate = new DateTime(2021, 05, 06, 12, 0, 0, 500);
return firstDate == secondDate;
}
Comparar con una tolerancia, o solo por la fecha, nos da las otras dos respuestas que promete el apartado anterior:
var tolerance = TimeSpan.FromMilliseconds(1); var areClose = (firstDate - secondDate).Duration() <= tolerance; var sameDay = firstDate.Date == secondDate.Date; var sameDayViaDateOnly = DateOnly.FromDateTime(firstDate) == DateOnly.FromDateTime(secondDate);
Fíjate en que areClose sigue devolviendo un bool, así que encaja en un if exactamente igual que los operadores de igualdad que vimos antes en el artículo; sameDay y sameDayViaDateOnly son dos caminos distintos para llegar a la misma respuesta basada solo en la fecha.
Buenas prácticas para comparar DateTime
| Técnica | Devuelve | ¿Tiene en cuenta Kind? | Ideal para |
|---|---|---|---|
a == b, a < b, a >= b ... | bool | No | Comprobaciones cotidianas, la opción más legible |
a.Equals(b) | bool | No | Mismo resultado que == |
DateTime.Compare(a, b) | int (-1/0/+1) | No | Semántica de ordenación, IComparer |
a.CompareTo(b) | int (-1/0/+1) | No | Ordenación, lógica de rangos |
a.Date == b.Date / DateOnly | bool | No | Comparación solo por fecha |
(a - b).Duration() <= tolerance | bool | No | Igualdad aproximada tras almacenar o serializar |
Todas las filas comparan ticks e ignoran Kind; convertir antes ambos lados con ToUniversalTime() es cosa nuestra.
Almacena y compara en UTC. Microsoft Learn da la misma instrucción que escribiríamos nosotros: “Al guardar o compartir datos DateTime, usa UTC y establece la propiedad Kind del valor DateTime en DateTimeKind.Utc“.
public static bool IsDateInSameTimeZone()
{
var firstDate = new DateTime(2021, 05, 06, 12, 0, 0, DateTimeKind.Local);
var secondDate = new DateTime(2021, 05, 06, 12, 0, 0, DateTimeKind.Utc);
var firstDateAsUtc = firstDate.ToUniversalTime();
return firstDateAsUtc.Equals(secondDate);
}
La conversión, firstDate.ToUniversalTime(), es lo que hace correcta esta comparación; la llamada a Equals() del final no hace más trabajo del que haría ==. Si te saltas la conversión, comparar una hora local con una hora UTC compara, sin avisar, los ticks equivocados.
Elige la precisión de forma deliberada. Si solo importa la fecha, compara .Date, o recurre a DateOnly y TimeOnly en C# cuando la parte de la hora no importa en absoluto; para valores que se han guardado o serializado y leído de nuevo, compara con una pequeña tolerancia en lugar de esperar una coincidencia exacta.
En la elección DateTimeOffset vs DateTime, es preferible DateTimeOffset para todo lo que cruce zonas horarias. Sus operadores de comparación tienen en cuenta el desplazamiento directamente, así que no tenemos que normalizar a UTC a mano como exige DateTime.
Conclusión
En este artículo, hemos visto la estructura DateTime, distintas formas de comparar valores DateTime con algunos ejemplos y buenas prácticas para la comparación de DateTime en C#.
Probado con .NET 10.0.10.