有没有更好的方法在C#中将DateTime四舍五入到最接近的5秒?

时间:2009-04-20 02:16:22

标签: c# algorithm datetime performance

我想将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);

请注意我之前发现了these two个问题,但他们截断而不是回合时间。

7 个答案:

答案 0 :(得分:46)

(抱歉复活;我知道这是一个陈旧且回答的问题 - 只是为了谷歌而添加了一些额外的代码。)

我从JayMcClellan's answer开始,但后来我希望它更通用,四舍五入到任意间隔(不仅仅是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

答案 1 :(得分:29)

DateTime的Ticks计数表示100纳秒的间隔,因此您可以通过舍入到最近的50000000-tick间隔来舍入到最接近的5秒,如下所示:

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

这更简洁,但不一定更好。这取决于您是否更喜欢简洁和速度而不是代码清晰度。你的理解可能更容易理解。

答案 2 :(得分:2)

就像你提到的那样,截断它很容易。所以,只需添加2.5秒,然后截断。

答案 3 :(得分:1)

我想不出更好的方法,虽然我可能会考虑圆方法:

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

此外,%返回一个int,所以将它与2.5比较让我觉得有点奇怪,即使它是正确的。我会使用&gt; = 3.

答案 4 :(得分:1)

我无法认识到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则为小时,依此类推)。

答案 5 :(得分:1)

这个怎么样(将几个答案混合在一起)?我认为它很好地传达了意义,并且应该优雅地处理边缘情况(四舍五入到下一分钟),因为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);
}

Jay所示的Ticks方法更简洁,但可读性稍差。如果您使用该方法,至少参考TimeSpan.TicksPerSecond

答案 6 :(得分:0)

从技术上讲,你只能在几秒钟内完全舍入到奇数间隔。

2,4,6,8,10&lt; - 没问题

如果您按时间间隔“分配”时间,并且抖动较低,则截断很多 更容易处理。

如果你可以在500毫秒标记处传递毫秒和圆形,你将能够奇数 秒,并且还会减少抖动的影响或完全消除它。