# Determine Next Event Using DateTime and TimeSpan

I'm currently working on a collection of sub-projects, one of which was a backup manager. I needed to be able to allow the user to specify a backup time and a repeat interval. After thinking about the solution, decided it would be best for the user to provide an Initial DateTime of the first occurance and how frequent they would like to repeat the event. This class can be used to help determaine Alarms, Reminders or anything that requires an event to be triggered at a certain time.

Fun and Good for Beginners

It was good fun writing this wee class and thought it would be a great example for junior developers on using DateTime and TimeSpan to determine things like DateTime manipulation, and the difference between two Dates and Times.

Note: “Event” throughout this snippet is referring to something like an alarm, a calendar reminder or any other action to be performed at a certain date and time.

## A Quick Introduction

User Input Required

The Date of the first occurrence
The Time of the First Occurrence
The Repeat Interval

0 = No Repeat
1 = Daily
2 = Weekly
3 = Monthly
4 = Yearly

The “NextEvent” method of the EventManager class then calculates the next occurrence of the event based on today’s date and time.

Returns

the next event date, time and a duration until the next event.

What's happening

Let’s say todays date is: Saturday 4th October 2014 at about 10:30
The user created an event whose first occurrence was on the Thursday 25th Sept 2014 21:00 Depending on the users repeat interval their repeat options would be (Excluding no repeat)

Daily = every day at 21:00
Weekly = every Thursday at 21:00
Monthly = 25th of every month at 21:00
Yearly = every 25th of September at 21:00 The repeat events would be as follows

Daily = Today at 21:00
Weekly = Thursday 9th October at 21:00
Monthly = Saturday 25th October at 21:00
Yearly = Friday 25th September 2015 at 21:00

The method also calculates the time of day to make sure if the date of the next event is today, the event time is still in the future, otherwise the next event will occur another interval step in the future (Day, Week, Month, Year).

As you can see from the next image, the user set the first event day time to Saturday 27th September at 21:00 and the repeat is weekly. Because in this example Today is Saturday, the time is referenced. As its only 10:30 and the event doesn’t occur until 21:00 the next event will be today. In a few hours. Let’s change the event time to a day which matches the current day (Saturday) but move the time to a position before the current time. As you’d expect, because the time has passed, the next event is now just under a week away on the next repeat interval: Saturday 11th October at 10:15

## Using The EventManager Class

Note: EventManager's methods are shared so you don't need to create a new instance before using them.

A new DateTime object is created and populated with the users chosen date and time for their event.

``````Dim stpSelected As DateTime = New DateTime(START_YEAR, _
START_MONTH, _
START_DAY, _
START_HOUR, _
MINUTE, 0)
``````

The final argument of `New DateTime` is hard-coded to 0. This is the seconds value, which I do not calculate (This is personal choice, not essential)

The start date and time is then passed is passed to the NextEvent method, along with the repeat value. The repeat value can be set by integer 0 to 5 or by using the enum `EventManager.RepeatValues`

``````Dim TV As EventManager.TimeValues = EventManager.NextEvent(stpSelected, REPEAT_VALUE)
``````

A new TimeValues object is created and is populated by NextEvents result.

RepeatValues

``````    Public Enum RepeatValues
NoRepeat = 0
Day = 1 'Repeat Every Day
Week = 2 'Repeat each week on the selescted DayOfWeek
Month = 3 'Repeat each month on the selected day
Year = 4 'repeat each year on the selected date
End Enum
``````

TimeValues

``````    Public Class TimeValues
Public Sub New(NextDate As DateTime, Duration As TimeSpan)

_NextDate = NextDate
_Duration = Duration

End Sub

Private _NextDate As DateTime
Public ReadOnly Property NextDate As DateTime
Get
Return _NextDate
End Get
End Property

Private _Duration As TimeSpan
Public ReadOnly Property Duration As TimeSpan
Get
Return _Duration
End Get
End Property

End Class
``````

TimeValues is a very simple class which does nothing more than hold a Next Event DateTime and Duration TimeSpan.

Note: The Values are calculated from your current system date and time.

Using The Returned Results

Here is one way to use the returned results:

``````If Not (IsNothing(TV)) Then

lblNext.Text = EventManager.VeryLongDate(TV.NextDate)
lblDuration.Text = TV.Duration.Days & " days, " & TV.Duration.Hours & "hrs, " & TV.Duration.Minutes & "mins"

Else

lblNext.Text = "This Event Has Passed"
lblDuration.Text = "This Event Does Not Repeat"

End If
``````

