A function to mimic moment.js fromNow() functionality
/**
* momentFrom( target, reference=now(), withoutSuffix=false )
* Mimics moment(a).from(b) - assumes all dates are in UTC.
*/
function momentFrom( required any target, any reference=now(), boolean withoutSuffix=false ){
// Java time classes
var ZoneId = createObject("java","java.time.ZoneId");
var Instant = createObject("java","java.time.Instant");
var ZonedDateTime = createObject("java","java.time.ZonedDateTime");
var Duration = createObject("java","java.time.Duration");
// Force UTC zone
var zone = ZoneId.of("UTC");
// Normalize to ZonedDateTime in UTC
var toZDT = function(any x){
if (isNumeric(x)) {
var n = javacast("long", x);
var ms = (n GT 9999999999) ? n : n * 1000; // seconds -> ms
return Instant.ofEpochMilli( javacast("long", ms) ).atZone( zone );
}
var d = x;
if ( !isInstanceOf(d, "java.util.Date") ) {
d = parseDateTime( javacast("string", x) );
}
// CF date is a java.util.Date (implicitly UTC here)
return Instant.ofEpochMilli( javacast("long", d.getTime()) ).atZone( zone );
};
var t = toZDT( arguments.target );
var ref = toZDT( arguments.reference );
var seconds = Duration.between( ref, t ).getSeconds(); // >0 = future
var future = (seconds GT 0);
var absSec = abs( seconds );
var days = absSec / 86400;
var value = 0;
var unit = "";
var phrase = "";
// Moment.js thresholds (rounded to largest unit)
if (absSec < 45) {
phrase = "a few seconds";
} else if (absSec < 90) {
phrase = "a minute";
} else if (absSec < 45*60) {
value = round(absSec/60);
unit = (value EQ 1 ? "minute" : "minutes");
phrase = "#value# #unit#";
} else if (absSec < 90*60) {
phrase = "an hour";
} else if (absSec < 22*3600) {
value = round(absSec/3600);
unit = (value EQ 1 ? "hour" : "hours");
phrase = "#value# #unit#";
} else if (absSec < 36*3600) {
phrase = "a day";
} else if (days < 26) {
value = round(days);
unit = (value EQ 1 ? "day" : "days");
phrase = "#value# #unit#";
} else if (days < 45) {
phrase = "a month";
} else if (days < 320) {
value = round(days/30);
unit = (value EQ 1 ? "month" : "months");
phrase = "#value# #unit#";
} else if (days < 548) {
phrase = "a year";
} else {
value = round(days/365);
unit = (value EQ 1 ? "year" : "years");
phrase = "#value# #unit#";
}
if (arguments.withoutSuffix) {
return phrase;
}
return future ? "in #phrase#" : "#phrase# ago";
}