views:

495

answers:

2

I need to calculate a difference between a starting date/time and an ending date/time. But, I only want to do this for the 5-day work week (exclude Sat/Sun as days). What is the best way to do this? My thought is that from the date, I'll have to get the day of the week and if it is a weekday, then I add to the accumulator. If it's not, then I don't add anything.

I'm sure someone has done this before, but I couldn't seem to find anything searching. Any links or other help would be very useful.

Many thanks,

Bruce

+3  A: 

DAYOFWEEK returns 1 for Sunday and 7 for Saturday. I'm not sure how your schema is set up, but this will perform a TIMEDIFF of two dates that are on a Monday - Friday work week.

select TIMEDIFF(date1,date2) from table 
where DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)

MySQL DATE/TIME functions

EDIT: From Bruce's comment about holidays. If you have a table full of holiday dates, something like this would work to exclude processing those days:

select TIMEDIFF(date1,date2) from table 
where date1 not in (select holiday from holiday_table) and
date2 not in (select holiday from holiday_table) and
DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)
Trevor
Wow, Thanks for the quick answer, Trevor! Looks like that will do the trick!
Bruce Garlock
No problem. If this works for you, can you please accept this answer? Thanks.
Trevor
Yes, I'll accept the answer - will be testing it tonight or tomorrow morning. I'm also considering hooking into a separate 'calendar' table. Since we already use a Web based calendar, and put in all the holiday's, this particular application could tap into that as well, since the holiday's are not considered 'work days' either.How would you go about something like that? A sub-select?I may go with a MySQL stored function. I just want to make sure that it is portable for the future. I see that MySQL's stored functions are mostly a standard, in case we had to move to another db.
Bruce Garlock
I've been coding in MySQL 3.23.58, and 4.1 with PHP 4.0.6 (All internal apps that never see the net, and have mod_security in front of them) All of these "new" MySQL features (to us anyway) are nice, and I want to make sure we design stuff that will work with little changes needed in the future.
Bruce Garlock
Thanks again, Trevor.
Bruce Garlock
I accepted this answer, and the one below also deserves credit, but I promised you first :-)
Bruce Garlock
+1  A: 

NETWORKDAYS() "Returns the number of whole working days between start_date and end_date. Working days exclude weekends and any dates identified in holidays. Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days worked during a specific term." according to the Excel 2007 help file.

The "between" description is a bit inaccurate because it includes the start and end dates, i.e. networkdays(21-01-2010. 22-01-2010) = 2. It also takes no account of times.

Here's a function in PHP that will give the same results. It doesn't work properly if the end date is less than the start date, nor does do anything about holidays (see below the function).

function networkdays($startdate, $enddate)
  {
     $start_array = getdate(strtotime($startdate));
     $end_array = getdate(strtotime($enddate));

     // Make appropriate Sundays
     $start_sunday = mktime(0, 0, 0, $start_array[mon], $start_array[mday]+(7-$start_array[wday]),$start_array[year]);
     $end_sunday = mktime(0, 0, 0, $end_array[mon], $end_array[mday]- $end_array[wday],$end_array[year]);

     // Calculate days in the whole weeks
     $week_diff = $end_sunday - $start_sunday;
     $number_of_weeks = round($week_diff /604800); // 60 seconds * 60 minutes * 24 hours * 7 days = 1 week in seconds
     $days_in_whole_weeks = $number_of_weeks * 5;

     //Calculate extra days at start and end
     //[wday] is 0 (Sunday) to 7 (Saturday)
     $days_at_start = 6 - $start_array[wday];
     $days_at_end = $end_array[wday];

     $total_days = $days_in_whole_weeks + $days_at_start + $days_at_end;

     return $total_days;
}

To take holidays into account, you'd have to work out the number of days using this function, then use a query like

Select count (holiday_date) from holidays
where holiday_date between start_date and end_date
and DAYOFWEEK(holiday_date) not in (1,7)

Be careful that there isn't a problem with the end_date being treated as 00:00 (i.e. first thing in the morning) - you may have to condition it to be 23:59:59 so that it works properly. It all depends on how your holidays are stored.

To return the holidays in the same time period and subtract that from the number you first thought of.

Kevin Jones
And it only works with Saturday and Sunday as the days off
Kevin Jones
Kevin - this is another great way of solving this issue that is PHP based, so I really appreciate it. I wish I could have marked this as an acceptable answer, which it is, but I had already promised Trevor for the MySQL based answer.Too bad I couldn't accept them both, because technically they both solve my problem.Many thanks,Bruce
Bruce Garlock
Thanks Bruce, but I'm not sure that the first answer will suit your needs. What it does is pick days that aren't weekends, and counts all the days between them. NETWORKDAYS() counts the number of non-weekend days for 2 dates, which isn't the same.For instance, if you wanted NETWORKDAYS(6-feb-2010, 13-feb-2010) (which should be 5), the first answer would return an empty result set because both dates are Saturdays.And it seems to run out of steam at 838:59:59, or 35 days.Not having a go at your solution Trevor, just don't think it was what was asked for.
Kevin Jones
OK, I see. One more question: If I needed to include the hours, and display the result in hours (e.g. Start time is 8a Friday morning, and End time is Tuesday at 8a, the result should be 48.0 hrs) I need to display the result in tenths of an hour. So, if the start is Friday 8a, and end is Friday at 5:30p, the result would be 9.5 hrs.How would I do that?
Bruce Garlock
The days version of the code I've given you you take the time as being 0 hours, 0 minutes an 0 seconds. You would presumably subtract the start time from 24 hours and add the time portion of the end time and add them to the days count. This might be problematic if the start and end are on the same day. An extra twist would be having to worry about daylight savings. You'd probably need to do use something like timediff() between midnight and the start/end times to make sure it was picked up.
Kevin Jones