2009-04-20 44 views
18

Quiero redondear un DateTime a los 5 segundos más cercanos. Esta es la forma en que lo estoy haciendo actualmente, pero me preguntaba si había una forma mejor o más concisa.¿Hay alguna manera mejor en C# para redondear un DateTime a los 5 segundos más cercanos?

DateTime now = DateTime.Now; 
int second = 0; 

// round to nearest 5 second mark 
if (now.Second % 5 > 2.5) 
{ 
    // round up 
    second = now.Second + (5 - (now.Second % 5)); 
} 
else 
{ 
    // round down 
    second = now.Second - (now.Second % 5); 
} 

DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second); 

Tenga en cuenta que he encontrado thesetwo preguntas anteriores, sin embargo, truncan en lugar de redonda el tiempo.

Respuesta

29

El recuento de las garrapatas de un DateTime representa intervalos de 100 nanosegundos, por lo que puede redondear a los 5 segundos más próximos al redondear al intervalo más próximo 50000000-tick así:

DateTime now = DateTime.Now; 
    DateTime rounded = new DateTime(((now.Ticks + 25000000)/50000000) * 50000000); 

Eso es más conciso, pero no necesariamente mejor. Depende de si prefiere la brevedad y la velocidad sobre la claridad del código. La tuya es posiblemente más fácil de entender.

+4

Esto funciona bien porque 59 segundos redondeados al 5 más cercano arrojarán 60, que no se puede pasar como el parámetro 'segundos' al constructor DateTime. De esta manera evitas esa trampa. –

+0

Sí, ese es un buen punto. Me olvidé de ese problema en mi código ... – Damovisa

+1

Una posible trampa, criticar mi propia respuesta, es que no estoy seguro de cómo DateTime representa segundos intercalares. El conteo de ticks se mide a partir de las 12:00:00 de la medianoche del 1 de enero de 0001. Por lo tanto, dependiendo del número de segundos intercalares desde entonces y si DateTime los cuenta, puede encontrar que el valor de Segundos resultante no es un múltiplo de 5. – JayMcClellan

2

Como mencionaste, es bastante fácil de truncar. Entonces, solo agregue 2.5 segundos, luego trunque hacia abajo.

+0

Si agrego 2.5 segundos, trunco ​​a los 5 segundos más cercanos y restar los 2.5 segundos, terminaré con 2.5sec, 7.5sec, 12.5sec etc ... – Damovisa

+0

Así que, omitiendo el último paso (agregue 2.5 segundos) debería funcionar ... – Damovisa

1

no puedo pensar en una mejor manera, a pesar de que probablemente factorizar el método redonda:

static int Round(int n, int r) 
{ 
    if ((n % r) <= r/2) 
    { 
     return n - (n % r); 
    } 
    return n + (r - (n % r)); 
} 

Además, devuelve un int%, por lo compara con 2.5 me parece un poco extraño, a pesar de que es correcto. Utilizaría> = 3.

+0

Sí, sé lo que quiere decir con la comparación con 2.5 - se sintió un poco incómodo. Como dices, es correcto, sin embargo, y aclara cuál es la intención. 2.5 es claramente la mitad de 5, mientras que 3 parece no encajar. – Damovisa

+0

Prefiero redondear números enteros como este: '((n + (r >> 1))/r) * r' (puntos medios redondos arriba) o' ((n + r >> 1 - 1)/r) * r '(puntos medios redondos hacia abajo). Si sé que 'r' es impar, solo uso el primero porque funcionan igual para 'r'. Este enfoque utiliza solo una división (vs 3) y no ramificación en comparación con su función. –

1

No pude reconocer la diferencia entre C# y una pastilla de jabón (bueno, no pude cuando originalmente escribí esta respuesta, las cosas han cambiado bastante en los años transcurridos desde) pero, si usted está buscando un solución más concisa, me acaba de poner todo en una función - no hay mucho que va a ser más conciso en su código que una simple llamada a dicha función:

DateTime rounded = roundTo5Secs (DateTime.Now); 

Luego puede poner lo que desee en la función y solo documentar cómo funciona, como (suponiendo que todas estas sean operaciones enteras):

