如何截断.NET DateTime的毫秒数

Jef*_*utz 319 .net c# datetime

我正在尝试将传入请求的时间戳与数据库存储值进行比较.SQL Server当然会保留一些毫秒的精度,当读入.NET DateTime时,它包括那些毫秒.但是,对系统的传入请求不提供该精度,因此我需要简单地删除毫秒.

我觉得我错过了一些明显的东西,但我还没有找到一种优雅的方法(C#).

Joe*_*Joe 534

以下内容适用于具有小数毫秒的DateTime,并且还保留Kind属性(Local,Utc或Undefined).

DateTime dateTime = ... anything ...
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind
    );
Run Code Online (Sandbox Code Playgroud)

或等同和更短的:

dateTime = dateTime.AddTicks( - (dateTime.Ticks % TimeSpan.TicksPerSecond));
Run Code Online (Sandbox Code Playgroud)

这可以推广到扩展方法:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan)
{
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException
    if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) return dateTime; // do not modify "guard" values
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks));
}
Run Code Online (Sandbox Code Playgroud)

使用如下:

dateTime = dateTime.Truncate(TimeSpan.FromMilliseconds(1)); // Truncate to whole ms
dateTime = dateTime.Truncate(TimeSpan.FromSeconds(1)); // Truncate to whole second
dateTime = dateTime.Truncate(TimeSpan.FromMinutes(1)); // Truncate to whole minute
...
Run Code Online (Sandbox Code Playgroud)

  • 您真的应该忽略 DateTime.MinValue 和 DataTime.MaxValue 吗?如果有人明确使用“DateTime.MaxValue.Truncate(TimeSpan.FromSeconds(1))”,我希望它能按照罐头上的说明进行操作。 (3认同)

ben*_*rce 139

var date = DateTime.Now;

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);
Run Code Online (Sandbox Code Playgroud)

  • 清楚简单,只需记住在构造函数的末尾添加",date.Kind",以确保不会丢失重要的信息. (32认同)
  • 在性能敏感代码中谨慎使用此解决方案.我的应用程序在[System.DateTime.GetDatePart]中花费了12%的CPU时间(https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,ff06f271f088f1a8). (9认同)
  • 这很简单,但比标记为最佳答案的问题要慢.并不是说这可能是一个瓶颈,但它大约慢了7-8倍. (3认同)
  • “慢得多”的说法并不完全正确,差异在 50% 到 100% 之间,具体取决于运行时间;[net 4.7.2: 0.35µs vs 0.62 µs](https://dotnetfiddle.net/itw6UD) 和 [core 3.1: 0.18 µs vs 0.12 µs](https://dotnetfiddle.net/Fwn83E) 这是微秒 ( 10^-6秒) (2认同)

Sky*_*ers 57

这是一个基于前一个答案的扩展方法,可以让你截断到任何分辨率......

用法:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond);
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute)
Run Code Online (Sandbox Code Playgroud)

类:

public static class DateTimeUtils
{
    /// <summary>
    /// <para>Truncates a DateTime to a specified resolution.</para>
    /// <para>A convenient source for resolution is TimeSpan.TicksPerXXXX constants.</para>
    /// </summary>
    /// <param name="date">The DateTime object to truncate</param>
    /// <param name="resolution">e.g. to round to nearest second, TimeSpan.TicksPerSecond</param>
    /// <returns>Truncated DateTime</returns>
    public static DateTime Truncate(this DateTime date, long resolution)
    {
        return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • ..但在我看来,parens增加了清晰度. (6认同)
  • 这是一个非常灵活且可重用的解决方案,简洁且富有表现力,但又不会过于冗长。我投票为最佳解决方案。 (2认同)
  • 您实际上不需要 % 操作数周围的括号。 (2认同)

chr*_*ean 27

DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);
Run Code Online (Sandbox Code Playgroud)

