Link to home
Start Free TrialLog in
Avatar of Eduardo Fuerte
Eduardo FuerteFlag for Brazil

asked on

Could you point a MySQL Server query that returns the number of business days between 02 dates ?

Hi Experts

Could you point a MySQL Server query that returns the number of business days (day-of-week days - Assuming Saturday and Sunday are not business days) between 02 dates ?

Obs.
I found this query elsewhere (based on a strange algorithm):
SET @E = '20161214';
SET @S = '20161201';
SELECT (DATEDIFF(@E, @S) DIV 7) + MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1) as dias_uteis

Open in new window


That doesn't returns the correct number of days between 02 dates.
User generated image
Thanks in advance!
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

This is easy to get right in PHP.  There are examples you can adapt in these articles.  Please see Practical Application #9.

Procedural
https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html

Object-Oriented
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html

Here's an adaptation:
https://iconoun.com/demo/temp_eduardo.php
<?php // demo/temp_eduardo.php
/**
 * https://www.experts-exchange.com/questions/28989520/Could-you-point-a-MySQL-Server-query-that-returns-the-number-of-business-days-between-02-dates.html
 *
 * https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html
 */
error_reporting(E_ALL);


// A FUNCTION TO COUNT BUSINESS DAYS BETWEEN GIVEN DATES
function count_business_days($alpha="today", $omega="tomorrow")
{
    // CREATE YOUR ARRAY OF HOLIDAYS, OR LOAD IT FROM AN EXTERNAL TABLE
    $holidays = array();
    $november = strtotime(date('Y') . '-11-0');
    $january  = strtotime(date('Y') . '-01-0');
    $nextyear = mktime(0,0,0, 1, 1, date('Y') + 1);
    $holidays['Dr_M_L_King']  = date('r', strtotime('Third Monday', $january));
    $holidays['Independence'] = date('r', strtotime(date('Y') . '-07-04'));
    $holidays['Thanksgiving'] = date('r', strtotime('Fourth Thursday', $november));
    $holidays['Christmas']    = date('r', strtotime(date('Y') . '-12-25'));
    $holidays['NewYear']      = date('r', $nextyear);

    // ACTIVATE THIS TO SEE THE HOLIDAYS
    // print_r($holidays);

    // INTERPRET THE INPUTS INTO TIMESTAMPS
    if (!$alpha = strtotime($alpha)) return FALSE;
    if (!$omega = strtotime($omega)) return FALSE;

    // MAKE AN ARRAY OF FUTURE TIMESTAMPS AND RFC2822 DATES
    $arr = range($alpha, $omega, 86400);
    $arr = array_flip($arr);
    foreach ($arr as $timestamp_key => $null)
    {
        // ASSIGN RFC2822 DATE STRINGS TO EACH TIMESTAMP
        $arr[$timestamp_key] = date('r', $timestamp_key);

        // REMOVE THE DAY FROM THE ARRAY IF IT IS A HOLIDAY OR WEEKEND DAY
        if (in_array($arr[$timestamp_key], $holidays)) $arr[$timestamp_key] = 'S';
        if (substr($arr[$timestamp_key],0,1) == 'S') unset($arr[$timestamp_key]);
    }

    // ACTIVATE THIS TO SEE THE ARRAY OF BUSINESS DAYS
    // print_r($arr);

    // RETURN THE COUNT OF BUSINESS DAYS
    return count($arr);
}


// DEMONSTRATE THE FUNCTION
$a = 'Nov 15, 2016';
$z = 'Dec 27, 2016';
$n = count_business_days($a, $z);
echo PHP_EOL . "BETWEEN $a AND $z THERE ARE $n BUSINESS DAYS";

Open in new window

You probably want to load the holidays from an external data source that can be checked against official calendars.  The reason goes to the rules for "observed" holidays.  This year, Christmas is on Sunday, so Dec 25 was already a holiday.  The observed holiday for Christmas is Dec 26 in 2016.
This is best handled in the database, IMO, and not PHP.  For one, the implementation is much simpler, and you don't have to worry about explicit string to date conversions.

  SELECT (DATEDIFF('2016-12-31', '2016-12-01')) -
        ((WEEK('2016-12-31') - WEEK('2016-12-01')) * 2) -
        (case when weekday('2016-12-31') = 6 then 1 else 0 end) -
        (case when weekday('2016-12-01') = 5 then 1 else 0 end);

Open in new window


If you had a table with holidays, you can count the number of holidays between the start and end date, and subtract those out, too.

P.S.  Weekday numbers, at least in MS SQL, can differ depending on configuration.  If that's also the case in MySQL, you'll want to verify that 5 and 6 are Saturday and Sunday.
Avatar of Eduardo Fuerte

ASKER

Hi @Megan

Just one doubt. Didn't the weekday must to be tested to 5 or 6?

  SELECT (DATEDIFF('2016-12-31', '2016-12-01')) -
        ((WEEK('2016-12-31') - WEEK('2016-12-01')) * 2) -
        (case when (weekday('2016-12-31') = 6 OR  weekday('2016-12-31'))= 5  then 1 else 0 end) -
        (case when (weekday('2016-12-01') = 6  OR  weekday('2016-12-31'))= 5 then 1 else 0 end)  AS dias_uteis;

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of zephyr_hex (Megan)
zephyr_hex (Megan)
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi @zephyr_hex

After some tests your last code looks OK... So I'm planning to put it under production.

Any other final note?
Nope.  I just suggest you test edge cases.  I did preliminary testing with May, October and December, which are all edge scenarios.  I didn't look at leap years, month cross overs (i.e.  from one month to the next), etc.
Thank you !

It's a very good starting point, some other changes if needed will be implemented later, after more apurated tests.