Another IBM BPM/BAW Date Hack

The team had a user story to determine the Thursday of the 2nd week of the first month of a quarter (so January, April, July and October), recognizing that a week might only contain one day of the month. This would be server-side code running in a BAW Service Flow asset.

January 2021 is a good example:

Notice that January 1 and 2 fall on Friday and Saturday but our user story considers that a valid “week” for the month. So the first Thursday of the 2nd “week” would be January 7.

We had existing code from a prior version of this requirement where the user story was written as “the 2nd Thursday of the Month”, which would end up on January 14, 2021 because we used a method to get the first occurrence of the weekday (Thursday is getDay() == 4 in this case) of the month using mod (%) 7 then moving that date of the month (1-31) out by 7 until we landed on the correct 2nd occurrence: (2-1)*7.

With the new user story, we still determine the Thursday of the 2nd week (that code has been successfully tested), but then we check to see if that week is actually the 2nd week in the month. If not, we subtract 7 days and use that value.

Here’s the new code:

var c = new java.util.GregorianCalendar.getInstance();
c.set(tw.local.targetDate.getFullYear(), tw.local.targetDate.getMonth(), tw.local.targetDate.getDate());
c.setMinimalDaysInFirstWeek(1);
var wk = c.get(java.util.Calendar.WEEK_OF_MONTH);

if (wk > 2) {
   // If we're not in the 2nd week, move back one week (7 days)
   tw.local.targetDate.setDate(tw.local.targetDate.getDate() - 7);
}

We start by creating an instance of the Java GregorianCalendar (the regular Calendar option doesn’t seem to work in either BPM, Java 8, or Rhino, for some reason). And we set the calendar to the matching year/month/date of targetDate (this was previously defined as the 2nd Thursday of the first month in the quarter using the existing code).

The we call setMinimalDaysInFirstWeek to define how many days we consider to be in the first week of the year (don’t worry, this carries over to the rest of the calendar year – October 2021 has the same setup as January 2021). In this case – just 1 day is considered a “week”.

Then we use the WEEK_OF_MONTH constant to determine which week of the month we’re in and decide if we can keep the current targetDate or set it back 7 days to the previous week (it will never be more than 3 weeks off because of the existing “2nd Thursday of the month” logic).

The GregorianCalendar object was helpful and being able to merge the Java and JavaScript was much easier than trying to transcribe into JavaScript or write something entirely from scratch.

The Java GregorianCalendar has a few other helpful methods, one of which is “roll”, but it behaves differently in Java 7 vs Java 8, so consider your environment if you want to experiment with this object.

JavaScript Notes

I ended up down a JavaScript wormhole the other day and found a few interesting sites I bookmarked and wanted to save the context. Remember – I’ve done a lot of IBM BPM development which uses ES5 and a very limited scope of JavaScript. There isn’t really a need for any of the functionality mentioned below, but it was still nice to learn about.

https://flaviocopes.com/javascript-iife/

Immediately-invoked Function Expressions(IIFE) – I’m pretty sure I’ve seen this pattern before…essentially you’re defining a function that is invoked as soon as it is created. The context for this was a security wrapper that was enforcing strict mode.

https://stackoverflow.com/questions/5378559/including-javascript-in-svg

JavaScript in SVG – Sadly I had no idea JavaScript runs inside SVG markup. I actually didn’t really know what SCG markup was aside from normally used for pictures and animation. But that Stackoverflow article provides code for a neat little ball animation that can be copy/pasted into an svg file and run locally, so it is a neat way to learn about JavaScript and SVG.

http://icyberchef.com/

iCyberChef – So I’m still not 100% sure what this site is used for…but it came up in a security article as well because apparently it was broken recently. I’ll follow-up on this again later.

https://www.w3schools.com/jsref/prop_loc_hash.asp

Location Hash – So I’m not entirely sure of the use case for only looking at the anchor part of a URL, but I’m sure its out there…like this, which talks about a SPA scenario and since IBM BPM isn’t really a SPA application, I’m just not that familiar with the pattern. But I’ll save this for later.

IBM BPM – Date Diff Code

Here’s a few blocks of code samples that I like to reference when trying to deal with Dates in IBM BPM.

This first block (Date Difference Calculation Testing) helps you determine an integer number of dates between two TWDate variables.

// Date Difference Calculation Testing
// Date1 + xNumberOfDays = Today
// xNumberOfDays = Today - Date1
 
tw.local.aDate = new TWDate(); //SETUP SAMPLE
tw.local.aDate.parse("05/15/2005","MM/dd/yyyy"); //SETUP SAMPLE
 
if (tw.local.aDate && tw.local.aDate < new TWDate()) {
    // Today...as in right now
    var tday = new Date();
    
    // convert to native Java Date
    var aDay = tw.local.aDate.toNativeDate(); 
    var one_day = 1000*60*60*24; // milliseconds in 1 day
    
    var tm = tday.getTime(); // milliseconds for date1
    var am = aDay.getTime(); // milliseconds for date2
    
    var diff = tm - am; // milliseconds difference
    
    tw.local.dateInt = diff; // this is actually a decimal
    
    // convert milliseconds to days
    var days = Math.round(Math.abs(diff/one_day)); 
    
    // Integer of number of days between aDate and today
    tw.local.dateIntTwo = days; 
}

This is a cool way to tell if a date variable is within a specific threshold.  My use case was that the application stored when it made a specific integration call and then if we came back and needed that call again we’d check to see when it was last invoked.  This could easily be done with caching or something fancier but for this scenario a simple “was the last call too long ago?” bit of logic was all that we needed.

// Assume the service needs to be called again
tw.local.refreshNeeded = true;
 
// aDate is the value of the last time the service was called
if (tw.local.aDate) {
    var lastCall = tw.local.aDate.toNativeDate();
    var now = new Date();
    // EPV to store threshold in seconds to consider time stamp stale
    var threshold = Number(tw.epv.refreshThreshold);
    var thresholdMS = threshold * 1000;
    var lcMS = lastCall.getTime();
    var nowMS = now.getTime();
    var diff = (nowMS - lcMS);
    if (diff <= thresholdMS) {
        tw.local.refreshNeeded = false;
    } 
}