I’m not sure what I did wrong here, but, my first version of this function didn’t work. The function returns true if the current time is withing two time spans. This code was written in a rush, without really thinking about how to do it, because it seemed pretty straightforward. The code, however, was a mess (and embarassing).
Private Function itIsTimeToWork() As Boolean ' Get the two start and end times, and determine if we're within ' the intervals. Dim now, start1, start2, end1, end2 As DateTime now = DateTime.Now start1 = DateTime.Parse(My.Settings.StartWorkAt1) end1 = DateTime.Parse(My.Settings.EndWorkAt1) If end1 < start1 Then end1 = end1.AddDays(1) End If start2 = DateTime.Parse(My.Settings.StartWorkAt2) end2 = DateTime.Parse(My.Settings.EndWorkAt2) If (end2 < start2) Then end2 = end2.AddDays(1) End If If (start1 < now) And (now < end1) Then itIsTimeToWork = True Exit Function End If If (start2 < now) And (now < end2) Then itIsTimeToWork = True Exit Function End If ' If a time interval crosses midnight, and we're on the ' early morning side of the clock, we adjust the interval to go back ' in time one day, because we want the ending time to be today, and the ' starting time to be yesterday. If (start1.AddDays(-1) < now) And (now < end1.AddDays(-1)) Then itIsTimeToWork = True Exit Function End If If (start2.AddDays(-1) < now) And (now < end2.AddDays(-1)) Then itIsTimeToWork = True Exit Function End If itIsTimeToWork = False End Function
Well, that code failed to work when the time went past midnight. I’m not sure why, but the logic was so disjointed that it seemed like a good idea to rewrite it. Here’s the new version, with comments interspersed.
Private Function itIsTimeToWork() As Boolean ' Get the two start and end times, and determine if we're within ' the intervals. Dim now As DateTime now = DateTime.Now
The function has been refactored into two functions, the second which returns True if the time is within an interval. The logic is now a lot simpler. Not only that, it can be written as a single statement.
Return (timeIsInInterval(DateTime.Parse(My.Settings.StartWorkAt1), _ DateTime.Parse(My.Settings.EndWorkAt1), now) Or _ timeIsInInterval(DateTime.Parse(My.Settings.StartWorkAt2), _ DateTime.Parse(My.Settings.EndWorkAt2), now)) End Function
Surprisingly enough, the logic is pretty simple. At least it is after you’ve thought about it. I had to spend a while playing with the logic to see that it reduced to this. It’s a little bit like doing math — you try different things until you get a nice, tidy statement.
' Intended usage is: timeIsInInterval( #8:00 PM#, #2:00 AM#, DateTime.Now ) ' so that the date is always "today". Function timeIsInInterval(ByVal s As DateTime, ByVal e As DateTime, ByVal time As DateTime)
First, isolate the most common, simple situation, and deal with it.
If (s < e) Then If s < time And time < e Then Return True End If Else
Second, deal with the difficult outlier situation. I had to think this out, and originally split it between times between midnight and the starting time before midnight, and the times after midnight, but before the ending time. I coded it that way. Then, I saw that the two situations could be combined with an Or. I also had to verify that the date issues weren’t going to be a problem. After the thinking, I made some test cases and ran the function against the tests.
' if the interval is reversed, assume it straddles midnight ' and the logic is inverted (the valid ranges are between midnight and the times) If s < time Or time < e Then Return True End If End If Return False End Function
Here’s the complete code. Shorter, better, and factored correctly.
Private Function itIsTimeToWork() As Boolean ' Get the two start and end times, and determine if we're within ' the intervals. Dim now As DateTime now = DateTime.Now Return (timeIsInInterval(DateTime.Parse(My.Settings.StartWorkAt1), _ DateTime.Parse(My.Settings.EndWorkAt1), now) Or _ timeIsInInterval(DateTime.Parse(My.Settings.StartWorkAt2), _ DateTime.Parse(My.Settings.EndWorkAt2), now)) End Function ' Intended usage is: timeIsInInterval( #8:00 PM#, #2:00 AM#, DateTime.Now ) ' so that the date is always "today". Function timeIsInInterval(ByVal s As DateTime, ByVal e As DateTime, ByVal time As DateTime) If (s < e) Then If s < time And time < e Then Return True End If Else ' if the interval is reversed, assume it straddles midnight ' and the logic is inverted (the valid ranges are between midnight and the times) If s < time Or time < e Then Return True End If End If Return False End Function