Forsta Plus provides six functions for working with dates. InterviewStart and InterviewEnd return the interview_start and interview_end time stamps automatically recorded when the respondent starts the interview and reach the end of the interview or a stop node (the end page). SetInterviewStart and SetInterviewEnd set these timestamps. IsDate and IsDateFmt are used for validation of dates.
Such date variables can be used both in the surveys when asking respondents for dates (for examples date of birth), and also as "hidden" variables to set and store dates through scripting. The date question type has built in validation and a calendar pop-up interface, so it is not necessary to use the IsDate and IsDateFmt functions for validation of the respondent's answer when using this question type.
You can supply supported formats into the function, and also decide if it should use respondent's culture or server's neutral culture. Respondent's culture affects things like day/month names (Monday vs понедельник), years (2024 is 1446 in Arabic), right-to-left languages.
InterviewStart and InterviewEnd
Both of these functions return dates, however they will return the dates as strings. The syntax you can use to get instances of the Date object from InterviewStartand InterviewEndis as below:
dateObj = InterviewStart();
dateObj = InterviewEnd();
Calculating Time Spent
You can calculate the response time of the respondents on specific pages or sections of the survey by using the Date object. Assume you want to calculate the number of seconds the respondent has spent from the beginning to a specific point in the interview and store that in a numeric question with hidden property (time_part1).
if(!f("time_part1").toBoolean())
{
var d1 = InterviewStart()
var d2 = new Date()
var diff = (d2.getTime() - d1.getTime())/1000
f("time_part1").set(diff)
}The if-condition is there to ensure that the time is only calculated the first time the respondent goes through the questionnaire (if he or she is allowed to modify previous responses), when no value has been set in the hidden time_part1 question.
You must ensure you use the correct settings on the hidden numeric question. Decimal places should be set to 3 (3 digits after the decimal point) since getTime returns milliseconds. We divide the number of milliseconds with 1000 to get the answer stored as seconds. Total digits has to be set high enough to be able to store both the 3 digits after the decimal point and the highest number of seconds a respondent will use.
If you want to calculate the response time for the next part of the questionnaire as well, you can for example use the following script to set that in another hidden numeric question (time_part2):
if(!f("time_part2").toBoolean())
{
var d1 = InterviewStart();
var d2 = new Date();
var diff = ((d2.getTime() - d1.getTime())/1000)-f("time_part1").toNumber();
f("time_part2").set(diff);
}IsDateFmt and IsDate
These functions are also described in IsDateFmtAndIsDate. Here we will look closer at the properties of the objects returned from them and provide some examples on how to use the functions together with the Date object.
If you have a string with day, month and year in some format (for example from an open text question) and you want to check if it is a valid date, you can use the IsDateFmt function.
dObj = IsDateFmt( argument,format );
If you have three values, one for day, one for month and one for year, (for example from three questions), you can use IsDate to check if it is a valid date.
dObj = IsDate( day,month,year );Both IsDateFmt and IsDate return an object with the properties day, month and year if the date is valid, null otherwise. The object returned is not a Date object – it has none of the methods of the Date object, just these three properties.
dObj.dayreturns the day part of the date as an integer in the range 1-31.
dObj.monthreturns the month part of the date as an integer in the range 1-12.
Important: This is different from the Date object, where getMonth and getUTCMonth return an integer in the range 0-11.
dObj.yearreturns the year part of the date as an integer (4 digits).
In this example we have an open text question and want the respondent to enter a date in the format YYYY-MM-DD. We want to validate that the format is correct and that the date is a valid date, and also that the date is not after the current date.
This can be done with the following script in the validation code field of the open text question (date1).
var d = IsDateFmt(f("date1").get(),"YYYY-MM-DD");
if(!d)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Invalid date. Please correct using the format YYYY-MM-DD");
}
else
{
var dt = new Date();
dt.setFullYear(d.year,d.month-1,d.day);
var current = new Date();
if(dt.valueOf() > current.valueOf())
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please do not enter a date after the current date.");
}
}If the date provided is not valid, IsDateFmt(f("date1").get(),"YYYY-MM-DD") will return null. Used in a condition null will yield the Boolean value false. If the date is valid, IsDateFmt will return an object. Used in a condition this will yield true. So the first if condition, with !d , makes sure there will come an error message only when the date is invalid or wrong format is used.
It is important that we include the rest of the script in an else branch because the properties year, month and day are not defined if null is returned. So we need to make sure that that part of the script only is run when we have an object in the variable d, to prevent script errors.
Then we introduce two instances of the Date object, and set one of them (dt) to the date answered, and the other (current) to the current date. We use the setFullYear method to set the date. Observe that we have to subtract 1 from the month, since the Date object uses the integers 0-11 for month, whereas the month property in the object returned from IsDateFmt uses the more conventional integers 1-12.
Validating that a Date with Drop-downs is within the next two Weeks
You can also set up the date with three questions, one for year, one for month and one for day. This can for example be set up in a 3D grid with 3 grid questions as drop-downs like this:
Figure 1 - 3D Grid with year, month and day grid questions
Figure 2 - 3D Grid Example
Note: The codes of the questions must be numeric and the same as the legal year/month/day values. For year the codes must be the full value (e.g. 2002), for month they must be numbers between 1 and 12, and for day they must be numbers between 1 and 31.
Let us say that we want to check that the answer is a valid date (so that e.g. February 30th is not allowed), and that it is a date in the next two weeks from the current date.
var d = IsDate(f("day")["1"].get(),f("month")["1"].get(),f("year")["1"].get());
if(!d)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Invalid date. Please correct.");
}
else
{
var dt = new Date();
dt.setFullYear
(d.year,d.month-1,d.day);
var current = new Date();
var limit = new Date();
limit.setDate(current.getDate()+14)
if(dt.valueOf() < current.valueOf() || dt.valueOf() > limit.valueOf())
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please select a date within the next two weeks.");
}
}This script takes advantage of the fact that when using setDate, the month and possibly also year value will automatically be updated if the value for date is outside the limit for the month. So if the result of current.getDate()+14 is a number larger than the number of days in a month, limit will be set to a date in the next month. For example, if current date is April 20th 2002, the limit will be 4th of May 2002 (April 20th + 14 days).
Finding the Weekday
You can use the Date object to make a small application in Forsta Plus to find the weekday of a specific date. Start with an open text question (date3) asking for a date. The validation can be done like this:
var d = IsDateFmt(f("date3").get(),"YYYY-MM-DD");
if(!d)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Invalid date. Please correct. Use the format YYYY-MM-DD");
}Then you can have a script node with the following function:
function FindDay(qID) : String
{
var d = IsDateFmt(f(qID).get(),"YYYY-MM-DD");
var dt = new Date();
dt.setFullYear(d.year,d.month-1,d.day);
var days = new Array
("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")
return days[dt.getDay()]
}
After the open text question you can have an info node like this:
Figure 4 - Info node after open text question
This will for example give this output if the answer on the date question was 2002-04-20:
Figure 5 - Example output
Exercise 7: Course Registration
Let us say you want to build a questionnaire where people can register for weekly courses that are held on Mondays. You want the respondents to provide the date they want to register for in an Open Text Question, but have to validate the date format (which should be "MM/DD YYYY") and check that the date provided is a Monday and it is after the current date.
Write a validation script for this question. Question ID is date4.
The answer is given in APPENDIX A Answers to Exercises.
Converting Data of Birth into Age (in number of years)
If you have an open text question dob where respondents insert their date of birth, you can calculate the respondent’s age and set it in a hidden question age with the following script, which is placed in the validation code of the question:
var d = IsDateFmt(f("dob").get(),"YYYY-MM-DD");
if(!d)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Invalid date. Please use the format YYYY-MM-DD");
}
else
{
var birthday = new Date();
birthday.setFullYear(d.year,d.month-1,d.day);
var today = new Date();
var years = today.getFullYear() - birthday.getFullYear();
birthday.setYear(today.getFullYear());
// If your birthday hasn't occurred yet this year, subtract 1.
if(today < birthday)
{
years-- ;
}
f("age").set(years);
}FormatDate
FormatDate(date, format, useRespondentCulture)Example
const date = new Date('12-23-2022 02:35');
const format = 'dddd, MMMM d, yyyy h:mm:ss tt';
FormatDate(date, format, true); // expected output
-> Friday, December 23, 2022 2:35:00 AM