Zaokrąglanie obiektów DateTime


105

Chcę zaokrąglić daty / godziny do najbliższego przedziału dla aplikacji do tworzenia wykresów. Chciałbym mieć podpis metody rozszerzenia, jak poniżej, aby można było uzyskać zaokrąglenie dla dowolnego poziomu dokładności:

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

Chodzi o to, że jeśli przejdę w przedziale czasowym dziesięciu minut, zaokrągli to do najbliższych dziesięciu minut. Nie mogę się skupić na implementacji i mam nadzieję, że któryś z was napisał lub użył czegoś podobnego wcześniej.

Myślę, że podłoga, sufit lub najbliższa realizacja są w porządku.

Jakieś pomysły?

Edycja: Dzięki @tvanfosson i @ShuggyCoUk implementacja wygląda następująco:

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

I tak się nazywa:

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

Twoje zdrowie!


1
Niektóre z implementacji tutaj również mogą pomóc: stackoverflow.com/questions/766626/ ...
Matt Hamilton


3
Nie zapomnij dodać oryginalnego DateTimeKind do nowo utworzonej daty, np .: new DateTime (ticks * span.Ticks, date.Kind);
AM

Odpowiedzi:


130

Piętro

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Zaokrąglij (w górę na środku)

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

return new DateTime( ticks * span.Ticks );

Sufit

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

return new DateTime( ticks * span.Ticks );

5
Niedawno napotkałem problem polegający na tym, że DateTimeKind nie został zachowany. Poniższe poprawki do ostatniej linii w każdej metodzie pomogły w moim przypadku:return new DateTime(ticks * span.Ticks, date.Kind);
Peet

39

Pozwoli ci to zaokrąglić do dowolnego podanego przedziału. Jest to również nieco szybsze niż dzielenie, a następnie mnożenie kleszczy.

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

Należy również jasno określić, czy chcesz, aby zaokrąglenie:

  1. być na początku, na końcu lub w środku interwału
    • start jest najłatwiejszy i często oczekiwany, ale powinieneś być jasny w swojej początkowej specyfikacji.
  2. Jak chcesz, aby przypadki graniczne były zaokrąglane.
    • zwykle jest to problem tylko wtedy, gdy zaokrąglasz do środka, a nie do końca.
    • Ponieważ zaokrąglanie do środka jest próbą uzyskania odpowiedzi wolnej od uprzedzeń, musisz użyć czegoś takiego jak zaokrąglanie bankierów, technicznie zaokrąglone do połowy, nawet, aby być naprawdę wolnym od uprzedzeń.

Jest całkiem prawdopodobne, że naprawdę zależy ci tylko na pierwszym punkcie, ale w tych `` prostych '' pytaniach wynikowe zachowanie może mieć daleko idące konsekwencje, ponieważ używasz go w prawdziwym świecie (często w odstępach czasu bliskich zeru)

Rozwiązanie tvanfosson obejmuje wszystkie przypadki wymienione w 1. Przykład punktu środkowego jest przesunięty w górę. Wątpliwe jest, aby byłby to problem związany z zaokrąglaniem czasu.


3

Po prostu użyj znaczników, używając ich do podzielenia, podłogi / sufitu / zaokrąglenia wartości i pomnóż ją z powrotem.


-2

Jeśli chcesz zaokrąglić godzinę do wartości maksymalnej

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

OP zażądał DateTime jako obiektu zwracającego.
aj.toulan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.