日期时间比较精度

34

我正在进行DateTime比较,但我不想在秒、毫秒和ticks级别上进行比较。最优雅的方法是什么?

如果我只是简单地比较DateTime,由于ticks差异,它们很少相等。


我认为这里没有银弹... 完全取决于你的喜好... 无论如何,感谢你提出这个问题... 我从这个问题中学到了很多东西。 - The King
3
我只是想指出,根据所选答案,这个问题是错误的。你并不是在询问比较绝对时间到分钟,而是在询问检查经过的时间是否小于某个阈值。 - ErikE
11个回答

38

使用 TimeSpan 怎么样?

if (Math.Truncate((A - B).TotalMinutes) == 0)
{
    //There is less than one minute between them
}

可能不是最优雅的方式,但它允许处理相隔一秒钟但具有不同天/小时/分钟部分(例如越过午夜)的情况。

编辑: 我意识到截断是不必要的...

if (Math.Abs((A - B).TotalMinutes) < 1)
{
    //There is less than one minute between them
}

就我个人而言,我认为这更加优雅...


我认为应该这样写:if ((A - B).TotalMinutes <= 1) - The King
是的,你说得对,我也已经删除了等于号,因为注释中写的是小于。 - James Barrass
为了使您的编辑生效,您需要在检查值是否小于1之前调用Math.Abs,否则您可能会在两个 DateTime 值之间有一个巨大的差异。例如 (DateTime.Now - DateTime.Now.AddMinutes(10)).TotalMinutes 将小于 1,但总时间差为 10 分钟。 - Dan Herbert
1
这个解决方案可以返回两个不同小时的不同分钟的“true”。我不认为这个答案是正确的。例如:DateTime A = new DateTime(2016, 10, 26, 10, 59, 40), B = new DateTime(2016, 10, 26, 11, 00, 20); - Dmytro Shevchenko

10

一种方法是从你要比较的值中创建两个新的DateTime,但忽略从秒开始以下的任何内容,然后进行比较:

DateTime compare1 = new DateTime(year1, month1, day1, hour1, minute1, 0);
DateTime compare2 = new DateTime(year2, month2, day2, hour2, minute2, 0);

int result = DateTime.Compare(compare1, compare2);

我首先承认这不是优雅的解决方案,但它能解决问题。


这个解决方案很好,因为它可以检测是否是同一分钟(而其他一些解决方案可能会在相隔几秒钟的不同分钟返回true,例如11:59:59和12:00:01)。此外,它应该比基于字符串的比较更有效。 - Mike Henry
如果你正在比较日期,有一个快捷方式是使用 new DateTime(...)。只需使用 foo.Date 即可。 - Eliot Gillum

8
public class DateTimeComparer : Comparer<DateTime>
{
    private Prescision _Prescision;

    public enum Prescision : sbyte
    {
        Millisecons,
        Seconds,
        Minutes,
        Hour,
        Day,
        Month,
        Year,
        Ticks
    }

    Func<DateTime, DateTime>[] actions = new Func<DateTime, DateTime>[]
        {
            (x) => { return x.AddMilliseconds(-x.Millisecond);},
            (x) => { return x.AddSeconds(-x.Second);},
            (x) => { return x.AddMinutes(-x.Minute);},
            (x) => { return x.AddHours(-x.Hour);},
            (x) => { return x.AddDays(-x.Day);},
            (x) => { return x.AddMonths(-x.Month);},
        };

    public DateTimeComparer(Prescision prescision = Prescision.Ticks)
    {
        _Prescision = prescision;
    }

    public override int Compare(DateTime x, DateTime y)
    {
        if (_Prescision == Prescision.Ticks)
        {
            return x.CompareTo(y);
        }

        for (sbyte i = (sbyte)(_Prescision - 1); i >= 0; i--)
        {
            x = actions[i](x);
            y = actions[i](y);
        }

        return x.CompareTo(y);
    }
}

使用示例:

new DateTimeComparer(DateTimeComparer.Prescision.Day).Compare(Date1, Date2)

8
使用TimeSpan,您可以获得所需的所有精度:
DateTime dt1, dt2;
double d = (dt2 - dt1).TotalDays;
double h = (dt2 - dt1).TotalHours;
double m = (dt2 - dt1).TotalMinutes;
double s = (dt2 - dt1).TotalSeconds;
double ms = (dt2 - dt1).TotalMilliseconds;
double ticks = (dt2 - dt1).Ticks;

测试表明,从两个相等的DateTime值创建的TimeSpan,除了TotalMilliseconds之外,其他所有值都大于1。 - dudeNumber4
@hoang 我撤回之前的言论。我错了。我只是想说,你不能直接比较这些值,因为总会有微小的差异。 - dudeNumber4
@hoang 现在我有点困惑了。我确实看到了很大的差异。DateTime d1 = DateTime.Now; DateTime d2 = new DateTime(d1.Year, d1.Month, d1.Day, d1.Hour, d1.Minute, d1.Second, d1.Millisecond); TimeSpan diff = d2 - d1; Debug.Print($"Days: {diff.TotalDays}"); - dudeNumber4
这是因为在毫秒之后有一些“未考虑”的滴答声。试试这个:DateTime d2 = new DateTime(d1.Ticks); - hoang

