Arrotondamento di oggetti DateTime


105

Voglio arrotondare le date / ore all'intervallo più vicino per un'applicazione di creazione di grafici. Vorrei una firma del metodo di estensione come segue in modo che l'arrotondamento possa essere ottenuto per qualsiasi livello di precisione:

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

L'idea è che se passo in un lasso di tempo di dieci minuti, verrà arrotondato all'intervallo di dieci minuti più vicino. Non riesco a capire l'implementazione e spero che uno di voi abbia già scritto o utilizzato qualcosa di simile.

Penso che un pavimento, un soffitto o un'implementazione più vicina vada bene.

Qualche idea?

Modifica: grazie a @tvanfosson e @ShuggyCoUk, l'implementazione è simile a questa:

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);
    }
}

E si chiama così:

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));
...

Saluti!


1
Anche alcune delle implementazioni qui potrebbero aiutare: stackoverflow.com/questions/766626/…
Matt Hamilton,


3
Non dimenticare di aggiungere il DateTimeKind originale alla data appena creata ex: new DateTime (ticks * span.Ticks, date.Kind);
AM

Risposte:


130

Pavimento

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Round (in alto sul punto medio)

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

return new DateTime( ticks * span.Ticks );

Soffitto

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

return new DateTime( ticks * span.Ticks );

5
Di recente ho riscontrato un problema in cui DateTimeKind non è stato conservato. La seguente modifica all'ultima riga di ciascun metodo ha aiutato nel mio caso:return new DateTime(ticks * span.Ticks, date.Kind);
Peet

39

Questo ti permetterà di arrotondare a qualsiasi intervallo dato. È anche leggermente più veloce della divisione e quindi della moltiplicazione delle zecche.

public static class DateTimeExtensions
{
  public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
  {
    return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
  }

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

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

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

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

11

Dovresti anche essere chiaro se vuoi che l'arrotondamento:

  1. essere all'inizio, alla fine o al centro dell'intervallo
    • l'inizio è il più semplice e spesso quello previsto, ma dovresti essere chiaro nelle specifiche iniziali.
  2. Come vuoi che i casi limite vengano arrotondati.
    • normalmente solo un problema se stai arrotondando al centro piuttosto che alla fine.
    • Poiché l'arrotondamento al centro è un tentativo di una risposta priva di pregiudizi, è necessario utilizzare qualcosa come Bankers Rounding tecnicamente intorno alla metà anche per essere veramente libero da pregiudizi.

È abbastanza probabile che ti interessi solo del primo punto, ma in queste domande 'semplici' il comportamento risultante può avere conseguenze di vasta portata quando lo usi nel mondo reale (spesso a intervalli adiacenti allo zero)

La soluzione di tvanfosson copre tutti i casi elencati in 1. L'esempio del punto medio è orientato verso l'alto. È dubbio che questo sarebbe un problema nell'arrotondamento relativo al tempo.


3

Usa semplicemente le zecche, usandole per dividere, pavimento / soffitto / arrotondare il valore e moltiplicarlo.


-2

Se vuoi solo arrotondare il valore dell'ora al soffitto

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

OP ha richiesto un DateTime come oggetto restituito.
aj.toulan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.