Because NextEvent returns nothing if the event's StartDate is in the past and the RepeatValue is set to 0 (NoRepeat), we provide the `If Not (IsNothing(TV)) Then` clause

You may have noticed this method `EventManager.VeryLongDate(TV.NextDate)`. This is a simple method created to return a long-long DateTime value as you can see in the images e.g. "Saturday 4th of October 2014 at 21:00"

## EventManager Methods

VeryLongDate

The VeryLongDate method has three arguments, two of which are optional.

1. DateValue: The DateTime object to be evaluated
2. IncludeTime: Include the time in the returned string
3. Time Deliminator: A string that seperates the date string and stime string (can be empty)

``````Public Shared Function VeryLongDate(DateValue As DateTime, _
Optional IncludeTime As Boolean = True, Optional TimeDeliminator As String = " at ") As String
``````

For ease of use I get the long values of elements such as DayOfWeek and Month

``````        Dim _Day As String = DateTime.Parse(DateValue).DayOfWeek.ToString
Dim _Month As String = MonthName(DateValue.Month)
``````

Using another method `SuffixDay` I suffix the day value to eg 1 becomes 1st, 22 becomes 22nd etc.

``````        Dim _Date As String = SuffixDay(DateValue)
``````

And preoduce a nice time string ensuring single digits are preceeded by 0 e.g. 1:9 becomes "01:09"

``````        Dim _Time As String = Format(DateValue.Hour, "00") & ":" & Format(DateValue.Minute, "00")
``````

Using `String.Format` we pin our values together. I find String.Format particularly useful as I can change the string format with the greatest of ease.

``````        If IncludeTime = True Then
Return String.Format("{0} {1} of {2} {3} {4} {5}", _Day, _Date, _Month, DateValue.Year, TimeDeliminator, _Time)
Else
Return String.Format("{0} {1} of {2} {3}", _Day, _Date, _Month, DateValue.Year)
End If
``````

SuffixDay

``````    Public Shared Function SuffixDay(DateValue As DateTime) As String

Dim sDay As String = DateValue.Day.ToString()

Select Case Strings.Right(sDay, 1)
Case "1" : sDay += "st"
Case "2" : sDay += "nd"
Case "3" : sDay += "rd"
Case "4" To "9", "0" : sDay += "th"
End Select

Return sDay

End Function
``````

another, nice, simple function whic simply checks the last digit of a number and adds the apropriate suffix eg 1 becomes 1st, 21 becomes 21st and 30 becomes 30th

NextEvent

This is the main method providing the user with the next event DateTime and a TimeSpan until the next event from the current system date and time.

``````    Public Shared Function NextEvent(StartDateTime As DateTime, Repeat As RepeatValues) As TimeValues
``````

I start by storing the current DateTime and the DateTime of the first events occurance

``````        'Get current DateTime
Dim CurrentTime As DateTime = Now

'Get DateTime of first event occurance
Dim NextTime As DateTime = StartDateTime
``````

And get the difference between the Current and Next values as a TimeSpan.

``````        'Get the differance between start and next as a TimeSpan
Dim ElapsedTime As TimeSpan = NextTime.Subtract(CurrentTime)

Dim Days As Integer = 1
Dim Dayn As Integer = 1
``````

TimeSpan will hold an absolute value if the Next event is in the future and a negative value if it's in the past. If the NExt event is in the future, we don't need to process anything, we simply return the next DateTime and the TimeSpan (Duration) until the next event.

``````        'If the TimeSpan < 0 then the next event would be in the past so...
If ElapsedTime.Milliseconds < 0 Then
``````

Depending on the users Repeat interval we have to calculate when the next event will happen

``````            Select Case Repeat
``````

If the user doesn't want this event to repeat, we simply return nothing

``````                Case 0 'Never

'This user doesn't want to repeat this event so let's
'return nothing for now.
Return Nothing
``````

Daily is pretty easy too, If it's in the past, we'll just set the next event to tomorrow.

``````                Case 1 'Daily

'Add one day on to the next event DateTime (Tomorrow)
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
``````

Weekly is a little more complicated. The repeat may be every Friday, but today is Sunday. In our heads, this is quite simple, so how do we determine this in code.

``````                Case 2 'Weekly
``````

I record todays DayOfWeek value (Days) and NextTimes DayOfWeek value (Dayn)

``````                    'Add one week on to the next event DateTime (Next Week)
Dayn = NextTime.DayOfWeek
Days = CurrentTime.DayOfWeek
``````

The integer values for DayOfWeek Start from 0 (Sunday) to 6 (Saturday). I want Sunday to be the last day of the week, so I switch the value of Sunday (0) to 7. This now let's me work with Monday(1) to Sunday (7

``````                    'Switch from 0 (Sunday) -> 6 (Saturday) to 1 (Monday) -> 7 (Sunday)
If Days = 0 Then Days = 7
If Dayn = 0 Then Dayn = 7
``````

DayDiff holds the value differencr between Days and Dayn

``````                    Dim DayDiff As Integer = 0
``````

If NextDay value is higher then TodaysDay value then the Day Differance is simple, we simply subtract the current day value from the next day value. e.g Today is Wednesday (3) Next is Friday (5)... The difference between Now and Next is 2 (5 - 3)

``````                    If Math.Abs(0 - Dayn) > Math.Abs(0 - Days) Then
DayDiff = Math.Abs((Math.Abs(0 - Days)) - Math.Abs(0 - Dayn))
Else
``````

If NextDay value is before todays value I simply get the number of days between today and sunday, then add on the value of the next days value e.g Today is Friday (5) Next is Wed(3). Sunday (7) - Friday (5) = 2. 2 + Next (Wed (3)). So the DayDiff between Friday (5) and Wednesday(3) is 5.

``````                        DayDiff = (7 - Math.Abs(0 - Days)) + Math.Abs(0 - Dayn)
End If

'If the event day is today, compare the current
'time to the event time
``````

Ok, so If the DayDiff is 7. It means today is the same value of next eg Friday (5). We need to reference the time to see if it's in the past.

``````                    If DayDiff = 7 Then
If NextTime.TimeOfDay > Now.TimeOfDay Then
``````

If the Next Time is still in the future, let's set DayDiff to 0, as the next event will occur today.

``````                            DayDiff = 0
``````

Otherwise leave it at 7, the enext event will be next week `Else`

``````                            DayDiff = 7
End If
End If
``````

Let's build a new NextTime object, incrementing the number of days as required. Preserve Hour, Minute

``````                    'Increment number of days until next event
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
``````

The Monthly repeat requires a little work too.

``````                Case 3 'Monthly

'TODO:Add handler which makes sure if the selected month has more
'days than the next event month, the day is switched
'to the last day of the month eg 31st = 29th
``````

If the Now day is the same as next day eg 0-31

``````                    'If the event date is today, check the time
If NextTime.Day = CurrentTime.Day Then
``````

We need to check the time.

If the NextTime time is still in the future there's no need to change NextTime

``````                        If NextTime.TimeOfDay >= Now.TimeOfDay Then
'The next event is today
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
``````

If the time has passed we need to set NextTime month foward by one. Preserve Day, Hour, Minute

``````                        Else
'The next event is next month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.AddMonths(1).Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
End If
``````

If the NextTime day is in the past let's set the NextTime month value ahead by one. Preserve Day, Hour, Minute

``````                    ElseIf NextTime.Day < CurrentTime.Day Then
'The next event is next month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.AddMonths(1).Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
``````

The NextTime month value is in the future so let's leave NextTime it as it is

``````                    Else
'The next event is this month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
End If
``````

Yearly is just as simple as daily, we'll just set the next event to next year. Preserve Month, Day, Hour Minute

``````                Case 4 'Yearly

'Increment event date by 1 year
NextTime.Month, NextTime.Day, NextTime.Hour, NextTime.Minute, 0)

End Select
``````

We now set the elapsed time TimeSpan to calculate the time span between now and the new NextTime DateValue.

``````            'Re-evaluate the TimeSpan between now and the next occorance of this event
ElapsedTime = NextTime.Subtract(CurrentTime)

End If
``````

And return the results to the user.

``````        Dim TV As TimeValues = New TimeValues(Now + ElapsedTime, ElapsedTime)

Return TV

End Function
``````

That's it, all comments stripped away it's a pretty short and simple piece of code.

Note:I'm not much of a mathmatition. The method I used to calculate the DayDiff values may not be the simplest way to get the required result. It does work 100% but you may see a simple equation which does the same.

That's it then, I hope this gives you a wee insite to TimeSpan and DateTime

Reverend Jim commented: Nicely done. +12
``````Public Class EventManager

Public Enum RepeatValues
NoRepeat = 0
Day = 1 'Repeat Every Day
Week = 2 'Repeat each week on the selescted DayOfWeek
Month = 3 'Repeat each month on the selected day
Year = 4 'repeat each year on the selected date
End Enum

Public Class TimeValues
Public Sub New(NextDate As DateTime, Duration As TimeSpan)

_NextDate = NextDate
_Duration = Duration

End Sub

Private _NextDate As DateTime
Public ReadOnly Property NextDate As DateTime
Get
Return _NextDate
End Get
End Property

