Округлення об'єктів DateTime


105

Я хочу округлити дати / часи до найближчого інтервалу для програми графіків. Мені хотілося б, щоб підпис методу розширення виглядав так, щоб округлення було досягнуто для будь-якого рівня точності:

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

Ура!


1
Деякі з реалізацій тут також можуть допомогти: stackoverflow.com/questions/766626/…
Метт Гамільтон


3
Не забудьте додати оригінальний DateTimeKind до новоствореної дати, наприклад: новий DateTime (галочки * span.Ticks, date.Kind);
14:00 13

Відповіді:


130

Підлога

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

5
Нещодавно я зіткнувся з проблемою, де DateTimeKind не збереглося. Наступний підхід до останнього рядка в кожному методі допоміг у моєму випадку:return new DateTime(ticks * span.Ticks, date.Kind);
Піт

39

Це дозволить вам обійти будь-який заданий інтервал. Це також трохи швидше, ніж ділення та розмноження кліщів.

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

Вам також повинно бути чітко, якщо ви хочете, щоб ваше округлення:

  1. бути до початку, кінця або середини інтервалу
    • старт - найпростіший і часто очікуваний, але ви повинні бути чіткими у своїх первинних специфікаціях.
  2. Як ви хочете, щоб граничні випадки округлилися
    • як правило, лише питання, якщо ви округляєте до середини, а не до кінця.
    • Оскільки округлення до середини - це спроба вільної відповіді на упередження, вам потрібно використовувати щось на кшталт « Банкерного округлення» з технічної округлої половини, навіть щоб бути справді вільним від упереджень.

Цілком ймовірно, що вас дійсно хвилює лише перший пункт, але в цих "простих" питаннях поведінка, що виникає в результаті, може мати далекосяжні наслідки, коли ви використовуєте її в реальному світі (часто з інтервалами, суміжними до нуля).

Розв’язання рішення tvanfosson охоплює всі випадки, перелічені в 1. Приклад середньої точки зміщений вгору. Сумнівно, що це буде проблемою у часовому округленні.


3

Просто використовуйте галочки, використовуючи це для поділу, підлоги / стелі / округлення значення і помножте його назад.


-2

Якщо ви просто хочете округлити значення Години до стельового

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

ОП запитував DateTime як об'єкт, що повертається.
aj.toulan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.