The Ultimate Answer to Dates, Forms and Workflows (and everything). ...which is not ‘42’.

3 Likes
over 3 years ago
For many years I have struggled with the date fields and managing them within workflows. Today I finally decided to do something about it. Here collected are the most important details to remember when handling date attributes within your provisioning request definitions (PRDs).

Form Script



There are several possible formats we might need to deal with in a PRD. Within the form, when you query LDAP using the IDVault.get() method, you get a string with the date in LDAP format:

20160506000000Z


If you try to set the value of a date field with this value, it will fail. It first needs to be parsed into a ECMAscript Date object. The Date object has a static .parse() method but I have my issues with it. For one thing, earlier browsers may not act consistently since the documentation describes it as implementation dependent.

Another issue is something of a pet peeve of mine – software opacity. Software that doesn’t let you in to see how it works (are you listening Apple?) makes programmers crazy. And the parse() method does some magic to figure out what the date format of the string you pass in is so it can produce the correct value for the Date object. If I knew how it worked I could perhaps make certain I got the correct results, but since there is this one method parse() and it uses foofoo dust or some other witchcraft to figure out what to do, I don’t want to use it.

Rather than taking my chances, the following code, which I will admit is plagiarized, will reliably and consistently convert the LDAP zulu time format into an ECMAscript Date object:

function toDate(str) {
//convert an LDAP date string into a Date object
try
{
str = str '';
var year = str.substring(0, 4);
var month = str.substring(4, 6);
var day = str.substring(6, 8);
var hour = str.substring(8, 10);
var minute = str.substring(10, 12);
var second = str.substring(12, 14);
var _date = new Date();
// Set date parts
_date.setFullYear(year);
_date.setMonth(month-1);
_date.setDate(day);
_date.setHours(hour);
_date.setMinutes(minute);
_date.setSeconds(second);
// Return the date object
return _date;
}
catch ( e ) {
alert( 'Error: ' e.description);
return new Date();
}


Kludgy, but reliable and effective.



It would be lovely to just pass in the resulting ECMAscript Date object into the setValues() method of the field object but alas it is not to be. To set the value on that object you must convert it to another string format: “MM/dd/yyyy”. I have another method to do that (a much betterer one that I wrote rather than the toDate() above that I borrowed):





function toMDYYYY(d)
{
return d.toString("MM/dd/yyyy");
}



Now really you don’t need a function for this, you could call the method on the object directly, but I personally prefer to build a function library so I don’t have to exercise all those extra neurons to reinvent the wheel each time I build a PRD.



The importance of a Date object in the middle of this is that you also might want to perform some sort of math before setting the control. A common use case is that you want the default date to be three months in the future so that a non-employee gets an expiring account automagically.





EXPIRATION = 90;

function setExpirationDate(field)
{
var s = new Date().getTime();
s = s EXPIRATION * 1000 * 24 * 60 * 60; // add n days to today
field.setValues( toMDYYYY(new Date(s)));
return s;
}



Workflow Script



With my form working, I turned to the workflow. I was setting the value from flowdata into a date attribute in an entity activity today, and saw the following error:

2018-05-03 11:45:22,914 [ERROR] LogEvent [RBPM] [Workflow_Error] Initiated by cn=UAAdmin,o=acme, Error Message: Attempt to set value on Data item [LoginExpirationTime2] using incorrect type; expecting [date] got [JavaException: java.text.ParseException: Unparseable date: "20180430200000000-0400"].., Process ID: 5979062fa75948338e0d9e77fec42cb3, Process Name:cn=UserAdministration,cn=RequestDefs,cn=AppConfig,cn=UserApp,cn=IDV,ou=IDM,ou=services,o=acme:739, Activity: Activity1, Recipient: cn=UAAdmin,o=acme


Today I was trying to recall how I solved this problem before, and found several forum posts which indicated the easiest way to work around this was to go into the DAL and change the attribute type from date-time to string. Then you can pass in the string the way that LDAP wants it.

It seemed daft to expect that to get dates to synchronize out of workflow into an object in an entity activity in a workflow, that you would have to intentionally mis-configure the DAL to tell it that the attribute is a string. That could also break other portlets that might be depending on that DAL definition to allow management of the attribute. But by doing that, you do work around the problem.

The function flowdata.get() from a date control field returns a string in this format:

20180430200000000-0400


This is the format produced in java when instantiating a java.text.SimpleDateFormat object with the parameter “yyyyMMddhhmmssSSSZ”. It was clear from the error message that the workflow engine was expecting an object, not a string, but was it an ECMAscript Date or a Java java.util.Date object?

My gut said go with the java.util.Date object, mostly because I think I tried the ECMAscript Date approach in the past and failed. So I wrote this function:



function workflowToJavaDate(data)
{
if (null == data) return null;
var workflowFormat = new java.text.SimpleDateFormat("yyyyMMddhhmmssSSSZ");
try
{
var tempDate = workflowFormat.parse(data);
}
catch (err)
{
System.out.println(err.toString());
return null;
}
return tempDate;
};


I wrapped this around the flowdata.get() in the data item mapping tab of the entity activity:


workflowToJavaDate(flowdata.get('start/request_form/PasswordExpirationTime'))



And voila! It worked, simple as that, without changing the DAL.

Conclusion



Dealing with dates is inherently confusing, but it could be worse; just imagine how bad this will get when someone invents time travel?

Seriously, dates are a major nuisance. Doing date math drives me bonkers. Normalizing the different ways we represent dates between different applications is frequently a challenge we need to overcome in IDM solutions. Unfortunately, here, even within the different components of the same IDM application, we need to convert date representations from one part to another.

Hopefully this document will put the process into writing so that those who are struggling with remembering how to handle this will have a place to go. And who might forget? Well, mostly, me. And so, thanks for your indulgence.

Labels:

How To-Best Practice
Comment List
Anonymous
Parents Comment Children
  • Hi,

    I tried your script to set a expiration date , but I fail setting the date in a Date field.

    I set the following onload action in the text field that works fine:

    function toMDYYYY(d)

    {

    return d.toString("MM/dd/yyyy");

    }

    var s = new Date().getTime();

    alert (' var s is ' + s)

    s = s +  180 * 1000 * 24 * 60 * 60;

    field.setValues( toMDYYYY(new Date(s)));​

    But I did not find which format to set for a Date field (I want to set a default expiration date but leave the option to change it with the pickdate field)

    Any help ?

    Thanks a lot.

  • HI,

    I finally made it with the following :

    var s = new Date().getTime();
    var s = s  + 180 * 1000 * 24 * 60 * 60;
    var t = new Date(s).toLocaleString()
    field.setValues(t.substring(0,10))

    This set the default value to "today + 6 Months".

    Thanks a lot for your help with the examples.

    Sylvain

Related Discussions
Recommended