Округление объектов DateTime
Я хочу округлить дату / время до ближайшего интервала для приложения для построения диаграмм. Я хотел бы, чтобы подпись метода расширения была следующей, чтобы округление можно было получить для любого уровня точности:
static DateTime Round(this DateTime date, TimeSpan span);
Идея состоит в том, что если я пройду через промежуток времени в десять минут, он округлится до ближайшего десятиминутного интервала. Я не могу разобраться с реализацией и надеюсь, что кто-то из вас написал или использовал что-то подобное раньше.
Я думаю, что пол, потолок или ближайшая реализация в порядке.
Есть идеи?
Изменить: Благодаря @tvanfosson & @ShuggyCoUk, реализация выглядит следующим образом:
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);
}
}
И называется так:
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));
...
Ура!
5 ответов
Этаж
long ticks = date.Ticks / span.Ticks;
return new DateTime( ticks * span.Ticks );
Круглый (вверх по средней точке)
long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;
return new DateTime( ticks * span.Ticks );
потолок
long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;
return new DateTime( ticks * span.Ticks );
Это позволит вам округлить до любого заданного интервала. Это также немного быстрее, чем деление, а затем умножение тиков.
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));
}
Вам также должно быть ясно, хотите ли вы, чтобы ваше округление:
- быть в начале, конце или середине интервала
- начало является самым простым и часто ожидаемым, но вы должны быть ясны в своей первоначальной спецификации.
- Как вы хотите, чтобы граничные случаи округлялись.
- обычно проблема только в том случае, если вы округлились до середины, а не до конца.
- Поскольку округление до середины - это попытка получить свободный от смещения ответ, вам нужно использовать что-то вроде банковского округления с технической точки зрения до половины, даже чтобы быть по-настоящему свободным от смещения.
Вполне вероятно, что вы действительно заботитесь только о первом пункте, но в этих "простых" вопросах результирующее поведение может иметь далеко идущие последствия, поскольку вы используете его в реальном мире (часто с интервалами, близкими к нулю)
Решение tvanfosson охватывает все случаи, перечисленные в 1. Пример средней точки смещен вверх. Сомнительно, что это было бы проблемой во временном округлении.
Просто используйте галочки, используя это, чтобы разделить, основать / потолок / округлить значение и умножить его обратно.
Если вы просто хотите округлить значение часа до потолка
Console.WriteLine(DateTime.Now.ToString("M/d/yyyy hh:00:00"));