Question

I need to calculate the difference (in days) between two dates in MySQL excluding weekends (Saturday and Sunday). That is, the difference in days minus the number of Saturday and Sunday in between.

At the moment, I simply count the days using:

SELECT DATEDIFF('2012-03-18', '2012-03-01')

This return 17, but I want to exclude weekends, so I want 12 (because the 3rd and 4th, 10th and 11th and 17th are weekends days).

I do not know where to start. I know about the WEEKDAY() function and all related ones, but I do not know how to use them in this context.

Was it helpful?

Solution

Illustration:

mtwtfSSmtwtfSS
  123456712345   one week plus 5 days, you can remove whole weeks safely
  12345-------   you can analyze partial week's days at start date
  -------12345   or at ( end date - partial days )

Pseudocode:

@S          = start date
@E          = end date, not inclusive
@full_weeks = floor( ( @E-@S ) / 7)
@days       = (@E-@S) - @full_weeks*7   OR (@E-@S) % 7

SELECT
  @full_weeks*5 -- not saturday+sunday
 +IF( @days >= 1 AND weekday( S+0 )<=4, 1, 0 )
 +IF( @days >= 2 AND weekday( S+1 )<=4, 1, 0 )
 +IF( @days >= 3 AND weekday( S+2 )<=4, 1, 0 )
 +IF( @days >= 4 AND weekday( S+3 )<=4, 1, 0 )
 +IF( @days >= 5 AND weekday( S+4 )<=4, 1, 0 )
 +IF( @days >= 6 AND weekday( S+5 )<=4, 1, 0 )
 -- days always less than 7 days

OTHER TIPS

Simply try it using a simple function :

CREATE FUNCTION TOTAL_WEEKDAYS(date1 DATE, date2 DATE)
RETURNS INT
RETURN ABS(DATEDIFF(date2, date1)) + 1
     - ABS(DATEDIFF(ADDDATE(date2, INTERVAL 1 - DAYOFWEEK(date2) DAY),
                    ADDDATE(date1, INTERVAL 1 - DAYOFWEEK(date1) DAY))) / 7 * 2
     - (DAYOFWEEK(IF(date1 < date2, date1, date2)) = 1)
     - (DAYOFWEEK(IF(date1 > date2, date1, date2)) = 7);

Test :

SELECT TOTAL_WEEKDAYS('2013-08-03', '2013-08-21') weekdays1,
       TOTAL_WEEKDAYS('2013-08-21', '2013-08-03') weekdays2;

Result :

| WEEKDAYS1 | WEEKDAYS2 |
-------------------------
|        13 |        13 |
Below function will give you the Weekdays, Weekends, Date difference with proper results:

You can call the below function like,
select getWorkingday('2014-04-01','2014-05-05','day_diffs');
select getWorkingday('2014-04-01','2014-05-05','work_days');
select getWorkingday('2014-04-01','2014-05-05','weekend_days');




    DROP FUNCTION IF EXISTS PREPROCESSOR.getWorkingday;
    CREATE FUNCTION PREPROCESSOR.`getWorkingday`(d1 datetime,d2 datetime, retType varchar(20)) RETURNS varchar(255) CHARSET utf8
    BEGIN
     DECLARE dow1, dow2,daydiff,workdays, weekenddays, retdays,hourdiff INT;
        declare newstrt_dt datetime;
       SELECT dd.iDiff, dd.iDiff - dd.iWeekEndDays AS iWorkDays, dd.iWeekEndDays into daydiff, workdays, weekenddays
      FROM (
       SELECT
         dd.iDiff,
         ((dd.iWeeks * 2) + 
          IF(dd.iSatDiff >= 0 AND dd.iSatDiff < dd.iDays, 1, 0) + 
          IF (dd.iSunDiff >= 0 AND dd.iSunDiff < dd.iDays, 1, 0)) AS iWeekEndDays
           FROM (
          SELECT  dd.iDiff, FLOOR(dd.iDiff / 7) AS iWeeks, dd.iDiff % 7 iDays, 5 - dd.iStartDay AS iSatDiff,  6 - dd.iStartDay AS iSunDiff
         FROM (
          SELECT
            1 + DATEDIFF(d2, d1) AS iDiff,
            WEEKDAY(d1) AS iStartDay
          ) AS dd
        ) AS dd
      ) AS dd ;
      if(retType = 'day_diffs') then
      set retdays = daydiff; 
     elseif(retType = 'work_days') then
      set retdays = workdays; 
     elseif(retType = 'weekend_days') then  
      set retdays = weekenddays; 
     end if; 
        RETURN retdays; 
        END;


Thank You.
Vinod Cyriac.
Bangalore

IT my helpful to you

The bellow logic only show the how many days like

sun   mon

1      2 .....................

DELIMITER $$
DROP FUNCTION IF EXISTS `xx`.`get_weekday` $$
CREATE FUNCTION `xx`.`get_weekday` (first_date date, last_date date, curr_week_day int) RETURNS INT
BEGIN
DECLARE days_tot int;
DECLARE whole_weeks int;
DECLARE first_day int;
DECLARE last_day int;
SET whole_weeks = FLOOR(DATEDIFF(last_date,first_date)/7) ;
SET first_day = WEEKDAY(first_date) ;
SET last_day = WEEKDAY(last_date)  ;
IF curr_week_day  BETWEEN first_day AND  last_day
           AND  last_day > first_day
           OR ( curr_week_day BETWEEN last_day AND first_day
           AND  last_day <  first_day  )
THEN SET days_tot = whole_weeks + 1;
ELSE SET days_tot = whole_weeks ;
END IF;
RETURN  days_tot;
END $$
DELIMITER ;

    SELECT
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 0) as mo,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 1) as tu,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 2) as we,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 3) as th,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 4) as fr,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 5) as sa,
      `xx`.`get_weekday` ('2009-01-01', '2009-07-20', 6) as su;

Table based query

ip:

Weekday count
2        10
3         5


SELECT WEEKDAY( `req_date_time` ) AS weekday, COUNT( id ) AS id
FROM `ddd`
WHERE (
`req_date_time` >= '2014-12-01'
AND `req_date_time` <= '2014-12-31'
)
AND WEEKDAY( `req_date_time` ) != '1'
GROUP BY WEEKDAY( `req_date_time` ) 

You can use a dates table:

tbl_dates

dates table

dow is 'day of week' in my table. Then your query looks like this:

SELECT Count(theDate) AS numWeekDays
FROM tbl_dates
WHERE theDate >[startDate] And theDate <=[endDate] AND dow <> 1 AND dow <> 7;

In this case, 1 and 7 are Sunday, Saturday, respectively (which is the default) and, of course, you can nest that into another query if you need to calculate this for many startDate(s) and endDate(s).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top