简体   繁体   中英

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 .

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:

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 :

-- 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 >= . 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 > . 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. I suppose I could add 3 milliseconds. That feels like a hack. But would it work reliably?

Note: please assume I cannot change this SQL or method.

You will find that the windows clock may not be as accurate as you may need it to be. 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.

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

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 date-time has a ToLocalTime() in .net and the database has GetUtcDate() in TSQL this way you are not off by time-zones.

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

According to the documentation , datetime is "rounded to increments of.000, .003, or.007 seconds." So the c# workaround is to round up the DateTime input to the next number, following that pattern.

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 . But if you are unable to change the database or code, I think this workaround will do the trick.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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