TDD and refactoring to patterns in C#: how to write a cron parser - Part 3

In this article, we will implement a simplified code for the cron parser to make the tests pass and, thereby, have a first version of our application.

It is indeed time to delve deeper into the C# code for the parser. The code we will write may not be the most efficient or the most pleasant to read, but the objective here is to have a working application. Refactoring will come later in the upcoming article.

Code in Visual Studio

We modify the GiveNextExecutionFrom method with the following code.

 1public DateTime GiveNextExecutionFrom(DateTime reference)
 2{
 3    var nextOccurrences = GetDates(reference);
 4    var fields = Pattern.Split(' ');
 5
 6    if (fields[3] != "*") // month
 7    {
 8        nextOccurrences = nextOccurrences.Where(t => t.Month == Convert.ToInt32(fields[3])).ToList();
 9    }
10
11    if (fields[2] != "*") // dayOfMonth
12    {
13        nextOccurrences = nextOccurrences.Where(t => t.Day == Convert.ToInt32(fields[2])).ToList();
14    }
15
16    if (fields[4] != "*") // dayOfWeek
17    {
18        nextOccurrences = nextOccurrences.Where(t => (int)t.DayOfWeek == Convert.ToInt32(fields[4])).ToList();
19    }
20
21    if (fields[1] != "*") // hour
22    {
23        nextOccurrences = nextOccurrences.Select(t => new DateTime(t.Year, t.Month, t.Day, Convert.ToInt32(fields[1]), 0, 0)).ToList();
24    }
25
26    if (fields[0] != "*") // minute
27    {
28        nextOccurrences = nextOccurrences.Select(t => new DateTime(t.Year, t.Month, t.Day, t.Hour, Convert.ToInt32(fields[0]), 0)).ToList();
29    }
30
31    return nextOccurrences.Where(x => x >= reference).Min();
32}
33
34private List<DateTime> GetDates(DateTime reference)
35{
36    var endDate = new DateTime(2099, 12, 31);
37    return Enumerable.Range(0, 1 + endDate.Subtract(reference).Days).Select(offset => reference.AddDays(offset)).ToList();
38}
Disclaimer

This code is probably not the most sophisticated solution for our current problem: we first generate all possible dates until 2099 and then progressively filter them based on the provided patterns. In the end, we return the earliest date found.

Running the tests

In accordance with the TDD philosophy, we now run tests to check that they pass.

All tests have passed.

Now that we have successfully written working code and our tests have passed, we can move on to the second phase: refactoring the code to make it more scalable, readable, and extensible. This is the subject of the next article (here).