Округление объектов 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));
...

Ура!

Ответ 1

Этаж

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

Ответ 2

Это позволит вам округлить до любого заданного интервала. Это также немного быстрее, чем деление, а затем умножение тиков.

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

Ответ 3

Вы также должны быть ясны, если хотите, чтобы ваше округление:

  • быть в начале, в конце или в середине интервала
    • start - самый простой и часто ожидаемый, но вы должны быть понятны в своей первоначальной спецификации.
  • Как вы хотите, чтобы граничные случаи округлялись.
    • обычно только проблема, если вы округляете до середины, а не конца.
    • Поскольку округление до середины является попыткой бесплатного ответа на смещение, вам нужно использовать что-то вроде "Банкиры округления" технически круглой половины, даже чтобы быть по-настоящему без предвзятости.

Весьма вероятно, что вы действительно заботитесь только о первом моменте, но в этих "простых" вопросах возникающее поведение может иметь далеко идущие последствия, поскольку вы используете его в реальном мире (часто на промежутках, примыкающих к нулю)

Решение tvanfosson охватывает все случаи, перечисленные в 1. Пример средней точки смещен вверх. Сомнительно, что это было бы проблемой во время округления.

Ответ 4

Просто используйте Ticks, используя это, чтобы разделить, поместить/увеличить/округлить значение и умножить его обратно.

Ответ 5

Если вы просто хотите округлить значение "Час до потолка"

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