JavaScript Transformation: Date function does not work as expected

I would like to use the JavaScript Transformation Service in order to display a nice human readable duration instead of an ISO 8601 DateTime string. I wrote a small JavaScript function which works nicely in a browser environment but as soon as I add it to the State Description Metadata with (JS(duration.js):%s) it won’t work as it returns “Invalid Date”. Apparently Date.parse(i) does not work as expected. Do I need to add a library and if so how?

Here’s the code:

(function(i) {
	if (i == 'NULL') { return i; }
	if (i == '-') { return 'Undefined'; }
	//var date = new Date(i);
	var now = Date.now();
	var thn = Date.parse(i);
	var dur = (now - thn) / 1000; // in Sekunden
//	console.log("Von: " + Date.parse(i));
//	console.log("Bis: " + now);
//	console.log("Seconds: " + dur);
	var weeks = 0;
	var days = 0;
	var hours = 0;
	var minutes = 0;
	var seconds = 0;
	var duration = "";
	if (dur >= 60) { // 60 seconds in a minute
		minutes = Math.floor(dur / 60); // Number of minutes
		seconds = Math.round(dur - (minutes * 60)); // Remove minutes from seconds bucket
//		console.log("Seconds: " + seconds);
//		console.log("Minutes: " + minutes);
	}
	if (minutes >= 60) { // 60 minutes in an hour
		hours = Math.floor(minutes / 60); // Number of hours
		minutes = minutes - (hours * 60); // Remove hours from minutes bucket
//		console.log("Minutes: " + minutes);
//		console.log("Hours: " + hours);
	}
	if (hours >= 24) { // 24 hours in a day
		days = Math.floor(hours / 24); // Number of days
		hours = hours - (days * 24); // Remove days from hours bucket
//		console.log("Hours: " + hours);
//		console.log("Days: " + days);
	}
	if (days >= 7) { // 7 days in a week
		weeks = Math.floor(days / 7); // Number of days
		days = days - (weeks * 7); // Remove weeks from days bucket
//		console.log("Days: " + days);
//		console.log("Weeks: " + weeks);
	}
//	console.log("Weeks: " + weeks);
	if (weeks > 0) {
		duration = weeks + ((weeks == 1) ? " Woche" : " Wochen")
//		console.log("Duration: " + duration);
		if (days > 0) {
			duration += " und " + days + ((days == 1) ? " Tag" : " Tagen");
//			console.log("Duration: " + duration);
		}
	} else {
		if (days > 0) {
			duration = days + ((days == 1) ? " Tag" : " Tagen")
//			console.log("Duration: " + duration);
			if (hours > 0) {
				duration += " und " + hours + ((hours == 1) ? " Stunde " : " Stunden");
//				console.log("Duration: " + duration);
			}
		} else {
			duration = new Date(i).toLocaleTimeString()
		}
	}
//	console.log(duration);
	return duration;
})(input)

If it’s a duration, if you use Number:Time to store the number of seconds (hours, minutes, whatever unit you desire) you can use the standard DateTime formatting to get to days, hours, minutes, seconds, milliseconds. For example, I have a Number:Time Item to store the number of seconds of runtime my UPs is predicted to be able to support. I have a state description pattern of %1$tH:%1$tM:%1$tS and the state appears as

So you could just stop after calculating the number of seconds and let the state description format that as you desire.

You can import the java.time.ZonedDateTime and Duration classes and use those. Your i is going to be the toString of a DateTimeType which uses ZonedDateTime under the covers. You import Java classes using var ZonedDateTime = Java.type('java.time.ZonedDateTime');

That would look something like:

(function(i) {
  if (i == 'NULL') { return i; }
  if (i == '-') { return 'Undefined'; }  

  var ZonedDateTime = Java.type('java.time.ZonedDateTime');
  var Duration = Java.type('java.time.Duration');

  var delta = Duration.between(ZonedDateTime.now(), ZonedDateTime.parse(i))
  var weeks = Math.floor(delta.toDaysPart() / 7);
  var days = delta.toDaysPart() % 7;
  var hours = delta.toHoursPart();
  var mins = delta.toMinutesPart();
  var secs = delta.toSecondsPart();

  return ((weeks) ? ""+weeks+":" : "") + hours + ":" + mins + ":" + secs;
  
})(input)

Or if you are OK with an ISO8601 duration string (e.g. P2DT3H45M67.897S)

(function(i) {
  if (i == 'NULL') { return i; }
  if (i == '-') { return 'Undefined'; }  

  var ZonedDateTime = Java.type('java.time.ZonedDateTime');
  var Duration = Java.type('java.time.Duration');

  return Duration.between(ZonedDateTime.now(), ZonedDateTime.parse(i)).toString();

})(input)
1 Like

Thanks a lot, Rich, that was extremely helpful.

As my timestamp comes like 2023-01-23T11:07:27.180445+0100 I had to add a formatter string:

	var DateTimeFormatter = Java.type('java.time.format.DateTimeFormatter');
	var f = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSX");