简体   繁体   English

在执行日期时间比较时,如何解决日期时间缺乏毫秒精度的问题?

[英]How can I account for datetime's lack of millisecond precision when performing datetime comparisons?

Background背景

I recently learned that SQL Server's datetime type only stores time to approximately 1/300th of a second .我最近了解到SQL 服务器的 datetime 类型只存储大约 1/300 秒的时间

So given these data:因此,鉴于这些数据:

create table datetime_test (id int, my_date datetime);
insert into datetime_test (id, my_date) values
    (0, '2020-12-11 17:14:07.000'),
    (1, '2020-12-11 17:14:07.197'),
    (2, '2020-12-11 17:14:07.198'),
    (3, '2020-12-11 17:14:08.000');

This query would return (1, 2, 3) , instead of (2, 3) as one might expect:这个查询将返回(1, 2, 3) ,而不是人们可能期望的(2, 3)

select id from datetime_test where my_date >= '2020-12-11 17:14:07.198';

The reason is that the millisecond part of this datetime is actually stored as .197 :原因是这个日期时间的毫秒部分实际上存储为.197

-- Result: 2020-12-11 17:14:07.197
select convert(datetime, '2020-12-11 17:14:07.198');

My Question我的问题

I am working with existing c# code that uses SQL to compare datetimes using >= .我正在使用现有的 c# 代码,该代码使用 SQL 来比较使用>=的日期时间。 Something like this:像这样的东西:

public Foo GetMyColumn(DateTime inputDatetime)
{
  // ...
}
select my_column
from my_table
where my_datetime >= @inputDatetime

I am trying to reuse this c# method to perform an exclusive comparison...the equivalent of using > .我正在尝试重用此 c# 方法来执行排他比较......相当于使用> How can I do that?我怎样才能做到这一点?

My original attempt was to add a single millisecond to the datetime input (in c#), but that will not work due to the precision issue outlined above.我最初的尝试是向日期时间输入(在 c# 中)添加一个毫秒,但由于上述精度问题,这将不起作用。 I suppose I could add 3 milliseconds.我想我可以增加 3 毫秒。 That feels like a hack.这感觉就像一个黑客。 But would it work reliably?但它会可靠地工作吗?

Note: please assume I cannot change this SQL or method.注意:请假设我无法更改此 SQL 或方法。

You will find that the windows clock may not be as accurate as you may need it to be.您会发现 windows 时钟可能不如您需要的准确。 Also, your server's clock is not that accurate, depending on the load it may be lagging, for this reason, we use special hardware with GPS timer when an accurate time is needed.此外,您的服务器的时钟不是那么准确,这取决于它可能滞后的负载,因此,当需要准确的时间时,我们使用带有 GPS 定时器的特殊硬件。

Also, the time on your server and the time on the DB server does not need to match, if your requirements are hard then they will/ may "never match".此外,您的服务器上的时间和数据库服务器上的时间不需要匹配,如果您的要求很难,那么它们将/可能“永远不会匹配”。 Only a broken watch is accurate 2x per day...只有一块破损的手表每天准确 2 次...

Just a small note, always use UTC date-time on the database and in code, this way you have the same time regardless of where the servers are hosted.请注意,始终在数据库和代码中使用 UTC 日期时间,这样无论服务器托管在何处,您都拥有相同的时间。 UTC date-time has a ToLocalTime() in .net and the database has GetUtcDate() in TSQL this way you are not off by time-zones. UTC 日期时间在 .net 中有一个 ToLocalTime() ,并且数据库在 TSQL 中有一个 GetUtcDate() 这样你就不会被时区限制。

using System;
using System.Runtime.InteropServices;
public static class HighResolutionDateTime 
{ 
    public static bool IsAvailable { get; private set; }
    [DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)] 
    private static extern void GetSystemTimePreciseAsFileTime(out long filetime); 
    public static DateTime UtcNow 
    {
        get { 
            if (!IsAvailable) 
            { 
                throw new InvalidOperationException("High resolution clock isn't available."); 
            } 
            long filetime; 
            GetSystemTimePreciseAsFileTime(out filetime); 
            return DateTime.FromFileTimeUtc(filetime); 
        } 
    } 
    static HighResolutionDateTime() 
    { 
        try 
        { 
            long filetime; 
            GetSystemTimePreciseAsFileTime(out filetime); 
            IsAvailable = true; 
        } 
        catch (EntryPointNotFoundException) 
        {             // Not running Windows 8 or higher.             
            IsAvailable = false;         
        }     
    } 
}

#edit as per comment as to comparing the date-time, use datetime2(7) as datatype in your data column, this will be as accurate as .net datetime #edit 根据评论比较日期时间,使用 datetime2(7) 作为数据列中的数据类型,这将与 .net datetime 一样准确

According to the documentation , datetime is "rounded to increments of.000, .003, or.007 seconds."根据文档datetime时间“四舍五入到 0.000、0.003 或 007 秒的增量”。 So the c# workaround is to round up the DateTime input to the next number, following that pattern.因此,c# 解决方法是按照该模式将DateTime输入四舍五入到下一个数字。

Something like this:像这样的东西:

private DateTime RoundUp(DateTime datetime)
{
    int lastMillisecondDigit;
    do
    {
        datetime = datetime.AddMilliseconds(1);
        lastMillisecondDigit = datetime.Millisecond % 10;
    } while (lastMillisecondDigit != 0 && lastMillisecondDigit != 3 && lastMillisecondDigit != 7);

    return datetime;
}

Note: I understand this is a big hack.注意:我知道这是一个大黑客。 There are better solutions in the comments, such as changing the datatype to datetime2 .评论中有更好的解决方案,例如将数据类型更改为datetime2 But if you are unable to change the database or code, I think this workaround will do the trick.但是,如果您无法更改数据库或代码,我认为这种解决方法可以解决问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM