BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Algorithms and Data Structures
.NET 3.5+

An Extensible Appointment Scheduling Library

Scheduling software can be used to plan future appointments, including those appointments that repeat on a regular basis. This article describes a library with four types of scheduling rule and the potential for additional rules to be incorporated.

CalendarGenerator Class

Now that we have a scheduling class and all of the supporting classes for generating appointments we can add the CalendarGenerator class. This is responsible for creating a list of appointments for a period using the rules from one or more schedules.

Start by adding the class declaration:

public class CalendarGenerator
{
}

The generator has a single public method, GenerateCalendar, that is called to create an appointment list. It accepts two parameters. The first specifies the period for which we want to calculate appointment dates. The second is a sequence of schedules, declared as IEnumerable<Sequence> so that many different collection types can be used. The method's overall function is to check every date in the period against every schedule to determine when appointments occur.

Internally, the GenerateCalendar method creates a new, empty list of appointments. It then processes all of the dates in the supplied period with a simple for loop. During each iteration the private AddAppointmentsForDate method is called. We'll see this method in a moment. When the loop completes, the appointments list will be populated. This is sorted by date and time using the LINQ OrderBy standard query operator and then returned.

public IEnumerable<Appointment> GenerateCalendar(
    Period period, IEnumerable<Schedule> schedules)
{
    var appointments = new List<Appointment>();
    for (DateTime checkDate = period.Start;
         checkDate <= period.End;
         checkDate = checkDate.AddDays(1))
    {
        AddAppointmentsForDate(checkDate, schedules, appointments);
    }
    return appointments.OrderBy(a => a.Time);
}

AddAppointmentsForDate accepts a date, the set of schedules and the appointments list. It processes each of the schedules within a foreach loop, calling the schedule's OccursOnDate method to determine whether an appointment for that schedule is required on the supplied date. If one is, it is instantiated using the GenerateAppointment method and added to the appointments list.

private void AddAppointmentsForDate(
    DateTime checkDate, IEnumerable<Schedule> schedules, List<Appointment> appointments)
{
    foreach (Schedule schedule in schedules)
    {
        if (schedule.OccursOnDate(checkDate))
        {
            appointments.Add(GenerateAppointment(checkDate, schedule));
        }
    }
}

GenerateAppointment is a simple method. It returns a new Appointment object with the name from the supplied schedule. The time for the appointment is created by combining the date from the checkDate argument and the time from the schedule.

private Appointment GenerateAppointment(DateTime checkDate, Schedule schedule)
{
    return new Appointment
    {
        Name = schedule.Name,
        Time = checkDate.Add(schedule.TimeOfDay)
    };
}

First Test

We now have enough classes to perform an initial test. All of the scheduling "engine" is in place and we have the ability to create one-off appointments, so let's try it out. To begin, add a new console application to your solution, add a reference to the scheduler library from the new project and set the console application as the startup project. To simplify access to the scheduler classes, add the following using directive to the Program class:

using Scheduler;

We'll add code to the Main method of the console application to perform the test. First we need some schedules. The code below creates two schedules and adds them to a list:

var single1 = new SingleSchedule
{
    Name = "Meet Bob for Pint",
    TimeOfDay = new TimeSpan(19, 30, 0),
    Date = new DateTime(2012, 5, 8)
};

var single2 = new SingleSchedule
{
    Name = "Confirm Meeting",
    TimeOfDay = new TimeSpan(9, 30, 0),
    Date = new DateTime(2012, 5, 12)
};

var schedules = new List<Schedule> { single1, single2 };

We can now create a new CalendarGenerator and use it to generate an appointment list for a period. The code below generates and outputs appointments for May and June of 2012. This includes the dates of both of the schedules created above.

var generator = new CalendarGenerator();
var period = new Period(new DateTime(2012, 5, 1), new DateTime(2012, 6, 30));
var appointments = generator.GenerateCalendar(period, schedules);

foreach (var appointment in appointments)
{
    Console.WriteLine(
        "{0} | {1}", appointment.Time.ToString("yyyy-MM-dd HH:mm"), appointment.Name);
}

If you run the code you'll see the following output. Try changing the dates in the period variable to exclude one or both schedules to see the differing results.

2012-05-08 19:30 | Meet Bob for Pint
2012-05-12 09:30 | Confirm Meeting
29 April 2012