我遇到了以下要求的有趣问题: 测试进程是否在同一天运行,如果没有运行该进程。日期存储为DataTimeOffset。
我原来的方法是:
大多数情况都有效,但我遇到了一个逻辑失败的情况。如果其中一个值的时间接近上一天/下一天,那么当转换为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));
}
我的直觉是,有一种更清洁的方法,而不是根据另一个值增加/减少。有更好的方法吗?
主要标准:
答案 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作为参考框架。首先,只要在相同的时区进行比较,它就无关紧要,但正如您所发现的那样,它可以将它们放在一天边界的任何一侧。
我认为您需要优化您的规范。问问自己,您要确定以下哪一项。
它也可能是别的东西。实施的定义(但被您拒绝)是&#34;值是否出现在同一个UTC日历日#34;。
答案 2 :(得分:1)
首先,你需要弄清楚程序应该做什么的一些混乱。对于两个通用时区中的两个通用时间戳(两个DateTimeOffset
实例没有特定限制),没有“日历日”这样的概念。每个时区都有自己的日历日。例如,我们可以有两个DateTimeOffset
实例,名为first
和second
,它们具有不同的偏移量。让我们可视化时间轴,并标记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.Date
和second.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天。