2009-06-17 21 views
249

Estoy tratando de comparar una marca de tiempo de una solicitud entrante con un valor almacenado de la base de datos. SQL Server por supuesto mantiene una precisión de milisegundos en el tiempo, y cuando se lee en .NET DateTime, incluye esos milisegundos. La solicitud entrante al sistema, sin embargo, no ofrece esa precisión, por lo que simplemente tengo que soltar los milisegundos.Cómo truncar milisegundos fuera de .NET DateTime

Siento que me falta algo obvio, pero no he encontrado una forma elegante de hacerlo (C#).

Respuesta

442

El siguiente trabajo para un DateTime que tiene milisegundos fraccionarios, y también conserva la propiedad Kind (Local, Utc o Indefinido).

DateTime dateTime = ... anything ... 
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind 
    ); 

o el equivalente y más corto:

dateTime = dateTime.AddTicks(- (dateTime.Ticks % TimeSpan.TicksPerSecond)); 

Esto podría ser generalizado en un método de extensión:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan) 
{ 
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException 
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks)); 
} 

que se utiliza como sigue:

dateTime = dateTime.Truncate(TimeSpan.FromMilliseconds(1)); // Truncate to whole ms 
dateTime = dateTime.Truncate(TimeSpan.FromSeconds(1)); // Truncate to whole second 
dateTime = dateTime.Truncate(TimeSpan.FromMinutes(1)); // Truncate to whole minute 
... 
+0

Si bien le doy esto porque es técnicamente correcto, para personas que leen datos de SQL Server para compararlos con algunos datos distribuidos (una solicitud basada en web, en mi caso), esta cantidad de resolución no es necesaria. –

+0

Agradable. Claramente, alguien necesita darle a la clase DateTime algunos métodos de extensión para redondear a lo más cercano, para que este tipo de codificación sea reutilizable. –

+0

Esto es muy poco probable, pero ¿no se rompe este enfoque cuando ticks = 0? – adotout

27
DateTime d = DateTime.Now; 
d = d.AddMilliseconds(-d.Millisecond); 
+64

-1: Funcionará solo si el valor de DateTime no incluye fracciones de un milisegundo. – Joe

+7

Al usar este método, algunas de las pruebas de mi unidad fallaron: Esperado: 2010-05-05 15: 55: 49.000 Pero fue: 2010-05-05 15: 55: 49.000. Supongo que debido a lo que Joe mencionó sobre fracciones de un milisegundo. –

+6

No funciona para la serialización, p. 2010-12-08T11: 20: 03.000099 + 15: 00 es la salida, no corta completamente los milisegundos. – joedotnot

114
var date = DateTime.Now; 

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind); 
+25

Claro y simple, solo recuerda agregar un ", date.Kind" al final del constructor para asegurarte de que no pierdas una información importante. – JMcDaniel

+6

La única respuesta mágica aquí. – Den

+0

Tenga cuidado con esta solución en el código sensible al rendimiento. Mi aplicación estaba gastando el 12% del tiempo de CPU en [System.DateTime.GetDatePart] (https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,ff06f271f088f1a8). –

8

En lugar de dejar caer los milisegundos después comparando, por qué no comparar la diferencia?

DateTime x; DateTime y; 
bool areEqual = (x-y).TotalSeconds == 0; 

o

TimeSpan precision = TimeSpan.FromSeconds(1); 
bool areEqual = (x-y).Duration() < precision; 
+1

la primera opción no funciona, porque TotalSeconds es un doble; también devuelve los milisegundos. – Jowen

+1

Comparar la diferencia no da el mismo resultado que truncar y luego comparar. P.ej. 5.900 y 6.100 están separados por menos de un segundo, por lo que se compararían de igual manera con su método. Pero los valores truncados 5 y 6 son diferentes. Cuál es apropiado depende de su requisito. – Joe

7

menos evidente pero más de 2 veces más rápido:

// 10000000 runs 

DateTime d = DateTime.Now; 

