logo

Predicting the end

The end is near. Longtime developers have known about this for quite some time. It seems to crop up in many of our development projects. I am sure that you may have already guessed that it is not as grim as I would have you believe. What I am talking about is the ability to calculate dates, and more specifically, calculating an end date.

A brief interruption:

Often, users will ask if Notes or Domino can be programmed to do this or to do that. Or, they saw an application and thought it was a fantastic implementation of a solution. "Can we integrate this into our work environment?" some might query. I counter with my overzealous mantra: "We can do anything!". In any type of development environment most things are easy to conquer once the secret code (algorithm) has been cracked.

With that in mind, allow me to preface that there are 382 ways to skin a cat, and this is just one of those ways.

The Problem:

Have you ever committed to a 21-day project with belief that your completion date was three weeks away? Ahhh... so you are like me. Many times, when we discuss a project's due date, we tend to think in terms of weeks, yet we cannot convince ourselves that a 21-day project is 21 work days. And as our mathematics will prove, 21 work days is not three weeks, rather almost four-and-a-half weeks. Never shortchange yourself!

Now it is time to reveal our "secret code." We will take the start date (obviously), add 1 day, check if it is a weekend or holiday, and continue this loop until the number of work days have been satisfied. Finally, we'll adjust our start date with the counter from our loop.

The Solution:

First, let's create a central storage area for our list of holidays. Unfortunately, I am unable to use the Domino Directory's holiday list, but it would be a good practice to do this if it is possible. If you want to use the Domino Directory and if your application will be distributed to mobile users, keep in mind that the mobile users will need to replicate a subset of the Directory.

Instead of the Directory, I chose to create a profile document. It is quick to access, and it is very easy to implement. If you decide on using a profile document, be sure your modify with the appropriate command:


@Command([EditProfile]; "yourprofiledocument")


The holiday list below contains a simple multi-value field. We will not use the holiday names in our application, but I have included them for future reference, and for clarification.



LotusScript or JavaScript?

Notes- or Web-based? Agent or UI? Which looks better? One or two? Fortunately for me, I have completed the computation in both 'Script languages. I'll define the elements using LotusScript and provide a translation for JavaScript.

LotusScript:

Let us begin using our upcoming holiday season for our scenario. We will assume that today is our start date (11/21/2001 in US format), and the number of work days is 22. With the help of my trusty Far Side calendar, and by using all fingers and toes (I have six toes on each foot), our end date should be 12/27/2001.

First, we will create a few date objects. Here's a quick tip: Make use of a reference date. In this case, the reference date is the start date, but more importantly, it is a date object which is never modified.

Dim session As New NotesSession
Dim workspace As New NotesUIWorkspace
Dim db As NotesDatabase
Set dB=session.CurrentDatabase
Dim uidoc As NotesUIDocument
Set uidoc = workspace.CurrentDocument
Dim profDoc As NotesDocument
Set profDoc = db.GetProfileDocument("profHolidays")
Dim referenceDate As notesdatetime
Set referenceDate = New NotesDateTime(Now)
Dim endDate As NotesDateTime
Set endDate = New NotesDateTime(referenceDate.DateOnly)
Dim testDate As NotesDateTime
Set testDate = New NotesDateTime(referenceDate.DateOnly)


I'll set up a few counters. intBusDays is the number of work days. In this instance we have it hard coded:

intAdjustmentCounter = 0
intTestCounter = 0
intBusDays = Cint(uidoc.FieldGetText("busDays"))


Now we will loop through the days until the condition is met that our test counter equals the number of work days. In other words, if we specified an end date 4 work days from now, and today is Friday, we would add two days to our adjustment counter to compensate the weekend (6 days).

Do Until intTestCounter = intBusDays

Add 1 to our adjustment counter. The adjustment counter is the number we use to adjust our start date to achieve our end date.

intAdjustmentCounter = intAdjustmentCounter + 1<br>

Increment the start date by one.

Call testDate.AdjustDay(1)

Our function checkHoliday is invoked next. With this function, we check the test date to see if it falls on a holiday or weekend. This function simply returns a True or a False.

intHolidayTest = checkHoliday(profDoc, testDate.dateonly)

If the test date is not a holiday/weekend, then we will increment the test counter. By incrementing the test counter, we are moving towards satisfying the number of work days.

If intHolidayTest = False Then
&nbsp;intTestCounter = intTestCounter + 1
End If
Loop

When the loop is finished, our adjustment counter contains the number of days to adjust the start date. In case you missed it in the top few lines, the endDate was initialized with our referenceDate value. Finally, we set the end date.

endDate.AdjustDay(intAdjustmentCounter)
Call uidoc.Fieldsettext("yourDate", endDate.DateOnly)

The final piece to include is the checkHoliday function. The code seems self-explanatory, but note that not only do we pass the test date to the function, but we also send the profile document containing the holiday list. The function returns an integer (true or false).

Function checkHoliday(profDoc As NotesDocument, dateToCheck As Variant) As Integer
Dim intHolCounter As Integer
intHolCounter = 0
holidays = profDoc.holidayDate
checkHoliday = False
' test the date with the list of holidays
Forall holidayDate In holidays
If holidayDate = dateToCheck Then checkHoliday = True
End Forall

'test the date for weekends<br>
If Weekday(dateToCheck) = 7 Or Weekday(dateToCheck) = 1 _ Then checkHoliday = True
End Function


JavaScript:

Now that you understand the method, here's the JavaScript translation. I will not detail the JavaScript, but it is merely a translation of the above LotusScript formula.

Before you start, ensure that the profile document containing the holiday list appears visible to your browser. If you do not have 'Generate HTML for all fields' ticked, be sure to use the method described in this article.



Above, I created a temporary, multi-value, computed-for-display field, encased in HTML. The values are separated with a comma, and the field formula is simply:

@GetProfileField("profHolidays"; "holidayDate")

Here is the code for your button's JavaScript onClick event:


var f=document.forms[0];
var referenceDate = f.StartDate.value;
var endDate = new Date(referenceDate);
var intBusDays = f.busDays.value //set the work days
var intAdjustmentCounter = 0;
var testDate = new Date(referenceDate);

for (var intTestCounter = 0; intTestCounter < intBusDays; "")
{
intAdjustmentCounter += 1;
testDate.setTime(testDate.getTime() + 1 * 24 * 60 * 60 * 1000);
var holidayTest = checkHoliday(getDateOnly(testDate));
if (holidayTest == false) {
intTestCounter += 1;
}
}

endDate.setTime(endDate.getTime() + intAdjustmentCounter * 24 * 60 * 60 * 1000);
f.busDays.value = getDateOnly(endDate);

Finally, include the following two functions in the JS header or in a .js file. The first function is the checkHoliday function similar to the one detail in LotusScript. The getDateOnly function merely transforms a date/time object to a string (mm/dd/yyyy) for comparison:

function checkHoliday(dateToCheck)
{
var theDate = new Date(dateToCheck);
var intHolCounter = 0;
var arrHolidays = document.forms[0].txtHolidays.value.split(",");
var hasHoliday = 0;

for (h = 0; h < arrHolidays.length; h++) {
var holiday = arrHolidays[h];
var holidayDate = new Date(holiday);

if (getDateOnly(holidayDate) == getDateOnly(theDate))
{
hasHoliday = 1;
}
}

if ((theDate.getDay() == 0) || (theDate.getDay() == 6) || hasHoliday == 1)
{
return true;
}else{
return false;
}
}

function getDateOnly(dtval) {
var theMonth = dtval.getMonth() + 1; //months are 0 to 11 (why?)
var theDate = dtval.getDate();
var theYear = dtval.getFullYear();
return theMonth + "/" + theDate + "/" + theYear;
}
Extending the formula:

There are many uses for this formula, and you can easily extend it. One practical use is with project management applications. These applications usually contain complex forms and documents, especially if parallel work is performed. Hence, one form may have multiple due date fields.

I modified the formulas to loop through field names and due days. Once again, to keep things centralized ('centralised' for Jake), I created a profile document with multi-value fields to contain this information. Do you get a sense that I am not a big fan of hard-coding field names and variable data?



Final note:

Now that you have tackled this method of computing the end, it seems that there are only 381 ways left to skin a cat!

Feedback

  1. Example Database

    Do you have an database that tracks projects that you wouldn't mind posting?

    Would be useful <not only to see an example of this in action> but to use as well.

    Thx

    1. Re: Example Database

      Scott,

      Unfortunately I do not have a project management database ready for your perusal. However, I can slam the functions from the article into a database if you like. I'll post the download location as soon as it is ready.

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:



Navigate other articles in the category "Miscellaneous"

« Previous Article Next Article »
The New Look and Stuff   Keeping the boss happy

About This Article

Author: Joseph Pollone of clickinvites.com
Category: Miscellaneous
Keywords: none

Options

Feedback
Print Friendly

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »