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