We help IT Professionals succeed at work.

Number of working days between two dates

Lee Savidge
Lee Savidge asked
on
Medium Priority
1,773 Views
Last Modified: 2012-05-06
Hi,

Before we go any further, no this is not homework. I have a requirement to create a function or functions, purely in javascript, that can return the number of days between 2 dates excluding weekends. Holidays etc., do not matter. I have found a page that claims to do it:

http://javascript.about.com/b/2009/01/26/day-difference-and-business-day-difference.htm

This seems to work but I get some odd results in testing. Here is what I have so far. Please feel free to let me know where I am going wrong or if you have a better solution, please advise. Please note, I am creating 2 date strings in the form dd/mm/yyyy. These are generated from the 2 input strings sContractSigned and sClosed. Now using the 2 example dates of the 14th and 19th Feb I would expect to get an answer of 4 days as the 14th is a Saturday, it should discount Sunday and only count Monday to Thursday but it gives me 3 as an answer. If I change the 14th to the 15th the answer sholdn't change but it does. It returns 4 instead. Everything is UK formatted.

Any help I would really appreciate.

Thanks,

Lee

<html>
<body>
 
<script type="text/javascript">
Date.prototype.dayDiff = function(d2)
{
  var d = Math.abs(this - d2);
  return Math.floor(d / (24 * 60 * 60 * 1000));
}
 
Date.prototype.busDayDiff = function(d2)
{
  var d = this.dayDiff(d2);
  var t= d%7;
  
  if (this < d2) {w1 = this.getDay(); w2 = d2.getDay();}
  else {w2 = this.getDay(); w1 = d2.getDay();}
 
  if (w1 > w2) {t -= 2;}
  if (w1 == 0 && w2 == 6) {t--;}
  return Math.abs((Math.floor(d / 7) * 5) + t);
}
 
    var sContractSigned = "14/02/2009 00:00:00"
    var arrContractSignedDateTime = sContractSigned.split(" ");
    var arrContractSignedDate = arrContractSignedDateTime[0].split("/");
    var dtContractSigned = new Date(arrContractSignedDate[2], arrContractSignedDate[1], arrContractSignedDate[0]);
    var sClosed = "19/02/2009 00:00:00";
    var arrClosedDateTime = sClosed.split(" ");
    var arrClosedDate = arrClosedDateTime[0].split("/");
    var dtClosed = new Date(arrClosedDate[2], arrClosedDate[1], arrClosedDate[0]);
    var iNumWorkingDays = dtClosed.busDayDiff(dtContractSigned);
    
    document.write("Contract Signed: " + sContractSigned);
    document.write("<br>Closed: " + sClosed);
    document.write("<br>Days Diff: " + iNumWorkingDays);
    document.write("<br>arrContractSignedDate: " + arrContractSignedDate[2] + " " + arrContractSignedDate[1] + " " + arrContractSignedDate[0]);
    document.write("<br>arrClosedDate: " + arrClosedDate[2] + " " + arrClosedDate[1] + " " + arrClosedDate[0]);
 
</script>
 
</body>
</html>

Open in new window

Comment
Watch Question

HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
Remember that the Date() object constructor expects the month number to
be from 0..11, not 1..12
HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:

var dtContractSigned = new Date( parseInt( arrContractSignedDate[ 2 ] ),
                                 parseInt( arrContractSignedDate[ 1 ] ) - 1, paseeInt( arrContractSignedDate[ 0 ] ) );

Open in new window

HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
Oh yeah, and if your two digit dates have leading zero's
you need to use something like:


parseInt( arrContractSignedDate[ 1 ], 10 ) - 1

Open in new window

HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
For example, try this:
var str = '14/02/2009'
var arr = str.split( '/' )
var day = new Date( parseInt( arr[ 2 ] ),
                    parseInt( arr[ 1 ], 10 ) - 1,
                    parseInt( arr[ 0 ], 10 ) )
 
function D2( val ) {
  return ( val ) < 10 ? '0' + val : val
}
 
