繁体   English   中英

如何检测两个日期是否跨越周末?

[英]How do I detect if two dates straddle a weekend?

问题

给定两个日期时间, dt0dt1 (可能是乱序),什么算法可以确定两个日期之间是否至少有24小时的周末(SAT,SUN)?

假设有一个dayofweek()函数为SUN返回0,为MON返回1,等等...

注意:问题很容易根据线段在几何上进行可视化,但目前我的计算结果不计算在内。

以下解决方案适用于UTC,但DST会失败。

  • weekdayno()实现不包括:SUN == 0,MON == 1等...
  • isWeekday()也没有显示,但微不足道的实施一旦你有dayofweek()
  • 二进制operator-()实现也没有显示,但我们只是简单地将两个实例转换为UNIX时间(自Epoch以来的秒数)并采用差异来产生两个DateTime之间的秒数。
  • hh() mm() ss()只是返回小时,分钟和秒的const访问器

关于夏令时, 詹姆斯麦克尼利斯是正确的。

让这段代码适用于一般的DST案例并非易事:需要添加tz以及在任何地方进行任何类型的日期算法都需要仔细考虑。 将需要额外的单元测试。

得到教训

  • 查询stackoverflow以查看问题的各种方法。
  • 你永远不会有太多的单元测试:需要它们来清除奇怪的边缘情况
  • 如果可能,使用可视化来查看问题
  • 当您查看详细信息(例如DST)时,看似微不足道的问题实际上有点棘手。
  • 保持解决方案尽可能简单,因为您的代码很可能会更改:为了修复错误/新测试用例或为了添加新功能(例如使其适用于DST)。 保持其可读性和易于理解:优先于交换机/案例的算法。
  • 要勇敢并尝试一下:坚持不懈地解决问题直到出现问题。 使用单元测试,这样你就可以不断重构。 编写简单的代码需要做大量的工作,但最终还是值得的。

结论

目前的解决方案足以满足我的目的(我将使用UTC来避免DST问题)。 我将选择holygeek的答案,因为他建议我画一些ASCII艺术。 在这种情况下,这样做有助于我提出一种易于理解的算法,并且实际上就像我可能做到的那样简单。 感谢所有人为此问题的分析做出贡献。

static const size_t ONEDAYINSECS = (24 * 60 * 60); 

DateTime
DateTime::nextSatMorning() const
{
  // 0 is SUN, 6 is SAT
  return *this + (6 - weekdayno()) * ONEDAYINSECS -
    (((hh() * 60) + mm())*60 + ss());
}

DateTime 
DateTime::previousSunNight() const
{
  return *this - ((weekdayno() - 1 + 7)%7) * ONEDAYINSECS -
    (((hh() * 60) + mm())*60 + ss());
}

bool
DateTime::straddles_24HofWeekend_OrMore( const DateTime& newDt ) const
{
  const DateTime& t0 = min( *this, newDt );
  const DateTime& t1 = max( *this, newDt );

  // ------------------------------------
  //
  // <--+--F--+--S--+--S--+--M--+-->
  //    t0    ^           ^    t1
  //    +---->+           +<----|
  //          |           |
  //          +<--nSecs-->+
  //        edge0       edge1
  //
  // ------------------------------------

  DateTime edge0 = t0.isWeekday() ? t0.nextSatMorning()   : t0;                       
  DateTime edge1 = t1.isWeekday() ? t1.previousSunNight() : t1;

  return (edge1 - edge0) > ONEDAYINSECS;
}

John Leidegren要求进行我的单元测试,所以这里有(使用googletest)注意他们通过上面的非DST案例(为UTC运行) - 我希望当前的实现失败的DST案例(没有将它们添加到以下测试用例)。

TEST( Test_DateTime, tryNextSatMorning )
{
  DateTime mon{     20010108, 81315 };
  DateTime exp_sat{ 20010113, 0ul   };

  EXPECT_EQ( exp_sat, mon.nextSatMorning() );
}

TEST( Test_DateTime, tryPrevSunNight )
{
  DateTime tue{      20010109, 81315 };
  DateTime exp_sun1{ 20010108, 0ul   };

  EXPECT_EQ( exp_sun1, tue.previousSunNight() );

  DateTime sun{      20010107, 81315 };
  DateTime exp_sun2{ 20010101, 0ul   };

  EXPECT_EQ( exp_sun2, sun.previousSunNight() );
}

TEST( Test_DateTime, straddlesWeekend )
{
  DateTime fri{ 20010105, 163125 };
  DateTime sat{ 20010106, 101515 };
  DateTime sun{ 20010107, 201521 };
  DateTime mon{ 20010108,  81315 };
  DateTime tue{ 20010109,  81315 };

  EXPECT_FALSE( fri.straddles_24HofWeekend_OrMore( sat ));
  EXPECT_TRUE(  fri.straddles_24HofWeekend_OrMore( sun ));
  EXPECT_TRUE(  fri.straddles_24HofWeekend_OrMore( mon ));
  EXPECT_TRUE(  sat.straddles_24HofWeekend_OrMore( sun ));
  EXPECT_TRUE(  sat.straddles_24HofWeekend_OrMore( mon ));
  EXPECT_FALSE( sun.straddles_24HofWeekend_OrMore( mon ));
  EXPECT_TRUE(  fri.straddles_24HofWeekend_OrMore( tue ));
  EXPECT_FALSE( sun.straddles_24HofWeekend_OrMore( tue ));
}

该图可能有所帮助:

  SAT       SUN
|---------|---------|
a---------b
 a---------b
 ...
          a---------b

我能想到解决这个问题的最简单方法是:复制较小的日期时间值,不断添加,直到它大于其他日期时间值(较大的值)或dayofweek()不再等于0或7。 然后检查您添加的时间总值是否少于24小时。

一个稍微不那么愚蠢的方法是检查一个周末,增加24小时的时间,然后检查一次,以确保它是一个周末,仍然少于第二个日期时间。

只要你的功能找到它的工作日,夏令时就不应真正发挥作用。

以结构化的方式处理它:什么是简单/边缘情况? 平均情况是多少?

简单的情况我可以想到我的头脑(请注意,因为你已经强迫t0为较低的值,我假设在下面):

  1. 如果t1 - t0小于1天,则返回false
  2. 如果t1 - t0 > = 6天,则返回true(即使您在星期天开始,在任何给定的6天区块中总共会有24小时的周末时间)。

然后我们对t0t1采用dayofweek(),并进行一些检查(这是一般情况)。 我们现在可以在这里特别便宜,因为我们知道 t0t1早5天。

编辑:删除我的条件,因为有一些讨厌的小边缘情况我没有考虑。 无论如何,我推荐的解决方案仍然可行,我不会在这里做。

该网站用C#计算营业日,这有帮助吗? http://www.infopathdev.com/forums/t/7156.aspx

暂无
暂无

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

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