Есть ли лучший способ в С# округлить DateTime до ближайших 5 секунд?

Я хочу округлить DateTime до ближайших 5 секунд. Это то, как я это делаю сейчас, но мне было интересно, был ли лучший или более сжатый способ?

DateTime now = DateTime.Now;
int second = 0;

// round to nearest 5 second mark
if (now.Second % 5 > 2.5)
{
    // round up
    second = now.Second + (5 - (now.Second % 5));
}
else
{
    // round down
    second = now.Second - (now.Second % 5);
}

DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second);

Обратите внимание, что я нашел эти два предыдущих вопроса, однако они truncate, а не round время.

Ответ 1

Счетчик Ticks для DateTime представляет 100-наносекундные интервалы, поэтому вы можете округлить до ближайших 5 секунд, округляя до ближайшего интервала 50000000 тиков следующим образом:

  DateTime now = DateTime.Now;
  DateTime rounded = new DateTime(((now.Ticks + 25000000) / 50000000) * 50000000);

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

Ответ 2

(Извините за воскрешение, я узнаю старый и ответный вопрос - просто добавив дополнительный код для Google ради.)

Я начал с ответа JayMcClellan, но потом я хотел, чтобы он был более общим, округляя до произвольных интервалов (не только 5 секунд). Поэтому я оставил метод Jay для метода, который использует Math.Round для тиков и помещает его в метод расширения, который может принимать произвольные интервалы, а также предлагает возможность изменения логики округления (округление банкира по сравнению с отсутствием нуля). Я размещаю здесь, если это полезно и для кого-то другого:

    public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) {
        return new TimeSpan(
            Convert.ToInt64(Math.Round(
                time.Ticks / (decimal)roundingInterval.Ticks,
                roundingType
            )) * roundingInterval.Ticks
        );
    }

    public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) {
        return Round(time, roundingInterval, MidpointRounding.ToEven);
    }

    public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) {
        return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
    }

Он не выиграет никаких наград за эффективную эффективность, но мне легко читать и интуитивно понятно. Пример использования:

new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs

Ответ 3

Как вы уже упоминали, его довольно легко обрезать. Итак, просто добавьте 2,5 секунды, затем обрезайте вниз.

Ответ 4

Я не могу придумать лучшего способа, хотя я бы, вероятно, отказался от круглого метода:

static int Round(int n, int r)
{
    if ((n % r) <= r / 2)
    {
        return n - (n % r); 
    }
    return n + (r - (n % r));
}

Кроме того,% возвращает int, поэтому сравнение его с 2.5 кажется мне немного странным, хотя оно и верно. Я бы использовал >= 3.

Ответ 5

Я не мог распознать разницу между С# и полосой мыла (ну, я не мог, когда я изначально написал этот ответ, с тех пор все изменилось совсем немного), но если вы ищете более сжатое решение, я бы просто поставил все в функции - там мало что будет более кратким в вашем коде, чем простой вызов указанной функции:

DateTime rounded = roundTo5Secs (DateTime.Now);

Затем вы можете поместить все, что хотите в функцию, и просто документировать, как это работает, например (при условии, что это все целые операции):

secBase = now.Second / 5;
secExtra = now.Second % 5;
if (secExtra > 2) {
    return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
        secBase + 5);
}
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
    secBase);

Вам также могут потребоваться дополнительные проверки, если secBase переходит на 60 (если только объекты С# DateTime достаточно умны, чтобы увеличить минуту (и час, если минута идет до 60 и т.д.).

Ответ 6

Как насчет этого (смешивая несколько ответов вместе)? Я думаю, что он хорошо передает смысл и должен обрабатывать края (округление до следующей минуты) элегантно из-за AddSeconds.

// truncate to multiple of 5
int second = 5 * (int) (now.Second / 5);
DateTime dt = new DateTime(..., second);

// round-up if necessary
if (now.Second % 5 > 2.5)
{
    dt = dt.AddSeconds(5);
}

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

Ответ 7

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

2, 4, 6, 8, 10 - не проблема

Если вы "распределяете" времена в интервалах и если джиттер низкий, усечение очень много более сносно.

Если вы можете пройти миллисекунды и округлить отметку 500 мс, вы сможете нечетно секунд, а также уменьшите эффект джиттера или полностью устраните его.