如何从 .NET DateTime 中截断毫秒

Posted

技术标签:

【中文标题】如何从 .NET DateTime 中截断毫秒【英文标题】:How to truncate milliseconds off of a .NET DateTime 【发布时间】:2010-11-03 12:46:08 【问题描述】:

我正在尝试将来自传入请求的时间戳与数据库存储值进行比较。 SQL Server 当然会在时间上保持一定的毫秒精度,当读入 .NET DateTime 时,它​​包括那些毫秒。然而,对系统的传入请求并没有提供这种精度,所以我需要简单地减少毫秒数。

我觉得我遗漏了一些明显的东西,但我还没有找到一种优雅的方式来做到这一点 (C#)。

【问题讨论】:

(第三次尝试...) 由于 20% 的答案(1、2、3)描述了如何从格式化的 @ 987654324@ 表示 DateTime,也许需要进行编辑以明确“截断”/“删除”毫秒意味着“产生一个 DateTime 值,其中所有日期/时间组件都相同,除了TimeOfDay.TotalMilliseconds0。”当然,人们不阅读,只是为了消除歧义。 【参考方案1】:

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

DateTime dateTime = ... anything ...
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind
    );

或同等或更短的:

dateTime = dateTime.AddTicks( - (dateTime.Ticks % TimeSpan.TicksPerSecond));

这可以概括为扩展方法:

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));

具体用法如下:

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
...

【讨论】:

虽然我给你这个是因为你在技术上是正确的,但是对于人们从 SQL Server 读取数据来比较一些分布式数据(在我的例子中是基于 Web 的请求),这个数量分辨率不是必需的。 不错。显然有人需要为 DateTime 类提供一些扩展方法来四舍五入到最接近的值,这样这种好的编码才能被重用。 这不太可能,但是当 ticks = 0 时这种方法不会中断吗? @adotout,如果 timeSpan 参数为零,上面的 Truncate 方法将抛出 DivideByZeroException,这就是您所说的“当 ticks = 0 时方法中断”的意思吗?当 timeSpan 为零时抛出 ArgumentException 会更好。 你真的应该忽略 DateTime.MinValue 和 DataTime.MaxValue 吗?如果有人明确使用DateTime.MaxValue.Truncate(TimeSpan.FromSeconds(1)),我希望它会按照锡上所说的那样做。【参考方案2】:
var date = DateTime.Now;

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);

【讨论】:

简洁明了,只记得在构造函数的末尾加上一个“,date.Kind”,确保不会丢失重要的信息。 在性能敏感的代码中谨慎使用此解决方案。我的应用在System.DateTime.GetDatePart 中花费了 12% 的 CPU 时间。 这很简单,但比标记为最佳答案的问题要慢。并不是说这可能是一个瓶颈,但它大约慢了 7-8 倍。 “慢得多”的说法并不完全正确,差异在 50% 和大约 100% 之间,具体取决于运行时; net 4.7.2: 0.35µs vs 0.62 µs 和 core 3.1: 0.18 µs vs 0.12 µs 是微秒(10^-6 秒)【参考方案3】:

这是基于先前答案的扩展方法,可让您截断为任何分辨率...

用法:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond);
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute)

类:

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);
    

【讨论】:

这是一个非常灵活且可重复使用的解决方案,简洁而富有表现力,不会过于冗长。我的投票是最佳解决方案。 您实际上并不需要 % 操作数周围的括号。 .. 但在我看来,括号增加了清晰度。【参考方案4】:
DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);

【讨论】:

-1:仅当 DateTime 值不包括毫秒的小数部分时才有效。 使用此方法导致我的一些单元测试失败:预期:2010-05-05 15:55:49.000 但是:2010-05-05 15:55:49.000。我猜是因为乔提到了几分之一毫秒。 不适用于序列化,例如2010-12-08T11:20:03.000099+15:00 是输出,并没有完全切断毫秒。 Millisecond property 给出了一个介于 0 和 999(含)之间的 整数。因此,如果操作前一天中的时间是23:48:49.1234567,那么该整数将为123,而操作后一天中的时间是23:48:49.0004567。所以它没有被截断为整数秒。【参考方案5】:

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

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");
    

【讨论】:

【参考方案6】:

与其减少毫秒然后比较,为什么不比较差异?

DateTime x; DateTime y;
bool areEqual = (x-y).TotalSeconds == 0;

TimeSpan precision = TimeSpan.FromSeconds(1);
bool areEqual = (x-y).Duration() < precision;

【讨论】:

第一个选项不起作用,因为 TotalSeconds 是双精度;它还返回毫秒数。 比较差异与截断然后比较的结果不同。例如。 5.900 和 6.100 相差不到一秒,因此可以与您的方法进行比较。但是截断的值 5 和 6 是不同的。哪个合适取决于您的要求。【参考方案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);

【讨论】:

请注意,第二个选项 d.AddMilliseconds(-d.Millisecond) 不一定会将 DateTime 准确地移到前一个整秒。 d.Ticks % TimeSpan.TicksPerMillisecond 滴答声(介于 0 到 9,999 之间)将保留在您的第二个之后。【参考方案8】:

向下舍入到第二个:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond)

替换为 TicksPerMinute 以向下舍入到分钟。


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

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second)

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

【讨论】:

这与 7 年前投票率最高并发布的答案中的解决方案不一样吗?【参考方案9】:

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

DateTime d = DateTime.Now;
d = d.Date.AddHours(d.Hour).AddMinutes(d.Minute).AddSeconds(d.Second)