  • -1:仅当DateTime值不包含毫秒的分数时才会起作用. (70认同)
  • 使用此方法导致我的一些单元测试失败:预期:2010-05-05 15:55:49.000但是:2010-05-05 15:55:49.000.我猜是因为Joe提到了几分之一毫秒. (7认同)
  • 不适用于序列化,例如2010-12-08T11:20:03.000099 + 15:00是输出,不会完全切断毫秒. (6认同)
  • [`Millisecond`属性](http://msdn.microsoft.com/en-us/library/system.datetime.millisecond.aspx)给出0到999(含)之间的***整数***.因此,如果操作前一天的时间是,例如,"23:48:49.1234567",则该整数将为"123",操作后的一天中的时间为"23:48:49.0004567".所以它没有截断到整数秒. (4认同)

Bob*_*Bob 9

而不是丢弃毫秒然后比较,为什么不比较差异?

DateTime x; DateTime y;
bool areEqual = (x-y).TotalSeconds == 0;
Run Code Online (Sandbox Code Playgroud)

要么

TimeSpan precision = TimeSpan.FromSeconds(1);
bool areEqual = (x-y).Duration() < precision;
Run Code Online (Sandbox Code Playgroud)

  • 第一个选项不起作用,因为TotalSeconds是double; 它还返回毫秒. (3认同)
  • 比较差异与截断然后比较的结果不同。例如,5.900 和 6.100 相隔不到一秒,因此与您的方法比较相等。但是截断的值 5 和 6 是不同的。哪个合适取决于您的要求。 (2认同)

Kin*_*ong 8

有时您希望截断为基于日历的内容,例如年份或月份.这是一种扩展方法,可让您选择任何分辨率.

public enum DateTimeResolution
{
    Year, Month, Day, Hour, Minute, Second, Millisecond, Tick
}

public static DateTime Truncate(this DateTime self, DateTimeResolution resolution = DateTimeResolution.Second)
{
    switch (resolution)
    {
        case DateTimeResolution.Year:
            return new DateTime(self.Year, 1, 1, 0, 0, 0, 0, self.Kind);
        case DateTimeResolution.Month:
            return new DateTime(self.Year, self.Month, 1, 0, 0, 0, self.Kind);
        case DateTimeResolution.Day:
            return new DateTime(self.Year, self.Month, self.Day, 0, 0, 0, self.Kind);
        case DateTimeResolution.Hour:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerHour));
        case DateTimeResolution.Minute:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMinute));
        case DateTimeResolution.Second:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerSecond));
        case DateTimeResolution.Millisecond:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMillisecond));
        case DateTimeResolution.Tick:
            return self.AddTicks(0);
        default:
            throw new ArgumentException("unrecognized resolution", "resolution");
    }
}
Run Code Online (Sandbox Code Playgroud)


Dia*_*tis 7

不太明显,但速度提高了2倍以上:

// 10000000 runs

DateTime d = DateTime.Now;

// 484,375ms
d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

// 1296,875ms
d = d.AddMilliseconds(-d.Millisecond);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,第二个选项`d.AddMilliseconds(-d.Millisecond)`并不一定将DateTime精确地移动到前一个完整秒.`d.Ticks%TimeSpan.TicksPerMillisecond`滴答声(介于0和9,999之间)将超出你的第二个. (3认同)

Col*_*nic 6

向下舍入到第二个:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond)
Run Code Online (Sandbox Code Playgroud)

替换TicksPerMinute为四舍五入到分钟。


如果您的代码对性能敏感,请谨慎使用

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second)
Run Code Online (Sandbox Code Playgroud)

我的应用程序在System.DateTime.GetDatePart 中花费了 12% 的 CPU 时间。

  • 这不是与 7 年前发布的投票最高的答案相同的解决方案吗? (2认同)

Ser*_*ral 5

一种轻松阅读的方法是...

//Remove milliseconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss", null);
Run Code Online (Sandbox Code Playgroud)

和更多...

//Remove seconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm"), "yyyy-MM-dd HH:mm", null);

//Remove minutes
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH"), "yyyy-MM-dd HH", null);

//and go on...
Run Code Online (Sandbox Code Playgroud)

据我了解,它很容易理解,但缺乏性能。

  • 就性能而言,转换为字符串并解析是一个糟糕的主意。 (8认同)
  • @JeffPutz 是的,但它*很*但是很简单。适合自动化测试,其中从数据库插入和提取的值会丢失刻度(我的确切情况)。然而这个答案可能比现在更简单,因为 `var now = DateTime.Parse(DateTime.Now.ToString())` 工作得很好。 (3认同)

All*_*ice 5

不是最快的解决方案,但简单易懂:

DateTime d = DateTime.Now;
d = d.Date.AddHours(d.Hour).AddMinutes(d.Minute).AddSeconds(d.Second)
Run Code Online (Sandbox Code Playgroud)