Private _Duration As TimeSpan
Public ReadOnly Property Duration As TimeSpan
Get
Return _Duration
End Get
End Property

End Class

Public Shared Function VeryLongDate(DateValue As DateTime, Optional IncludeTime As Boolean = True, Optional TimeDeliminator As String = " at ") As String

Dim _Day As String = DateTime.Parse(DateValue).DayOfWeek.ToString
Dim _Month As String = MonthName(DateValue.Month)
Dim _Date As String = SuffixDay(DateValue)

Dim _Time As String = Format(DateValue.Hour, "00") & ":" & Format(DateValue.Minute, "00")

If IncludeTime = True Then
Return String.Format("{0} {1} of {2} {3} {4} {5}", _Day, _Date, _Month, DateValue.Year, TimeDeliminator, _Time)
Else
Return String.Format("{0} {1} of {2} {3}", _Day, _Date, _Month, DateValue.Year)
End If

End Function

Public Shared Function SuffixDay(DateValue As DateTime) As String

Dim sDay As String = DateValue.Day.ToString()

Select Case Strings.Right(sDay, 1)
Case "1" : sDay += "st"
Case "2" : sDay += "nd"
Case "3" : sDay += "rd"
Case "4" To "9", "0" : sDay += "th"
End Select

Return sDay

End Function

Public Shared Function NextEvent(StartDateTime As DateTime, Repeat As RepeatValues) As TimeValues

'Get current DateTime
Dim CurrentTime As DateTime = Now
'Get DateTime of first event occurance
Dim NextTime As DateTime = StartDateTime
'Get the differance between start and next as a TimeSpan
Dim ElapsedTime As TimeSpan = NextTime.Subtract(CurrentTime)

Dim Days As Integer = 1
Dim Dayn As Integer = 1

'If the TimeSpan < 0 then the next event would be in the past so...
If ElapsedTime.Milliseconds < 0 Then

'Let's upate our next event so it's in the future based on the
'users chosen repeat value.
Select Case Repeat
Case 0 'Never

'This user doesn't want to repeat this event so let's
'return nothing for now.
Return Nothing

Case 1 'Daily

'Add one day on to the next event DateTime (Tomorrow)
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _

Case 2 'Weekly

'Add one week on to the next event DateTime (Next Week)
Dayn = NextTime.DayOfWeek
Days = CurrentTime.DayOfWeek

'Switch from 0 (Sunday) -> 6 (Saturday) to 1 (Monday) -> 7 (Sunday)
If Days = 0 Then Days = 7
If Dayn = 0 Then Dayn = 7

Dim DayDiff As Integer = 0

'Get the difference from today to the next event day
If Math.Abs(0 - Dayn) > Math.Abs(0 - Days) Then
DayDiff = Math.Abs((Math.Abs(0 - Days)) - Math.Abs(0 - Dayn))
Else
DayDiff = (7 - Math.Abs(0 - Days)) + Math.Abs(0 - Dayn)
End If

'If the event day is today, compare the current
'time to the event time
If DayDiff = 7 Then
If NextTime.TimeOfDay > Now.TimeOfDay Then
DayDiff = 0
Else
DayDiff = 7
End If
End If

'Increment number of days until next event
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _

Case 3 'Monthly

'TODO:Add handler which makes sure if the selected month has more
'days than the next event month, the day is switched
'to the last day of the month eg 31st = 29th

'If the event date is today, check the time
If NextTime.Day = CurrentTime.Day Then

'If the Event time is ahead of the current time
If NextTime.TimeOfDay >= Now.TimeOfDay Then
'The next event is today
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
Else
'The next event is next month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.AddMonths(1).Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
End If

'If todays date is after the event day
ElseIf NextTime.Day < CurrentTime.Day Then
'The next event is next month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.AddMonths(1).Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
Else
'The next event is this month
NextTime = New DateTime(CurrentTime.Year, CurrentTime.Month, _
NextTime.Day, NextTime.Hour, NextTime.Minute, 0)
End If

Case 4 'Yearly

'Increment event date by 1 year
NextTime.Month, NextTime.Day, NextTime.Hour, NextTime.Minute, 0)

End Select

'Re-evaluate the TimeSpan between now and the next occorance of this event
ElapsedTime = NextTime.Subtract(CurrentTime)

End If

Dim TV As TimeValues = New TimeValues(Now + ElapsedTime, ElapsedTime)

Return TV

End Function

End Class``````
Reverend Jim 4,202

I'd say this goes way beyond "code snippet" and could easily be a tutorial. Nicely done.

Thanks Reverend Jim.

Yeah, I got kinda carried away explaining things. Doh!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.