document.write( D2( day.getDate() ) + '/' +
                D2( day.getMonth() + 1 ) + '/' +
                day.getFullYear()
              )

Open in new window

HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
whole thing
<html>
<head>
<title>Q_24130760</title>
</head>
<script type="text/javascript">
  Date.prototype.dayDiff = function( d2 ) {
    var d = Math.abs( this - d2 );
    return Math.floor(d / (24 * 60 * 60 * 1000));
  }
 
  Date.prototype.busDayDiff = function( d2 ) {
    var d = this.dayDiff( d2 );
    var t = d % 7;
 
    if ( this < d2 ) {
      w1 = this.getDay();
      w2 = d2.getDay();
    }	else {
      w2 = this.getDay();
      w1 = d2.getDay();
    }
 
    if ( w1 > w2 ) {
      t -= 2;
    }
    if ( w1 == 0 && w2 == 6) {
      t--;
    }
    return Math.abs( ( Math.floor( d / 7 ) * 5 ) + t );
  }
 
  //----------------------------------------------------------------------------
  // Name: D2()
  // Role: Prepend a '0' if the specified value is only 1 digit long
  //----------------------------------------------------------------------------
  function D2( val ) {
    return ( val < 10 ) ? '0' + val : val
  }
 
  //----------------------------------------------------------------------------
  // Name: Edate()
  // Role: Convert European date string to a date object
  //----------------------------------------------------------------------------
  function Edate( str ) {
    var result = new Date()
    if ( /(\d{1,2})([-\/])(\d{1,2})\2(\d{4})/.exec( str ) ) {
      var mon = RegExp.$3
      var day = RegExp.$1
      var yr  = RegExp.$4
      result = new Date( parseInt( yr ), parseInt( mon, 10 ) - 1, parseInt( day ) )
//    alert( 'Edate( "' + str + '" ) = ' + D2( result.getDate() ) + '/' + D2( result.getMonth() + 1 ) + '/' + result.getFullYear() )
    } else {
      alert( 'Edate( "' + str + '" ) : unexpected date format' )
    }
    return result
  }
</script>
 
<body>
<script type="text/javascript">
  var sContractSigned = "14/02/2009 00:00:00"
  Edate( sContractSigned )
  var dtContractSigned = Edate( sContractSigned )
 
  var sClosed = "19/02/2009 00:00:00"
  var dtClosed = Edate( sClosed )
 
  var iNumWorkingDays = dtClosed.busDayDiff( dtContractSigned )
 
  document.write("Contract Signed: " + sContractSigned )
  document.write("<br>Closed: " + sClosed )
  document.write("<br>Business Days: " + iNumWorkingDays )
//document.write("<br>arrContractSignedDate: " + arrContractSignedDate[2] + " " + arrContractSignedDate[1] + " " + arrContractSignedDate[0])
//document.write("<br>arrClosedDate: " + arrClosedDate[2] + " " + arrClosedDate[1] + " " + arrClosedDate[0])
</script>
 
</body>
</html>

Open in new window

HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
ooh... I finally went back, and reread the whole thing... (sigh).

Sorry about that.

The reason you are getting 3 is that the starting and ending days are not counted.  So, the only "work" days are:

  16/02/2009
  17/02/2009
  18/02/2009
Software Engineer
CERTIFIED EXPERT
Commented:
If you would prefer to understand the computation, you could replace the
busDaysDiff() with something like:


If you did, then you could figure out a way to add some data structure
(probably an associative array, indexed by date objects) of "holidays"

Then, the "test" would change from

