C++ 如何检测一个周末是否有两次约会? 问题

C++ 如何检测一个周末是否有两次约会? 问题,c++,datetime,c++11,C++,Datetime,C++11,给定两个日期时间,dt0和dt1(可能出现故障),有什么算法可以确定两个日期之间是否至少有24小时的周末(周六、周日)? 假设有一个dayofweek()函数,它为SUN返回0,为MON返回1,等等 注意:这个问题很容易以直线段的形式进行几何可视化,但我暂时无法进行计算 解决方案 下面的解决方案适用于UTC,但适用于DST将失败 weekdayno()未包括实现:SUN==0、MON==1等 isWeekday()也没有显示,但是一旦您有了dayofweek() 二进制运算符-()实现也未显示

给定两个日期时间,
dt0
dt1
(可能出现故障),有什么算法可以确定两个日期之间是否至少有24小时的周末(周六、周日)?

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

注意:这个问题很容易以直线段的形式进行几何可视化,但我暂时无法进行计算

解决方案 下面的解决方案适用于UTC,但适用于DST将失败

  • weekdayno()
    未包括实现:SUN==0、MON==1等
  • isWeekday()
    也没有显示,但是一旦您有了
    dayofweek()
  • 二进制
    运算符-()
    实现也未显示,但我们只需将这两个实例转换为UNIX时间(自Epoch以来的秒数),并取其差得出两个
    日期时间
    之间的秒数
  • hh()
    mm()
    ss()
    只是分别返回小时、分钟和秒的常量访问器
詹姆斯·麦克内利斯(James McNellis)在DST方面是正确的

让这段代码在一般的DST情况下工作是非常重要的:需要添加tz,并且在任何地方执行任何类型的日期算法都需要仔细考虑。需要进行额外的单元测试

经验教训
  • 查询stackoverflow以了解查看问题的各种方法
  • 你永远不会有太多的单元测试:需要它们来清除奇怪的边缘用例
  • 如果可能,使用可视化来查看问题
  • 当您查看细节(例如DST)时,看似微不足道的问题实际上可能有点棘手
  • 保持解决方案尽可能简单,因为您的代码很可能会更改:为了修复bug/新的测试用例,或者为了添加新功能(例如使其适用于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;
}

我能想到的解决这个问题的最愚蠢的方法是:复制较小的datetime值,不断添加到它,直到它大于另一个datetime值(较大的一个),或者dayofweek()不再等于0或7。然后检查您添加的总时间值是否小于24小时

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

只要您的功能能够找到白天,夏令时就不应该真正发挥作用。

此图可能有助于:

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

以结构化的方式进行处理:哪些是简单/边缘情况?一般情况是什么

我能想到的简单案例(注意,因为您已经强制
t0
为较低的值,我假设如下):

  • 如果
    t1-t0
    小于1天,则返回false
  • 如果
    t1-t0
    为>=6天,则返回true(在任何给定的6天块中,周末时间始终为24小时,即使是从周日开始)
  • 然后我们对
    t0
    t1
    都取dayofweek(),并进行一些检查(这是平均情况)。我们现在可以在这里特别便宜,因为我们知道
    t0
    只比
    t1
    早5天


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

    这个网站用C#计算工作日,这有帮助吗

    不-这是我正在建设的图书馆的一部分,没有任何限制,从头开始写这篇文章会很乏味,因为人们必须考虑不同时区和地点的夏令时,在其他不规则的日历中,@JamesMcNellis+1指出DST问题:一旦我也想到了基本算法,我会注意它们,但我处理的是日期时间,而不仅仅是日期,所以这有点棘手-另一种可能性是,如果我能检测到工作日和周末之间t0和t1范围内的所有边界,并将我的时间线分为工作日、周末、工作日、周末、工作日、周末等。。。然后,我可以计算工作日段的长度和周末段的长度。我认为这可能有点接近……您的日期时间应该是一个大值,例如自1970年1月1日UTC 00:00:00以来的秒数。如果你是自己编造的,也可能会有同样令人困惑的事情。我如何在内部实现datetime在这里不会有什么不同。然而,dst是一个问题-例如,两个日期可能是周六上午9点和周日上午9点,但它们之间的间隔可能只有23小时或多达25小时-因此任何类型的日期时间算法都可能变得棘手-解决这一问题的一种方法是要求所有日期都是UTC或在没有dst的时区,但这有点像逃避现实+1赞成-可视化肯定会有帮助:实际上你给了我一个便宜的把戏-如果我给a加上24小时,它小于b,我就完成了(根据a的位置做了一点调整)。。。我会想得更多一些
      SAT       SUN
    |---------|---------|
    a---------b
     a---------b
     ...
              a---------b