Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do I detect if two dates straddle a weekend?
    primarykey
    data
    text
    <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>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload