• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1726
  • Last Modified:

Number of working days between two dates

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

0
Lee Savidge
Asked:
Lee Savidge
  • 8
  • 3
1 Solution
 
HonorGodCommented:
Remember that the Date() object constructor expects the month number to
be from 0..11, not 1..12
0
 
HonorGodCommented:

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

Open in new window

0
 
HonorGodCommented:
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

0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
HonorGodCommented:
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

0
 
HonorGodCommented:
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

0
 
HonorGodCommented:
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
0
 
HonorGodCommented:
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

0
 
Lee SavidgeAuthor 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

0
 
Lee SavidgeAuthor 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

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

Lee
0
 
HonorGodCommented:
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.
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 8
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now