2009-09-08 15 views
94

Chcę zaokrąglić daty/czas do najbliższego przedziału dla aplikacji do tworzenia wykresów. Chciałbym podpisu metodę rozszerzenia jak następuje tak, że zaokrąglanie można osiągnąć na każdym poziomie dokładności:Zaokrąglanie DateTime objects

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

Chodzi o to, że jeśli przejdzie w przedziale czasu od dziesięciu minut, to zaokrąglić do najbliższej Dziesięć minut interwału. Nie mogę zrozumieć mojej implementacji i mam nadzieję, że któryś z was napisał wcześniej lub użył czegoś podobnego.

Myślę, że albo podłoga, sufit, albo najbliższe wdrożenie jest w porządku.

Wszelkie pomysły?

Edit: Dzięki @tvanfosson & @ShuggyCoUk, realizacja 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 nazywa się tak:

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

Cheers!

+1

Niektóre z tutejszych implementacjach może pomóc też: http://stackoverflow.com/questions/766626/is-there- a-better-way-in-c-to-round-a-datetime-to-the-nearest-5-seconds –

+1

możliwy duplikat [Czy istnieje lepszy sposób przycinania DateTime do określonej dokładności?] (http: //stackoverflow.com/questions/152774/is-there-a-better-way-to-trim-a-datetime-to-a-specific-precision) –

+1

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

Odpowiedz

112

Floor

long ticks = date.Ticks/span.Ticks; 

return new DateTime(ticks * span.Ticks); 

okrągły (w górę na środkowym)

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

return new DateTime(ticks * span.Ticks); 

sufitowa

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

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

Wystarczy użyć kleszczy, aby podzielić, podłoga/sufit/zaokrąglić wartość i pomnożyć ją.

10

Należy również jasne, czy chcesz, aby zaokrąglenie do:

  1. do początku, końca lub środka przedziału
    • Początek jest najłatwiejszy i często oczekiwany, ale powinieneś być czysty w początkowej specyfikacji.
  2. Jak chcesz spraw granicznych do rundy.
    • zwykle jest to problem tylko w przypadku zaokrąglania do środka, a nie do końca.
    • Ponieważ zaokrąglanie do środka jest próbą uzyskania wolnej od uprzedzeń odpowiedzi, trzeba użyć czegoś w stylu półobrotu, aby być naprawdę wolną od uprzedzeń.

Jest całkiem prawdopodobne, że troszczą się tylko o pierwszym punkcie, ale w tych „prostych” pytań otrzymany zachowanie może mieć daleko idące konsekwencje, jak użyć go w świecie rzeczywistym (często w przerwach sąsiadujące z zerą)

Pokrywa rozwiązania tvanfosson we wszystkich przypadkach wymienionych w 1. Przykład punktu środkowego jest przesunięty w górę. Jest wątpliwe, czy byłby to problem związany z zaokrąglaniem w czasie.

28

To pozwoli Ci zaokrąglić do dowolnej podanej przerwy.Jest również nieco szybszy niż dzielenie, a następnie pomnożenie tyknięć.

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

Jeśli chcesz po prostu zaokrąglić w górę do Hour wartość górna

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

OP zażądał DateTime jako zwracanego obiektu. –