title: How do I detect if two dates straddle a weekend?
body: <h2>Problem</h2>
  <p><strong>Given two datetimes, <code>dt0</code> and <code>dt1</code> (could be out of order), what is an algorithm which can determine if there is at least 24 hours worth of weekend (SAT, SUN) between the two dates?</strong></p>
  <p>Assume that there is a <code>dayofweek()</code> function that returns 0 for SUN, 1 for MON, etc...</p>
  <p>Note: the problem is easy to visualize geometrically in terms of line segments, but the calculation eludes me for the moment.</p>
  <h2>Solution</h2>
  <p>The solution below will work for UTC, but it will fail for DST.</p>
  <ul>
  <li><code>weekdayno()</code> implementation not included: SUN==0, MON==1, etc...</li>
  <li><code>isWeekday()</code> is also not shown, but is trivial to implement once you have <code>dayofweek()</code></li>
  <li>binary <code>operator-()</code> implementation also not shown, but we simply convert both instances to UNIX-time (no. of secs since Epoch) and take the difference to yield the number of seconds between two <code>DateTime</code>s</li>
  <li><code>hh()</code> <code>mm()</code> <code>ss()</code> are just const accessors for returning hours, minutes, and seconds, respectively</li>
  </ul>
  <p><strong>James McNellis</strong> is right on the mark concerning DST.</p>
  <p><em>Getting this code to work for the general DST case is non-trivial: need to add tz and anywhere you do any kind of date arithmetic requires careful consideration.  Additional unit tests will be needed.</em></p>
  <h2>Lessons Learned</h2>
  <ul>
  <li>Query stackoverflow for various ways to look at a problem.</li>
  <li>You can never have too many unit tests:  need them to flush out weird edge cases</li>
  <li>Use visualization, if possible, to look at a problem</li>
  <li>What appears to be a trivial problem can actually be a bit tricky when you look at the details (eg DST).</li>
  <li>Keep the solution <strong>as simple as possible</strong> because your code will very likely change: in order to fix for bugs/new test cases or in order to add new features (eg make it work for DST).  Keep it as readable and easy to understand as possible: prefer algorithms over switch/cases.</li>
  <li>Be brave and try things out: keep hammering at the solution until something works comes about.  Use unit-tests so you can continuously refactor.  It takes a lot of work to write simple code, but in the end, it's worth it.</li>
  </ul>
  <h2>Conclusion</h2>
  <p>The current solution is sufficient for my purposes (I will use UTC to avoid DST problems).  I will select <strong>holygeek</strong>'s Answer for his suggestion that I draw some ASCII art.  In this case, doing so has helped me come up with an algorithm that is easy-to-understand and really, as simple as I can possibly make it.  Thanks to all for contributing to the analysis of this problem.</p>
  <pre><code>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&amp; newDt ) const
  {
    const DateTime&amp; t0 = min( *this, newDt );
    const DateTime&amp; t1 = max( *this, newDt );
    
    // ------------------------------------
    //
    // &lt;--+--F--+--S--+--S--+--M--+--&gt;
    //    t0    ^           ^    t1
    //    +----&gt;+           +&lt;----|
    //          |           |
    //          +&lt;--nSecs--&gt;+
    //        edge0       edge1
    //
    // ------------------------------------
  
    DateTime edge0 = t0.isWeekday() ? t0.nextSatMorning()   : t0;                       
    DateTime edge1 = t1.isWeekday() ? t1.previousSunNight() : t1;
                     
    return (edge1 - edge0) &gt; ONEDAYINSECS;
  }
  </code></pre>
  <p><strong>John Leidegren</strong> asked for my unit tests so here there are (using googletest)
  Note that they pass for the non-DST cases above (running for UTC) - I expect the current implementation to fail for DST cases (haven't added them to the test cases below yet).</p>
  <pre><code>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 ));
  }
  </code></pre>