This the first of many posts about the implementation of PayDay, the software that I’m writing to help me with my personal finances. I’ve been working on the project for a couple of months and one of the first things that I worked on was frequencies for recurring income and expenses.
One of the fundamental concepts in PayDay is the idea of a recurring income or expense. I need to represent, for example, a monthly income, a weekly train ticket or a fortnightly gym membership. These bills or income occur every so often, but the every-so-often is different for each one.
Not only that but a recurring item (from now on whenever I say ‘item’ I mean income or expense) has its own start date. Two monthly items, while they occur as often as each other, might start on different days of the month. My car loan comes out on the 16th but I’m paid on the 14th, for example. Also frequencies aren’t always as simple as daily, weekly or monthly because you might want to represent things like ‘every second Tuesday’ or ‘the 14th of the month unless it’s on a weekend then the last Friday before the 14th’.
To implement the functionality of frequencies I considered creating an enum (enumerated type) with all possible types of frequencies. The problem with this approach is that there are an infinite number of possible frequencies that you can have for a recurring event. Most things fit into the common frequencies like weekly and monthly, but there are other events whose occurrences have to be specifically determined. Take, for example, the schedule for being paid monthly. Most employers will pay on the 14th or 15th of the month except if that date occurs on a weekend or a Monday, in which case the employee will be paid on the Friday so that they have money for that weekend.
Now I can’t think up all possible types of frequencies, yet I want to be able to support new types of frequencies without too much modification. I could, theoretically, make a frequency engine that you supply parameters and rules to define your frequency but that’s too much work for people who aren’t developers. Imagine PayDay asking you to “define the parameters of the frequency of this expense” … yeah I wouldn’t use software that asked me to do that!
I need to implement a few common ones and get on with the rest of the application. I do, though, want to be able to introduce new frequencies later on but also I’d like anyone that writes software that uses the PayDay library to be able to invent their own frequencies without having to touch the PayDay library’s source code.
To accommodate this I created an interface that any class that wants to offer frequency information needs to implement. I called it ITimeFrequency and it had two methods to implement:
/// Given a date when the event happens,
/// return the date of the next occurrence
DateTime NextOccurence(DateTime occurrence);
/// Given a date when the event happens,
/// return the date of the previous occurrence
DateTime PreviousOccurence(DateTime occurrence);
I really wanted to keep the interface lean. There originally was another method in the interface that returns all the dates of occurrences between a start and end date, but all concrete classes implementing the interface would have the same code for that method I decided to put that method into a static class called TimeFrequency:
public static List<DateTime> OccurrencesInDateRange(
So whenever you call TimeFrequency.OccurencesInDateRange(), it’s calling NextOccurence of the ITimeFrequency passed to it to figure out the dates of the occurrencies
I created concrete classes that implement the ITimeFrequency interface: daily, weekly, monthly, yearly and fortnightly.
The general idea with all this is that if you have an event that occurs regularly, you can figure out when that event occurs from when it started onwards. So if I have a weekly event that started on the 26th of July, 2010, I know that the next occurrence will be 2nd of August, 2010 and the next occurrence is the 9th of August and so on. The NextOccurrence() method defined in the ITimeFrequency interface is what calculates what the date of the next occurrence will be. Though the application will not try to calculate occurrences that occur too far into the future, it will calculate all occurrences that occur in the date range that the user is viewing.
After the ITimeFrequency interface and it’s concrete classes was written I hit a snag with the monthly frequency. The issue was that calendar months have different numbers of days, 28, 29, 30 and 31. If something starts on the 31st the next month, unless it’s August, will have less days in it and therefore the next occurrence needs to be the last day of the month; except when the subsequent occurrence is calculated it will not be on the 31st, it will be on either the 30th, 29th or 28th, depending on the month.
Here’s an example: I have a monthly event that starts on the 31st of August. The second occurrence will be the 30th of September. This is because the monthly frequency knows that there is no 31st of September so it chooses the 30th, which is September’s last day. The third occurrence is then calculated but because NextOccurrence() only goes off the date of the previous occurrence, 30th of September in this case, it doesn’t know that the event really should occur on the 31st of October and it erroneously chooses the 30th of October.
What ends up happening is the day the occurrences are meant to happen on eventually go to the 28th. Once the occurrences hit February on a non-leap year that’s it.
So the monthly frequency really needed to have an occurrence day. The day of the month when an occurrence should happen if the month has that many days. If it doesn’t then the last day of the month is selected. The occurrence day is supplied in the constructor of the monthly frequency object.
Giving the month frequency an occurrence day introduced another issue. If you create a monthly frequency with an occurrence day, then you call it’s NextOccurrence() method with a date that has a day that is different to the monthly frequency’s occurrence day, the method not return a date that is one month after, it will return the next time the occurrence day occurs.
For example: I create a monthly frequency with an occurrence day of the 12th. I then call its NextOccurrence() method with a parameter of the 15th of July, the answer will not be the 15th of August, it will be the 12th of August. Now this is exactly how NextOccurrence() should work. It has given you the correct answer; the next occurrence of the 12th after the 15th of July is the 12th of August, but if I have a recurring item that starts on the 15th yet I’ve declared that it occurs on the 12th, I need a way to tell the user that the dates and frequency they’ve chosen are inconsistent. To do this the code that calls NextOccurrence() needs to call another method that checks to see if the start date is counted as an occurrence.
/// Is the given date a valid occurrence
bool IsValidOccurence(DateTime occurrence)
Now it’s possible for the recurring item code to check to see if an item’s start date should be counted as an occurrence.
I’ve really tried to make the frequency code open for all kinds of frequencies. The only restriction I have is that the smallest unit of time frequencies can work with is a day. For this application I didn’t want to have sub-day frequencies like hourly and every-minute.
My next post about PayDay I’ll talk about how I’ve dealt with representing money.