DateTimeOffset当天比较

时间:2014-11-23 19:07:24

标签: c# datetimeoffset

我遇到了以下要求的有趣问题: 测试进程是否在同一天运行,如果没有运行该进程。日期存储为DataTimeOffset。

我原来的方法是:

  1. 将两个值都转换为UTC,因为这些日期可能已在不同时区创建并具有不同的偏移量。
  2. 查看每个值的日期值。这是在转换为UTC后完成的,因为Date方法忽略了偏移量。
  3. 大多数情况都有效,但我遇到了一个逻辑失败的情况。如果其中一个值的时间接近上一天/下一天,那么当转换为UTC时,它将更改日期。如果其他值没有时间也转换为上一天/下一天,则日期比较失败。

    所以我最终得到了以下逻辑来包含这种情况:

    public static bool SameDate(DateTimeOffset first, DateTimeOffset second)
    {
        bool returnValue = false;
        DateTime firstAdjusted = first.ToUniversalTime().Date;
        DateTime secondAdjusted = second.ToUniversalTime().Date;
    
        // If date is now a day ahead after conversion, than add/deduct a day to other date if that date hasn't advanced
        if (first.Date < firstAdjusted.Date && second.Date == secondAdjusted.Date)
            secondAdjusted = secondAdjusted.Date.AddDays(1);
        if (first.Date > firstAdjusted.Date && second.Date == secondAdjusted.Date)
            secondAdjusted = secondAdjusted.Date.AddDays(-1);
    
        if (second.Date < secondAdjusted.Date && first.Date == firstAdjusted.Date)
            firstAdjusted = firstAdjusted.Date.AddDays(1);
        if (second.Date > secondAdjusted.Date && first.Date == firstAdjusted.Date)
            firstAdjusted = firstAdjusted.Date.AddDays(-1);
    
        if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0)
            returnValue = true;
    
        return returnValue;
    }
    

    现在通过的单元测试失败了:

     [TestMethod()]
     public void SameDateTest()
     {
     DateTimeOffset current = DateTimeOffset.Now;
     DateTimeOffset first = current;
     DateTimeOffset second = current;
    
     // 23 hours later, next day, with negative offset (EST) -- First rolls over
     first = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0));
     second = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0));
     Assert.IsFalse(Common.SameDate(first, second));
    
     // 23 hours earlier, next day, with postive offset -- First rollovers
     first = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0));
     second = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0));
     Assert.IsFalse(Common.SameDate(first, second));
    
     // 23 hours later, next day, with negative offset (EST) -- Second rolls over
     first = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0));
     second = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0));
     Assert.IsFalse(Common.SameDate(first, second));
    
     // 23 hours earlier, next day, with postive offset -- Second rolls over
     first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0));
     second = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0));
     Assert.IsFalse(Common.SameDate(first, second));
    }
    

    我的直觉是,有一种更清洁的方法,而不是根据另一个值增加/减少。有更好的方法吗?

    主要标准:

    1. 调整两个日期以获得相同的偏移量。
    2. 仅当第一个和第二个日期都发生在同一日历日而不是24小时内时,才返回true。

5 个答案:

答案 0 :(得分:4)

调整两个日期差异的其中一个日期:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second)
{
    bool returnValue = false;
    DateTime firstAdjusted = first.ToUniversalTime().Date;
    DateTime secondAdjusted = second.ToUniversalTime().Date;

    // calculate the total diference between the dates   
    int diff = first.Date.CompareTo(firstAdjusted) - second.Date.CompareTo(secondAdjusted);
    // the firstAdjusted date is corected for the difference in BOTH dates.
    firstAdjusted = firstAdjusted.AddDays(diff);

    if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0)
        returnValue = true;

    return returnValue;
}

在这个函数中我假设偏移量永远不会超过24小时。 IE日期与其调整日期之间的差异不会是两天或更多天。如果不是这种情况,那么您可以使用time span比较。

答案 1 :(得分:3)

您描述的一般方法(转换为公共时区然后比较日期部分)是合理的。这里的问题实际上是决定参考框架的问题。您可以任意选择UTC作为参考框架。首先,只要在相同的时区进行比较,它就无关紧要,但正如您所发现的那样,它可以将它们放在一天边界的任何一侧。

我认为您需要优化您的规范。问问自己,您要确定以下哪一项。

  • 值是否出现在指定时区的同一日历日。
  • 这些值是否相隔不超过12小时(+/- 12小时是24小时)。
  • 价值是否相隔不超过24小时。