【讨论】:

【参考方案10】:

一种便于阅读的方法是……

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

还有更多...

//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...

我知道它很容易理解,但它缺乏性能。

【讨论】:

转换为字符串并进行解析在性能方面是一个糟糕的想法。 @JeffPutz 是的,但它但很简单。适用于自动化测试,其中从数据库中插入和提取的值会丢失刻度(我的确切情况)。然而,这个答案可能比它更简单,因为var now = DateTime.Parse(DateTime.Now.ToString()) 工作得很好。 @GrimmTheOpiner - 大多数情况下,“......工作得很好”,但不能保证。它的作用是:“将 DateTime 舍入到当前用户的控制面板首选项中配置的任何精度‘长时间’”。这通常是但不一定是秒。 就像它的简单性一样,性能对于自动化测试情况来说不是问题。【参考方案11】:

关于 Diadistis 反应。这对我有用,除了我必须在乘法之前使用 Floor 来删除除法的小数部分。所以,

d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

变成

d = new DateTime(Math.Floor(d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

我原以为两个 Long 值相除会产生一个 Long,因此会删除小数部分,但它会将其解析为 Double,在相乘后留下完全相同的值。

苦恼

【讨论】:

【参考方案12】:

上述解决方案的2种扩展方法

    public static bool LiesAfterIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate > compareDate;
    


    public static bool LiesAfterOrEqualsIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate >= compareDate;
    

用法:

bool liesAfter = myObject.DateProperty.LiesAfterOrEqualsIgnoringMilliseconds(startDateTime, DateTimeKind.Utc);

【讨论】:

【参考方案13】:

这是我在此处和类似问题中发布的扩展方法的版本。这以易于阅读的方式验证刻度值并保留原始 DateTime 实例的 DateTimeKind。 (当存储到像 MongoDB 这样的数据库时,这会产生微妙但相关的副作用。)

如果真正的目标是将 DateTime 截断为指定值(即 Hours/Minutes/Seconds/MS),我建议在您的代码中实现此扩展方法。它确保您只能截断到有效的精度,并保留原始实例的重要 DateTimeKind 元数据:

public static DateTime Truncate(this DateTime dateTime, long ticks)

    bool isValid = ticks == TimeSpan.TicksPerDay 
        || ticks == TimeSpan.TicksPerHour 
        || ticks == TimeSpan.TicksPerMinute 
        || ticks == TimeSpan.TicksPerSecond 
        || ticks == TimeSpan.TicksPerMillisecond;

    // https://***.com/questions/21704604/have-datetime-now-return-to-the-nearest-second
    return isValid 
        ? DateTime.SpecifyKind(
            new DateTime(
                dateTime.Ticks - (dateTime.Ticks % ticks)
            ),
            dateTime.Kind
        )
        : throw new ArgumentException("Invalid ticks value given. Only TimeSpan tick values are allowed.");

那么你可以使用这样的方法:

DateTime dateTime = DateTime.UtcNow.Truncate(TimeSpan.TicksPerMillisecond);

dateTime.Kind => DateTimeKind.Utc

【讨论】:

【参考方案14】:
DateID.Text = DateTime.Today.ToShortDateString();

Use ToShortDateString() //Date 2-02-2016
Use ToShortDateString() // Time 

并通过使用

ToLongDateString() // its show 19 February 2016.

:P

【讨论】:

-1。我可以看到这个问题可能被误解为要求生成string 而不是DateTime,但这完全 省略了输出中的时间分量。 (这也使得访问 Today 属性变得不必要了。)【参考方案15】:

新方法

String Date = DateTime.Today.ToString("dd-MMM-yyyy"); 

//定义字符串传参dd-mmm-yyyy return 24-feb-2016

或显示在文本框上

txtDate.Text = DateTime.Today.ToString("dd-MMM-yyyy");

// 加载PageonLoad

【讨论】:

-1。我可以看到这个问题可能被误解为要求生成string 而不是DateTime,但这完全 省略了输出中的时间分量。 (这也使得访问 Today 属性变得不必要了。)【参考方案16】:

在我的例子中,我的目标是从 datetimePicker 工具中保存 TimeSpan 而不保存秒和毫秒,这就是解决方案。

首先将 datetimePicker.value 转换为您想要的格式,我的是“HH:mm”,然后将其转换回 TimeSpan。

var datetime = datetimepicker1.Value.ToString("HH:mm");
TimeSpan timeSpan = Convert.ToDateTime(datetime).TimeOfDay;

【讨论】:

一个更好的方法(更清晰的意图,避免格式化和从string 解析)是DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); 或者您可以使用Joe's extension method 的变体,它在@987654325 上运行@ 值并使用 TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1)); 截断秒数。【参考方案17】:

我知道答案已经很晚了,但摆脱毫秒的最佳方法是

var currentDateTime = DateTime.Now.ToString("s");

尝试打印变量的值,它将显示日期时间,没有毫秒。

【讨论】:

这并不理想。你有一个字符串,而不是一个 DateTime。

以上是关于如何从 .NET DateTime 中截断毫秒的主要内容,如果未能解决你的问题,请参考以下文章

在net中怎么把datetime类型转换成毫秒数

在datagridview中过滤数据时截断日期

java如何获取数据库中datetime类型数据

如何从python中的datetime.now获取最小,秒和毫秒

.NET Compact Framework 上的 DateTime.Now 中的毫秒数始终为零?

mongodb,保存后截断日期时间的毫秒数