Working days

05/31/2008

I'm all for reusable code. Whenever I'm coding, I'm constantly wondering if there's a way that I can further abstract what I've done so I can use it somewhere else. Because of this, I came across a problem yesterday that got the better of me a little.

I'm working on a project at the moment that requires me to have an awareness of "working" days. Specifically, when a product is sold, the customer has three working days to cancel their order. This would be all well and good if there was 365 working days in the year, and all companies were the same. I started out with this assumption, but was quickly confounded.

Before I explain how I solved (well, only partially - it would require a calendar app to fully solve it to my satisfaction!) it, I'll explain a little about how some classes come to be in my workflow.<!--more-->

When working in PHP, I have around 5 or 6 classes that I take everywhere with me. One for generating a class that represents every database table, one for DOM generation, one for database access, one for laying out application-specific database entity grouping (how hard can one try to avoid saying the word "model"?!) and one that acts as a sort of "other" class and includes functionality that doesn't fit elsewhere. This is a single class (called "core", if you're interested) that contains a bunch of un-interrelated static functions. It acts as a breeding ground for functionality that gets bigger and bigger (either in the file or my head) before I give up and move it to its own domain. An example of this was back when I started this way of working and my database access functionality was in here. In a perfect world, only my __autoload() (best function ever, by the way) function would be here but, as I said - in a perfect world!

So, I have my core::nWorkingDaysPassed function, and all is well. The first argument is the date you're counting from, the second is the date you're counting to (defaults to now), the third is the number of days you want to count (defaults to 3 because that's what I'm working on now) and the 4th is an array of days of the week that are in your working week, defaulting to Monday through Saturday, again because of what I'm working on now.

I've already spotted a problem with this function - it has too many arguments. I don't like passing a lot of arguments to a function, and I think this is probably to do with my grounding in PHP - the argument orders in PHP's core functionality is so varying that it's incredibly difficult to remember what they are, even for functions you use every day! Luckily the documentation is good enough to counteract this. By the time I've finished this thought, I've spotted another problem - it doesn't take public holidays into account. To take public holidays into account, it's going to need to be an object of its own. I can't get that much functionality into one function. Because of time constraints, and authorisation from the client, holidays are no-longer a concern (as they're open, thankfully!).

On to solving my problem. It'll be a lot easier to write this in pseudo-code. I know this because I just deleted the verbose version!

normalweek = ('mo','tu','we','th','fr','sa','su')
workingweek = normalweek
for each workingweek as day:
 if day is not in argumentworkingweek:
 remove day from workingweek
weeksspanned = argumentdays / 7 rounded up to nearest 1
allweeks = merge weeksspanned*normalweeks
i = first instance of argumentfrom weekday in allweeks
j = i + argumentdays
while i < j:
 argumentfrom = argumentfrom + 1 day
 if allweeks[i] is in workingweek: # normal working day
 i++
 else: # shouldn't count towards our total
 append a day to allweeks # so we don't run out!
 i++
 j++
if argumentfrom >= argumentto: # that amount of days has passed
 return True
else:
 return False
I realise that commenting pseudo-code largely invalidates the need for pseudo-code but it started to look like Python and I didn't want to spoil it!

Looking back at it now, I don't know why it caused such a headache but I'm just one of those people who has problems with the difference between dates. I hope this helps someone, though I doubt it will!