Runden von DateTime-Objekten

105

Ich möchte Datums- / Uhrzeitangaben für eine Diagrammanwendung auf das nächste Intervall runden. Ich möchte eine Signatur der Erweiterungsmethode wie folgt, damit die Rundung für jede Genauigkeitsstufe erreicht werden kann:

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

Die Idee ist, dass wenn ich in einer Zeitspanne von zehn Minuten vergebe, es auf das nächste Intervall von zehn Minuten gerundet wird. Ich kann mich nicht mit der Implementierung auseinandersetzen und hoffe, dass einer von Ihnen zuvor etwas Ähnliches geschrieben oder verwendet hat.

Ich denke, entweder ein Boden, eine Decke oder eine nächste Implementierung ist in Ordnung.

Irgendwelche Ideen?

Bearbeiten: Dank @tvanfosson & @ShuggyCoUk sieht die Implementierung folgendermaßen aus:

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

Und heißt so:

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

Prost!

Granate
quelle
1
Einige der Implementierungen hier könnten auch helfen: stackoverflow.com/questions/766626/…
Matt Hamilton
3
Vergessen Sie nicht, das ursprüngliche DateTimeKind zum neu erstellten Datum hinzuzufügen, z. B.: New DateTime (Häkchen * span.Ticks, date.Kind);
Uhr

Antworten:

130

Fußboden

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Runde (auf Mittelpunkt)

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

return new DateTime( ticks * span.Ticks );

Decke

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

return new DateTime( ticks * span.Ticks );
Tvanfosson
quelle
5
Ich bin kürzlich auf ein Problem gestoßen, bei dem DateTimeKind nicht erhalten blieb. Die folgende Änderung an der letzten Zeile in jeder Methode hat in meinem Fall geholfen:return new DateTime(ticks * span.Ticks, date.Kind);
Peet
39

Auf diese Weise können Sie auf ein bestimmtes Intervall runden. Es ist auch etwas schneller als das Teilen und dann Multiplizieren der Zecken.

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));
  }
}
aj.toulan
quelle
11

Sie sollten auch klar sein, ob Ihre Rundung:

  1. am Anfang, Ende oder in der Mitte des Intervalls sein
    • Der Start ist am einfachsten und oft der erwartete, aber Sie sollten in Ihrer ursprünglichen Spezifikation klar sein.
  2. Wie sollen Grenzfälle gerundet werden?
    • Normalerweise nur dann ein Problem, wenn Sie eher auf die Mitte als auf das Ende runden.
    • Da das Runden auf die Mitte ein Versuch einer vorurteilsfreien Antwort ist, müssen Sie so etwas wie Bankers Rounding verwenden, um die Hälfte technisch zu runden, selbst um wirklich vorurteilsfrei zu sein.

Es ist sehr wahrscheinlich, dass Sie sich wirklich nur um den ersten Punkt kümmern, aber in diesen „einfachen“ Fragen kann das resultierende Verhalten weitreichende Konsequenzen haben, wenn Sie es in der realen Welt verwenden (oft in Intervallen neben Null).

Die Lösung von tvanfosson deckt alle in 1 aufgeführten Fälle ab. Das Mittelpunktbeispiel ist nach oben vorgespannt. Es ist zweifelhaft, dass dies ein Problem bei der zeitbezogenen Rundung wäre.

ShuggyCoUk
quelle
3

Verwenden Sie einfach die Häkchen, um den Wert zu teilen, zu decken, zu decken und zu multiplizieren.

Lucero
quelle
-2

Wenn Sie nur die Stunde auf den Deckenwert aufrunden möchten

Console.WriteLine(DateTime.Now.ToString("M/d/yyyy hh:00:00"));
Priyanka
quelle
OP hat eine DateTime als zurückkehrendes Objekt angefordert.
Aj.toulan