它也可能是别的东西。实施的定义(但被您拒绝)是&#34;值是否出现在同一个UTC日历日#34;。

答案 2 :(得分:1)

首先,你需要弄清楚程序应该做什么的一些混乱。对于两个通用时区中的两个通用时间戳(两个DateTimeOffset实例没有特定限制),没有“日历日”这样的概念。每个时区都有自己的日历日。例如,我们可以有两个DateTimeOffset实例,名为firstsecond,它们具有不同的偏移量。让我们可视化时间轴,并标记DateTimeOffset实例引用*的具体时刻和相应时区中的日历日(即0:00到23:59之间的时间间隔)。特定时区)|__|。它看起来像这样:

first:  ........|___________________*__|.......
second: ...|______*_______________|............

first的时区内,second事件发生在相同日历日(凌晨2-3点)之间。在second的时区内,first事件发生在关注日历日(凌晨1-2点)之间。

所以很明显这个问题需要澄清,可能还有一些范围限制。那些真的是通用时区,还是它们是同一个地方的时区,可能只在夏令时间有所不同?在那种情况下,你为什么不忽略时区?例如。在2014年11月2日00:10到23:50之间,UTC偏移量已经改变(EDT-> ET)并且两个时刻相隔超过24小时的时间并不重要:new DateTimeOffset(2014, 11, 02, 00, 10, 00, new TimeSpan(-4, 0, 0)).Date == new DateTimeOffset(2014, 11, 02, 23, 50, 00, new TimeSpan(-5, 0, 0)).Date 。基本上,这是martijn试图做的事情,但是以一种非常复杂的方式。什么时候试试

public static bool SameDateSimple(DateTimeOffset first, DateTimeOffset second)
{
    return first.Date == second.Date;
}

它适用于所有上述单元测试。而且,这也是大多数人称之为“同一日历日”的情况,当保证两个实例在一个地方引用时间时。

或者,如果你真的要比较两个“随机”时区,你必须选择你的参考时区。你最初尝试过它可能是UTC。或者,从人的角度来看,使用第一个时区作为参考可能更合乎逻辑(你也可以选择第二个时区,它会给出不同的结果,但两个变量都“同样好”):

public static bool SameDateGeneral(DateTimeOffset first, DateTimeOffset second)
{
    DateTime secondAdjusted = second.ToOffset(first.Offset).Date;
    return first.Date == secondAdjusted.Date;
}