if ( dow > 0 && dow < 6 ) {

to something like:

if ( dow > 0 && dow < 6 && !holiday[ d ] ) {
  //----------------------------------------------------------------------------
  // Name: busDayDiff()
  // Role: Compute the number of work days between two date objects
  //----------------------------------------------------------------------------
  Date.prototype.busDayDiff = function( d2 ) {
    var result = 0
 
    if ( this < d2 ) {
      var start  = this
      var fini   = d2
    } else {
      var start  = d2
      var fini   = this
    }
 
    for ( var d = start; d < fini; d.setDate( d.getDate() + 1 )  ) {
      var dow = d.getDay()
      if ( dow > 0 && dow < 6 ) {
        result++
//      alert( 'workday: ' + USdate( d ) )
      }
    }
 
    return result
  }

Open in new window

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
CERTIFIED EXPERT

Author

Commented:
Hi,

I think we're close here. I have taken your code and restructured it to fit our coding requirements but the functionality is the same. See below. There is a problem though. Using the sContractSignedDate as it is below, I correctly get the number of days as 4. The 13th is a Friday. If I change it to the 14th or 15th Feb, the number of days should not change as these are weekend dates, but it returns 3 which is not correct. It seems that if the sContractSignedDate is a weekend, the number will be one less that what it should. Technically the contract signed date will never be a weekend but this feature relies on the condition not being likely rather than not being possible. I guess I need to add code in to check to see if the contract signed date is a weekend date, and if so, add one to the result. If you could push me in the right direction I would be grateful as my brain is fried with some heavy coding today.

Thanks,

Lee

<html>
<body>
 
<script type="text/javascript">
Date.prototype.busDayDiff = function(pdtEndDate)
{
  var iNumDays = 0;
 
  if (this < pdtEndDate)
  {
    var dtStartDate = this;
    var dtEndDate = pdtEndDate;
  }
  else
  {
    var dtStartDate = pdtEndDate;
    var dtEndDate = this;
  }
 
  for (var d = dtStartDate; d < dtEndDate; d.setDate(d.getDate() + 1))
  {
    var iDOW = d.getDay();
 
    if (iDOW > 0 && iDOW < 6)
    {
      iNumDays++;
    }
  }  
 
  return iNumDays;
}
 
    var sContractSigned = "13/02/2009 00:00:00"
    var sClosed = "19/02/2009 00:00:00";
    var arrContractSignedDateTime = sContractSigned.split(" ");
    var arrContractSignedDate = arrContractSignedDateTime[0].split("/");
    var dtContractSigned = new Date(parseInt(arrContractSignedDate[2]), parseInt(arrContractSignedDate[1]) - 1, parseInt(arrContractSignedDate[0]));
    var arrClosedDateTime = sClosed.split(" ");
    var arrClosedDate = arrClosedDateTime[0].split("/");
    var dtClosed = new Date(parseInt(arrClosedDate[2]), parseInt(arrClosedDate[1]) - 1, parseInt(arrClosedDate[0]));
    var iNumWorkingDays = dtContractSigned.busDayDiff(dtClosed);
    
    document.write("Contract Signed: " + sContractSigned);
    document.write("<br>Closed: " + sClosed);
    document.write("<br>Days Diff: " + iNumWorkingDays);
</script>
 
</body>
</html>

Open in new window

CERTIFIED EXPERT

Author

Commented:
Hang on, I had a moment of clarity...

Thanks for your help.

Lee

Date.prototype.busDayDiff = function(pdtEndDate)
{
  var iNumDays = 0;
 
  if (this < pdtEndDate)
  {
    var dtStartDate = this;
    var dtEndDate = pdtEndDate;
    if (dtStartDate.getDay() == 0 || dtStartDate.getDay() == 6) {iNumDays += 1;}
  }
  else
  {
    var dtStartDate = pdtEndDate;
    var dtEndDate = this;
    if (dtEndDate.getDay() == 0 || dtEndDate.getDay() == 6) {iNumDays += 1;}
  }
 
  for (var d = dtStartDate; d < dtEndDate; d.setDate(d.getDate() + 1))
  {
    var iDOW = d.getDay();
 
    if (iDOW > 0 && iDOW < 6)
    {
      iNumDays++;
    }
  }  
 
  return iNumDays;
}

Open in new window

CERTIFIED EXPERT

Author

Commented:
With my minor addition to cater for the starting date being a weekend, the code works like a charm. Thanks again.

Lee
HonorGodSoftware Engineer
CERTIFIED EXPERT

Commented:
Lee:  that is excellent news.  I'm glad to have been able to help.

Thanks for the grade & points.

Good luck & have a great day.
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.