// 484,375ms 
d = new DateTime((d.Ticks/TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond); 

// 1296,875ms 
d = d.AddMilliseconds(-d.Millisecond); 
+2

Tenga en cuenta que la segunda opción, 'd.AddMilliseconds (-d.Millisecond)', no mueve necesariamente el DateTime exactamente al segundo anterior completo. 'd.Ticks% TimeSpan.TicksPerMillisecond' ticks (en algún lugar entre 0 y 9.999) más allá de su segundo se mantendrá. – Technetium

1

En cuanto Respuesta Diadistis. Esto funcionó para mí, excepto que tuve que usar Floor para eliminar la parte fraccional de la división antes de la multiplicación. Así,

d = new DateTime((d.Ticks/TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond); 

convierte

d = new DateTime(Math.Floor(d.Ticks/TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond); 

lo que habría esperado la división de dos valores largos para dar lugar a un largo, eliminando así la parte decimal, pero se resuelve como un doble dejando la misma exacta valor después de la multiplicación.

Eppsy

38

Aquí es un método de extensión basado en una respuesta anterior que le permitirá truncar a cualquier resolución ...

Uso:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond); 
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute) 

Clase:

public static class DateTimeUtils 
{ 
    /// <summary> 
    /// <para>Truncates a DateTime to a specified resolution.</para> 
    /// <para>A convenient source for resolution is TimeSpan.TicksPerXXXX constants.</para> 
    /// </summary> 
    /// <param name="date">The DateTime object to truncate</param> 
    /// <param name="resolution">e.g. to round to nearest second, TimeSpan.TicksPerSecond</param> 
    /// <returns>Truncated DateTime</returns> 
    public static DateTime Truncate(this DateTime date, long resolution) 
    { 
     return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind); 
    } 
} 
+0

Esta es una solución realmente flexible y reutilizable, que es concisa y expresiva sin ser excesivamente prolija. Mi voto como la mejor solución. – Jaans

+0

Realmente no necesita los paréntesis alrededor de% operandos. – ErikE

+3

.. pero los parens agregan claridad, en mi opinión. –

1

2 Los métodos de extensión para las soluciones mencionadas anteriormente

public static bool LiesAfterIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind) 
    { 
     DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind); 
     compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind); 

     return thisDate > compareDate; 
    } 


    public static bool LiesAfterOrEqualsIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind) 
    { 
     DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind); 
     compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind); 

     return thisDate >= compareDate; 
    } 

uso:

bool liesAfter = myObject.DateProperty.LiesAfterOrEqualsIgnoringMilliseconds(startDateTime, DateTimeKind.Utc); 
8

A veces se quiere truncar a algo basado en el calendario, como el año o el mes. Aquí hay un método de extensión que le permite elegir cualquier resolución.

public enum DateTimeResolution 
{ 
    Year, Month, Day, Hour, Minute, Second, Millisecond, Tick 
} 

public static DateTime Truncate(this DateTime self, DateTimeResolution resolution = DateTimeResolution.Second) 
{ 
    switch (resolution) 
    { 
     case DateTimeResolution.Year: 
      return new DateTime(self.Year, 1, 1, 0, 0, 0, 0, self.Kind); 
     case DateTimeResolution.Month: 
      return new DateTime(self.Year, self.Month, 1, 0, 0, 0, self.Kind); 
     case DateTimeResolution.Day: 
      return new DateTime(self.Year, self.Month, self.Day, 0, 0, 0, self.Kind); 
     case DateTimeResolution.Hour: 
      return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerHour)); 
     case DateTimeResolution.Minute: 
      return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMinute)); 
     case DateTimeResolution.Second: 
      return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerSecond)); 
     case DateTimeResolution.Millisecond: 
      return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMillisecond)); 
     case DateTimeResolution.Tick: 
      return self.AddTicks(0); 
     default: 
      throw new ArgumentException("unrecognized resolution", "resolution"); 
    } 
} 
2

simple ...

//Remove milliseconds 
DateTime date = DateTime.Now; 
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss", null); 

Y más ...

//Remove seconds 
DateTime date = DateTime.Now; 
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm"), "yyyy-MM-dd HH:mm", null); 

//Remove minutes 
DateTime date = DateTime.Now; 
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH"), "yyyy-MM-dd HH", null); 

//and go on... 
+3

La conversión a cadenas y el análisis es una idea horrible en términos de rendimiento. –

+1

@JeffPutz cierto, pero * es * pero simple. Adecuado para una prueba automatizada donde un valor insertado y extraído de un DB pierde los tics (mi situación exacta). Sin embargo, esta respuesta podría ser incluso más simple de lo que es, ya que 'var now = DateTime.Parse (DateTime.Now.ToString())' funciona bien. –

1
DateID.Text = DateTime.Today.ToShortDateString(); 

Use ToShortDateString() //Date 2-02-2016 
Use ToShortDateString() // Time 

Y por el uso de

ToLongDateString() // its show 19 February 2016. 

: P

1

Nuevo método

String Date = DateTime.Today.ToString("dd-MMM-yyyy"); 

// Definir cadena pasan parámetro de retorno dd-mmm-aaaa 24-Feb-2016

O se muestra en la caja de texto

txtDate.Text = DateTime.Today.ToString("dd-MMM-yyyy"); 

// poner en PageonLoad

0

Para redondear a la segunda:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond) 

Reemplace con TicksPerMinute para redondear al minuto.


Si el código es sensible el rendimiento, tener cuidado de no

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second) 

Mi aplicación gastaba el 12% del tiempo de CPU en System.DateTime.GetDatePart.

Cuestiones relacionadas