Есть ли лучший способ в С# округлить 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 мс, вы сможете нечетно
секунд, а также уменьшите эффект джиттера или полностью устраните его.