2009-09-08 23 views
94

Quiero redondear fechas/tiempos al intervalo más cercano para una aplicación de gráficos. Me gustaría una firma como método de extensión de la siguiente manera para que el redondeo se puede lograr por cualquier nivel de precisión:Rounding DateTime objects

static DateTime Round(this DateTime date, TimeSpan span); 

La idea es que si paso en un intervalo de tiempo de diez minutos, se redondeará a la más cercana intervalo de diez minutos. No entiendo la implementación y espero que alguno de ustedes haya escrito o usado algo similar antes.

Creo que la implementación en el piso, en el techo o más cercana está bien.

¿Alguna idea?

Edit: Gracias a @tvanfosson & @ShuggyCoUk, la puesta en práctica se parece a esto:

public static class DateExtensions { 
    public static DateTime Round(this DateTime date, TimeSpan span) { 
     long ticks = (date.Ticks + (span.Ticks/2) + 1)/ span.Ticks; 
     return new DateTime(ticks * span.Ticks); 
    } 
    public static DateTime Floor(this DateTime date, TimeSpan span) { 
     long ticks = (date.Ticks/span.Ticks); 
     return new DateTime(ticks * span.Ticks); 
    } 
    public static DateTime Ceil(this DateTime date, TimeSpan span) { 
     long ticks = (date.Ticks + span.Ticks - 1)/span.Ticks; 
     return new DateTime(ticks * span.Ticks); 
    } 
} 

y se llama así:

DateTime nearestHour = DateTime.Now.Round(new TimeSpan(1,0,0)); 
DateTime minuteCeiling = DateTime.Now.Ceil(new TimeSpan(0,1,0)); 
DateTime weekFloor = DateTime.Now.Floor(new TimeSpan(7,0,0,0)); 
... 

Salud!

+1

Algunas de las implementaciones aquí podría ayudar también: http://stackoverflow.com/questions/766626/is-there- a-better-way-in-c-to-round-a-datetime-to-the-nearest-5-seconds –

+1

posible duplicado de [¿Hay una mejor manera de recortar un DateTime a una precisión específica?] (http: //stackoverflow.com/questions/152774/is-there-a-better-way-to-trim-a-datetime-to-a-specific-precision) –

+1

No te olvides de agregar el DateTimeKind original a la fecha recién creada ex : nuevo DateTime (ticks * span.Ticks, date.Kind); –

Respuesta

112

piso

long ticks = date.Ticks/span.Ticks; 

return new DateTime(ticks * span.Ticks); 

Ronda (hasta el punto medio)

long ticks = (date.Ticks + (span.Ticks/2) + 1)/ span.Ticks; 

return new DateTime(ticks * span.Ticks); 

techo

long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks; 

return new DateTime(ticks * span.Ticks); 
2

Simplemente use los Ticks, usando eso para dividir, piso/ceil/redondee el valor, y multiplíquelo de regreso.

10

También debe quedar claro si quiere que su redondeo a:

  1. al inicio, al final o al medio del intervalo
    • el inicio es el más fácil ya menudo el esperado, pero debe tener en cuenta su especificación inicial.
  2. Cómo desea que se redondeen los casos de límite.
    • Normalmente solo es un problema si se está redondeando hacia el centro en lugar de hacia el final.
    • Dado que el redondeo al medio es un intento de una respuesta libre de sesgo, necesita usar algo como Bankers Rounding técnicamente la mitad redonda, incluso para estar verdaderamente libre de sesgo.

es muy probable que realmente sólo se preocupan por el primer punto pero en estas preguntas 'simples' el comportamiento resultante puede tener consecuencias de largo alcance como se utiliza en el mundo real (a menudo en los intervalos adyacente a cero)

La solución de tvanfosson cubre todos los casos enumerados en 1. El ejemplo de punto medio está sesgado hacia arriba. Es dudoso que esto sea un problema en el redondeo relacionado con el tiempo.

28

Esto le permitirá redondear a cualquier intervalo dado.También es un poco más rápido que dividir y luego multiplicar los tics.

private static DateTime Floor(DateTime dateTime, TimeSpan interval) 
{ 
    return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks)); 
} 

private static DateTime Ceiling(DateTime dateTime, TimeSpan interval) 
{ 
    var overflow = dateTime.Ticks % interval.Ticks; 

    return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow); 
} 

private static DateTime Round(DateTime dateTime, TimeSpan interval) 
{ 
    var halfIntervalTicks = (interval.Ticks + 1) >> 1; 

    return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks)); 
} 
-3

Si lo que desea es reunir a la hora de Valor de techo

Console.WriteLine(DateTime.Now.ToString("M/d/yyyy hh:00:00")); 
+0

OP pidió un DateTime como el objeto que regresa. –