Title: Find Friday the Thirteenths in C#
The following code shows how this example lists the Friday the Thirteenths between selected start and end dates.
// List Friday the 13ths between the start and end dates.
private void btnGo_Click(object sender, EventArgs e)
{
lstResults.Items.Clear();
// Get the start and end date components.
DateTime start_date = dtpStart.Value.Date;
DateTime end_date = dtpEnd.Value.Date;
int start_year = start_date.Year;
int end_year = end_date.Year;
// Loop over the selected years.
for (int year = start_year; year <= end_year; year++)
{
// Loop over the months in the year.
for (int month = 1; month <= 12; month++)
{
// See if this month's 13th is a Friday.
DateTime test_date = new DateTime(year, month, 13);
// If we haven't reached the start date, skip this one.
if (test_date < start_date) continue;
// If we've passed the end date, stop looping.
if (test_date > end_date) break;
// See if this is a Friday.
if (test_date.DayOfWeek == DayOfWeek.Friday)
lstResults.Items.Add(test_date.ToShortDateString());
}
}
}
The code first clears the result ListBox. It then gets the start and end dates and uses them to get the start and end year numbers.
Next the program loops between the start and end years, inclusive. For each year, the code loops over the month numbers 1 through 12. For each month, the code creates a DateTime object representing the 13th of the month in the given year.
If the date is before the selected start date, the code uses a continue statement to skip the rest of the month loop and continue with the next month. This could happen up to 11 times (if the start month is December).
If the date is after the end date, the code uses a break statement to break out of the month loop. That happens at most once during the final year, so the year loop also ends at that point. (This won't happen at all if the end month is December.)
Finally, if the code is still running in the loop, it checks the test date's DayOfWeek property and adds it to the result ListBox if it is a Friday.
This program is fairly simple (and perhaps not hugely useful for business applications), but it demonstrates two important techniques.
First, when the program gets the start and end dates, it explicitly only takes the date portion of the selected DateTime values. If you compare DateTime variables and you're not interested in the time part, you still need to be aware of that part. If the code uses a value selected by a DateTimePicker or some other control, you may need to remove the time portion to get the correct results.
In this example, suppose the time selected by dtpStart is Friday June 13, 2014 at 13:30:00. When the code loops through the months, the code considers the date June 13, 2014 but when you initialize the DateTime object, the time is omitted so it defaults to 00:00:00. The test_date > end_date test thinks the test date is before the start date of 13:30:00 so it skips that date.
The program fixes this problem by only using the date portion of the start and end dates. (Note that this problem might lead you to a related issue: thorough testing. In this example, you could find this problem by testing the program with start and end dates that are Friday the Thirteenths.)
The second important technique that this program demonstrates is looping over a range of dates of a month. In the first and last years this code might generate dates that are outside of the desired range and either skip those months or end the month loop without finishing it. Those tests waste a little time. You could avoid them by handling the start year separately, looping over the middle years, and then handling the end year separately, but that would make the code a lot more complicated. This version is much simpler and the tests shouldn't be a big performance hit. If you're checking a few thousand or fewer months, you shouldn't notice any performance penalty.
Download the example to experiment with it and to see additional details.
|