这对于某些上述测试不起作用,但在两个随机时区“正确”(在某种意义上)工作的意义上更为通用:如果你尝试first = new DateTimeOffset(2014, 1, 2, 0, 30, 0, new TimeSpan(5, 0, 0)), second = new DateTimeOffset(2014, 1, 1, 23, 30, 0, new TimeSpan(4, 0, 0)),那么简单{{1返回SameDateSimple(和martijn一样),即使这两个实例引用完全相同的时刻(两者都是2014-01-01 19:30:00Z)。 false正确返回SameDateGeneral

答案 3 :(得分:0)

首先,您的UnitTest中有错误。

    [TestMethod()]
    public void SameDateTest()
    {
        DateTimeOffset current = DateTimeOffset.Now;
        DateTimeOffset first = current;
        DateTimeOffset second = current;

        // 23 hours later, next day, with negative offset (EST) -- First rolls over
        first = new DateTimeOffset( 2014, 1, 1, 19, 0, 0, new TimeSpan( -5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 2, 18, 0, 0, new TimeSpan( -5, 0, 0 ) );
        Assert.IsTrue( DateTimeComparison.Program.SameDate( first, second ) );

        // 23 hours earlier, next day, with positive offset -- First rollovers
        first = new DateTimeOffset( 2014, 1, 1, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 2, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
        Assert.IsFalse( DateTimeComparison.Program.SameDate( first, second ) );

        // 23 hours later, next day, with negative offset (EST) -- Second rolls over
        first = new DateTimeOffset( 2014, 1, 2, 18, 0, 0, new TimeSpan( -5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 1, 19, 0, 0, new TimeSpan( -5, 0, 0 ) );
        Assert.IsTrue( DateTimeComparison.Program.SameDate( first, second ) );

        // 23 hours earlier, next day, with positive offset -- Second rolls over
        first = new DateTimeOffset( 2014, 1, 2, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 1, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
        Assert.IsFalse( DateTimeComparison.Program.SameDate( first, second ) );
    }

这是经过纠正的测试。你的第一次测试应该返回&#34; True&#34;,就像你的第三次测试一样。被比较的那些DateTimeOffsets在相同的UTC日期。只有测试用例2和4应该返回&#34; False&#34;,因为那些DateTimeOffsets实际上是在2个不同的日期。

其次,您可以将SameDate()功能简化为:

    public static bool SameDate( DateTimeOffset first, DateTimeOffset second )
    {
        bool returnValue = false;
        DateTime firstAdjusted = first.UtcDateTime;
        DateTime secondAdjusted = second.UtcDateTime;

        if( firstAdjusted.Date == secondAdjusted.Date )
            returnValue = true;

        return returnValue;
    }

正如您感兴趣的那样,first.Datesecond.Date实际上是在同一个UTC日期,这样就可以完成工作而无需额外的转换/转换为UTC。

第三,您可以使用这个完整的程序测试您的测试用例:

using System;

namespace DateTimeComparison
{
   public class Program
   {
       static void Main( string[] args )
       {
           DateTimeOffset current = DateTimeOffset.Now;
           DateTimeOffset first = current;
           DateTimeOffset second = current;

           // 23 hours later, next day, with negative offset (EST) -- First rolls over
           first = new DateTimeOffset( 2014, 1, 1, 19, 0, 0, new TimeSpan( -5, 0, 0 ) );
           second = new DateTimeOffset( 2014, 1, 2, 18, 0, 0, new TimeSpan( -5, 0, 0 ) );
           if( false == SameDate( first, second ) ) {
               Console.WriteLine( "Different day values!" );
           } else {
               Console.WriteLine( "Same day value!" );
           }

           // --Comment is wrong -- 23 hours earlier, next day, with positive offset -- First rollovers
           first = new DateTimeOffset( 2014, 1, 1, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
           second = new DateTimeOffset( 2014, 1, 2, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
           if( false == SameDate( first, second ) ) {
               Console.WriteLine( "Different day values!" );
           } else {
               Console.WriteLine( "Same day value!" );
           }

           // 23 hours later, next day, with negative offset (EST) -- Second rolls over
           first = new DateTimeOffset( 2014, 1, 2, 18, 0, 0, new TimeSpan( -5, 0, 0 ) );
           second = new DateTimeOffset( 2014, 1, 1, 19, 0, 0, new TimeSpan( -5, 0, 0 ) );
           if( false == SameDate( first, second ) ) {
               Console.WriteLine( "Different day values!" );
           } else {
               Console.WriteLine( "Same day value!" );
           }


           // --Comment is wrong --  23 hours earlier, next day, with positive offset -- Second rolls over
           first = new DateTimeOffset( 2014, 1, 2, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
           second = new DateTimeOffset( 2014, 1, 1, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
           if( false == SameDate( first, second ) ) {
               Console.WriteLine( "Different day values!" );
           } else {
               Console.WriteLine( "Same day value!" );
           }
       }

       public static bool SameDate( DateTimeOffset first, DateTimeOffset second )
       {
           bool returnValue = false;
           DateTime firstAdjusted = first.UtcDateTime;
           DateTime secondAdjusted = second.UtcDateTime;

           if( firstAdjusted.Date == secondAdjusted.Date )
               returnValue = true;

           return returnValue;
       }          
    }
}

在任意位置设置断点并在调试器中运行此短程序。这将向您显示测试用例2和测试用例4实际上相隔UTC时间超过2天,因此应该是错误的。此外,它将显示测试用例1和测试用例3在相同的UTC日期,并且从正常运行的SameDate()开始应该是正确的。

如果您希望第二个和第四个测试用例在同一天相隔23小时,那么对于测试用例2,您应该使用:

        // 23 hours earlier, next day, with positive offset -- First rollovers
        first = new DateTimeOffset( 2014, 1, 2, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 1, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
        Assert.IsTrue( DateTimeComparison.Program.SameDate( first, second ) );

对于测试用例4,您应该使用:

        // 23 hours earlier, next day, with positive offset -- Second rolls over
        first = new DateTimeOffset( 2014, 1, 2, 5, 0, 0, new TimeSpan( 5, 0, 0 ) );
        second = new DateTimeOffset( 2014, 1, 3, 4, 0, 0, new TimeSpan( 5, 0, 0 ) );
        Assert.IsTrue( DateTimeComparison.Program.SameDate( first, second ) );

答案 4 :(得分:0)

这个功能怎么样:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second)
{
    return Math.Abs((first - second).TotalDays) < 1;
}

你可以减去两个日期(DateTimeOffset是智能的并知道时区),它会给你一个范围,一个时间跨度。然后你可以检查这个范围是否是+ - 1天。