secBase = now.Second/5; 
secExtra = now.Second % 5; 
if (secExtra > 2) { 
    return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 
     secBase + 5); 
} 
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 
    secBase); 

Es posible que también necesite algunos controles adicionales si secBase va a 60 (a menos que los objetos C# DateTime sean lo suficientemente inteligentes como para aumentar el minuto (y la hora si el minuto pasa a 60, y así sucesivamente).

+0

Sí, buen punto. Haré esto, simplemente no lo dejé claro en la pregunta. – Damovisa

+0

El tiempo del baño para ti debe ser difícil ;-) –

+0

Bueno, @Gordon, sé un poco más sobre C# hoy en día, pero el baño es bastante difícil. Aunque eso tiene más que ver con tener hijos pequeños que con cualquier falta de coordinación de mi parte :-) – paxdiablo

1

¿Qué tal esto (combinando algunas respuestas)? Creo que transmite el significado bien y debe manejar los casos extremos (redondeando al próximo minuto) elegantemente debido a AddSeconds.

// truncate to multiple of 5 
int second = 5 * (int) (now.Second/5); 
DateTime dt = new DateTime(..., second); 

// round-up if necessary 
if (now.Second % 5 > 2.5) 
{ 
    dt = dt.AddSeconds(5); 
} 

El enfoque Ticks como se muestra por Jay es más concisa, pero puede ser un poco menos legible. Si usa ese enfoque, al menos consulte TimeSpan.TicksPerSecond.

+0

-1: no funciona si el tiempo original contiene fracciones de segundo. – Joe

+0

Tienes razón. Volví a una edición anterior que era más clara en el manejo de esa caja –

+0

¡Eliminada -1 entonces! – Joe

46

(Lo siento por la resurrección; reconozco que es una vieja y respondió a la pregunta - simplemente añadiendo un poco de código extra por el amor de Google.)

Empecé con JayMcClellan's answer, pero luego quería que fuera más genérica, el redondeo a intervalos arbitrarios (no solo 5 segundos). Así que terminé dejando el método de Jay para uno que usa Math.Round en ticks y lo puse en un método de extensión que puede tomar intervalos arbitrarios y también ofrece la opción de cambiar la lógica de redondeo (redondeo bancario versus cero fuera de cero).Quiero poner aquí en caso de que esto sea útil a alguien más, así:

public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) { 
     return new TimeSpan(
      Convert.ToInt64(Math.Round(
       time.Ticks/(decimal)roundingInterval.Ticks, 
       roundingType 
      )) * roundingInterval.Ticks 
     ); 
    } 

    public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) { 
     return Round(time, roundingInterval, MidpointRounding.ToEven); 
    } 

    public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) { 
     return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks); 
    } 

no va a ganar ningún premio por eficiencia desnudo, pero me resulta fácil de leer e intuitivo de usar. Ejemplo de uso:

new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00 
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00 
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25 
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs 
+0

Brillante, gracias - ¡justo lo que estaba buscando! – chris

+0

Este es un buen código para redondear al * más cercano * 'DateTime', pero también quiero la capacidad de redondear * up * a un múltiplo de' roundingInterval'. Intenté modificar la sobrecarga 'DateTime' para tomar también un' MidpointRounding' y luego pasarlo a la otra sobrecarga. Pero eso no agregó la capacidad deseada. – HappyNomad

+0

@HappyNomad 'MidpointRounding' solo entra en juego cuando el valor está en el punto medio real. Si siempre quieres redondear, necesitarías agregar una sobrecarga o cambiar la primera función para usar 'Math.Ceiling' en lugar de' Math.Round', e ignorar el 'roundingType' en total. –

0

Técnicamente, nunca se puede correctamente y vuelta a un intervalo extraño, ya sólo unos segundos.

2, 4, 6, 8, 10 < - no son un problema

Si va a distribuir '' veces en intervalos y si la fluctuación es baja, el truncamiento es mucho más manejable .

Si puede pasar milisegundos y redondear en una marca de 500mS, podrá odiar segundos y también recortar el efecto de jitter hacia abajo o eliminarlo por completo.

Cuestiones relacionadas