如何从.NET DateTime截断毫秒数


334

我正在尝试将传入请求中的时间戳与数据库存储值进行比较。SQL Server当然会在时间上保持毫秒级的精度,当读入.NET DateTime时,它会包含这些毫秒级。但是,对系统的传入请求没有提供这种精度,因此我只需简单地减少毫秒。

我觉得我缺少明显的东西,但是我还没有找到一种优雅的方法(C#)。


(第3试...)因为问题的答案20%(123)说明如何省略或从格式化删除毫秒组件string的表示DateTime,也许需要编辑,使其明确指出,要“截断” /“滴”毫秒装置“产生一个DateTime值,其中所有日期/时间组件是除了同一TimeOfDay.TotalMilliseconds0”。人们当然不会读书,只是为了消除任何歧义。
培根

Answers:


556

以下内容适用于具有小数毫秒的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的请求)进行比较的人们来说,这种解决方案的数量不是必需的。
Jeff Putz

1
真好 显然,有人需要给DateTime类一些扩展方法,以使其四舍五入到最接近的值,以便可以重新使用这种类型的良好编码。
chris.w.mclean

这是不太可能的,但是当ticks = 0时这种方法不会中断吗?
2013年

@adotout,如果timeSpan参数为零,则上面的Truncate方法将引发DivideByZeroException,这是您的意思是“当滴答= 0时方法中断”吗?当timeSpan为零时,最好抛出ArgumentException。
2013年

145
var date = DateTime.Now;

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

34
简单明了,只需记住在构造函数的末尾添加“,date.Kind”,以确保您不会丢失重要的信息。
JMcDaniel 2011年

9
对性能敏感代码中的此解决方案要谨慎。我的应用在System.DateTime.GetDatePart中花费了12%的CPU时间。
上校恐慌

3
这很简单,但是比标记为最佳答案的问题要慢。并不是说这可能是一个瓶颈,但是它慢了大约7-8倍。
乔纳斯(Jonas)

“慢得多”的语句并不正确,其差异在50%到100%之间,具体取决于运行时间。净4.7.2:0.35µs和0.62µs核心3.1:
0.18µs和0.12µs

61

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

用法:

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

1
这是一个真正灵活且可重复使用的解决方案,简洁明了,且不会过于冗长。我的投票是最好的解决方案。
Jaans

2
您实际上不需要%操作数周围的括号。
2016年

8
..但我认为这些括号增加了清晰度。
猎户座elenzil

28
DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);

70
-1:仅在DateTime值不包含毫秒的小数时才有效。

7
使用此方法导致我的某些单元测试失败:预期:2010-05-05 15:55:49.000但是是:2010-05-05 15:55:49.000。我猜是由于乔提到的关于毫秒的分数。
塞斯·里诺

6
不适用于序列化,例如2010-12-08T11:20:03.000099 + 15:00是输出,不能完全截断毫秒数。
joedotnot 2010年

5
Millisecond属性给出一个介于0和999(含)之间的整数。因此,如果说在一天之前的时间是,23:48:49.1234567那么该整数将是123,而一天之后的时间是23:48:49.0004567。因此它还没有被截断到整秒。
2013年

11

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

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

9

为什么不比较毫秒而不是比较毫秒呢?

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

要么

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

3
第一个选项不起作用,因为TotalSeconds是双精度型;它还返回毫秒。
Jowen 2010年

1
比较差异不会得到截断然后比较的结果。例如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);

3
请注意,第二个选项d.AddMilliseconds(-d.Millisecond)不一定会将DateTime精确地移到前一秒。d.Ticks % TimeSpan.TicksPerMillisecond超过秒数的滴答声(0到9,999之间的某个滴答声)将保留。
Technetium 2012年

5

向下舍入到第二个:

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时间。


3

一种易于阅读的方法是...

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

4
就性能而言,转换为字符串并进行解析是一个糟糕的主意。
杰夫·普茨

2
@JeffPutz真实的,但它,但简单。适用于自动测试,在该测试中,从数据库插入和拉出的值丢失了滴答声(我的确切情况)。但是,这个答案可能比实际要简单得多,因为var now = DateTime.Parse(DateTime.Now.ToString())效果很好。
格林(Grimm The Opiner)

1
@GrimmTheOpiner-大多数情况下,“ ...效果很好”,但不能保证。它的作用是:“将DateTime的值四舍五入为当前用户“控制面板”首选项中所配置的精度“ Long time”。通常是,但不一定是秒。

1
就像它的简单一样,性能对于自动测试情况也不是问题。

1

关于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,而在乘法之后却留下了完全相同的值。

癫痫病


1

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

1

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

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

0
DateID.Text = DateTime.Today.ToShortDateString();

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

并通过使用

ToLongDateString() // its show 19 February 2016.

:P


-1。我可以看到问题可能被误解为要求生成a string而不是a DateTime,但这完全忽略了输出中的时间成分。(这也使得Today不必访问该属性。)
BACON

0

新方法

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。我可以看到问题可能被误解为要求生成a string而不是a DateTime,但这完全忽略了输出中的时间成分。(这也使得Today不必访问该属性。)
BACON

0

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

首先将datetimePicker.value转换为所需的格式,即“ HH:mm”,然后将其转换回TimeSpan。

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

这样做的更好方法(意图更清晰,避免格式化为和从a解析回string),DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); 或者可以使用Joe的扩展方法的一种变体,该变体对TimeSpan值进行操作并用于TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));截断秒数。
培根

0

这是我在此处发布的扩展方法版本,并且存在类似问题。这以一种易于阅读的方式验证了ticks值,并保留了原始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;

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

-1

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

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

尝试打印该变量的值,它将显示日期时间,而不是毫秒。


1
这是不理想的。然后,您有一个字符串,而不是DateTime。
杰夫·普茨
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.