Есть ли лучший способ в C # округлить DateTime с точностью до 5 секунд? - PullRequest
18 голосов
/ 20 апреля 2009

Я хочу округлить 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);

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

Ответы [ 7 ]

46 голосов
/ 05 ноября 2010

(извините за воскресение; я понимаю, что это старый и ответ на вопрос - просто добавив дополнительный код для Google.)

Я начал с ответа JayMcClellan , но затем я хотел, чтобы он был более общим, округляя до произвольных интервалов (не только 5 секунд). В итоге я оставил метод Джея для метода, который использует 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
29 голосов
/ 20 апреля 2009

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

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

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

2 голосов
/ 20 апреля 2009

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

1 голос
/ 20 апреля 2009

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

1 голос
/ 20 апреля 2009

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

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 (если только объекты C # DateTime не достаточно умны, чтобы увеличивать минуты (и час, если минута становится равной 60, и т. Д.).

1 голос
/ 20 апреля 2009

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

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

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

0 голосов
/ 22 января 2013

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...