3
这个比较器类怎么样?
public class DateTimeComparer : Comparer<DateTime>
{
    private string _Format;

    public DateTimeComparer(string format)
    {
        _Format = format;
    }

    public override int Compare(DateTime x, DateTime y)
    {
        if(x.ToString(_Format) == y.ToString(_Format))
            return 0;

        return x.CompareTo(y);
    }
}

这可以被以下人员使用:

List.Sort(new DateTimeComparer("hh:mm"));

2
你可以将它们转换为字符串格式并相互比较。这也让你自由选择比较参数,例如仅比较时间而不比较日期等等。
if (String.Format("{0:ddMMyyyyHHmmss}", date1) == String.Format("{0:ddMMyyyyHHmmss}", date2))
{
     // success
}

谢谢,好主意。不过我认为更优雅的写法是 date1.ToString("ddMMyyyyHHmmss") == date2.ToString("ddMMyyyyHHmmss") - Jérôme MEVEL
这看起来很简洁明了,但它将数字转换为字符串,然后比较字符串 - 与这里的其他解决方案相比效率低下。 - artoonie

1
我写这个是为了帮助自己:

    internal class ImpreciseCompareDate : IComparer<DateTime>
{
    private readonly double _Tolerance;

    public ImpreciseCompareDate(double MillisecondsTolerance)
    {
        _Tolerance = MillisecondsTolerance;
    }

    public int Compare(DateTime x, DateTime y)
    {
        return Math.Abs((x - y).TotalMilliseconds) < _Tolerance ? 0 : x.CompareTo(y);
    }
}

公差可以设置为(10d/3d),以考虑 SQL 服务器的1/300毫秒。如果超过公差,则委托给默认比较器。


这种方法的优点显然在于能够设置任意公差,独立于 datetime 已经提供的级别(秒、毫秒等)。这是因为 SQL Server 的公差即使在其中一个级别上也不会中断,例如 1/300 秒。 - Sprague

0
非常简单的解决方案,来自我的代码:
TimeSpan timeDifference = presentLastSavedDate.Subtract(previousLastSavedDate);
if (timeDifference.Seconds > 0)
{
    return Content(HttpStatusCode.Conflict, ALREADY_CHANGED_MSG);
}

0
另一种方法是通过使用简单(非四舍五入)计算在刻度级别上进行首次转换:
var now = DateTime.UtcNow;
// 636340541021531973, 2017-06-26T06:08:22.1531973Z

var millisecondsPrecision = new DateTime(now.Ticks / 10000 * 10000, now.Kind);
// 636340541021530000, 2017-06-26T06:08:22.1530000Z

var secondsPrecision = new DateTime(now.Ticks / 10000000 * 10000000, now.Kind);
// 636340541020000000, 2017-06-26T06:08:22.0000000Z

var minutePrecision = new DateTime(now.Ticks / (10000000*60) * (10000000*60), now.Kind);
// 636340541000000000, 2017-06-26T06:08:00.0000000Z

0

我已经创建了一个非常快速的比较函数,用于比较不同精度的DateTime。所有计算都是算术计算,没有创建新对象。

public enum DateTimeComparePrecision : long
{
    Millisecond  = TimeSpan.TicksPerMillisecond,
    Second = TimeSpan.TicksPerSecond,
    Minute = TimeSpan.TicksPerMinute,
    Hour = TimeSpan.TicksPerHour,
    Day = TimeSpan.TicksPerDay,
}


public static bool DatesAreEqual(DateTime d1, DateTime d2, DateTimeComparePrecision Precision)
{
    return (d1.Ticks - (d1.Ticks % (long)Precision)) == (d2.Ticks - (d2.Ticks % (long)Precision));
}        


public static int DatesCompare(DateTime d1, DateTime d2, DateTimeComparePrecision Precision)
{
    long Day1 = (d1.Ticks - (d1.Ticks % (long)Precision));
    long Day2 = (d2.Ticks - (d2.Ticks % (long)Precision));

    if (Day2 > Day1) 
        return 1;            

    if (Day2 < Day1)            
        return -1;            

    return 0;
}

如何将Ticks转换为比较

DateTime NowIs = DateTime.UtcNow;
Console.WriteLine($"{NowIs:dd MM yyyy HH:mm:ss.fffffff}");

DateTime d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerMillisecond)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerSecond)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerMinute)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerHour)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerDay)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

输出

01 03 2022 12:51:26.7237323
01 03 2022 12:51:26.7230000
01 03 2022 12:51:26.0000000
01 03 2022 12:51:00.0000000
01 03 2022 12:00:00.0000000
01 03 2022 00:00:00.0000000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接