JavaScript Human-Readable Time Diff (“5 Hours Ago”, “Will Be In A Day”,…)

Some time ago, I’ve wrote an article iCompile – PHP Human-Readable Time Diff (“5 Hours Ago”, “Will Be In A Day”,…),
running the “main action” procedure in a recursive-loop, I could improve the spectrum of the result, so it will include also complex time-periods (“5 hours, 100 milliseconds”, “3 years, 12 days”, “3 days, 14 seconds”, etc…)
among other code improvements :)

[also available in my github’s gist: https://gist.github.com/eladkarako/65b3b395c5c9d5233f104eceeb7cede4]

function human_time_diff(from, to){ /*accept unix-datetime integer, Date object or date string, fallback is current date*/
  "use strict";

  var now, diff;

  /*---*/
  function difference(diff){
    var
         MINUTE_IN_SECONDS = 60
       , HOUR_IN_SECONDS   = 60  * MINUTE_IN_SECONDS
       , DAY_IN_SECONDS    = 24  * HOUR_IN_SECONDS
       , MONTH_IN_SECONDS  = 30  * DAY_IN_SECONDS
       , YEAR_IN_SECONDS   = 365 * DAY_IN_SECONDS
       , since
       , milliseconds, seconds, minutes, hours, days, months, years
       ;

    if(0 === diff){
      since = "0 seconds";
    }
    else if(diff > 0 && diff < 1){
      milliseconds = Math.trunc(diff * 1000);
      since = milliseconds + " " + (1 === milliseconds ? "millisecond" : "milliseconds");
    }
    else if(diff >= 1 && diff < MINUTE_IN_SECONDS){
      seconds = Math.trunc(diff);
      seconds = Math.max(diff, 1);
      since = seconds + " " + (1 === seconds ? "second" : "seconds");
      
      diff = diff - (seconds);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }
    else if(diff >= MINUTE_IN_SECONDS && diff < HOUR_IN_SECONDS){
      minutes = Math.trunc(diff / MINUTE_IN_SECONDS);
      minutes = Math.max(minutes, 1);
      since = minutes + " " + (1 === minutes ? "minute" : "minutes");
      
      diff  = diff - (minutes * MINUTE_IN_SECONDS);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }
    else if(diff >= HOUR_IN_SECONDS && diff < DAY_IN_SECONDS){
      hours = Math.trunc(diff / HOUR_IN_SECONDS);
      hours = Math.max(hours, 1);
      since = hours + " " + (1 === hours ? "hour" : "hours");

      diff  = diff - (hours * HOUR_IN_SECONDS);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }
    else if(diff >= DAY_IN_SECONDS && diff < MONTH_IN_SECONDS){
      days = Math.trunc(diff / DAY_IN_SECONDS);
      days = Math.max(days, 1);
      since = days + " " + (1 === days ? "day" : "days");

      diff  = diff - (days * DAY_IN_SECONDS);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }
    else if(diff >= MONTH_IN_SECONDS && diff < YEAR_IN_SECONDS){
      months = Math.trunc(diff / MONTH_IN_SECONDS);
      months = Math.max(months, 1);
      since = months + " " + (1 === months ? "month" : "months");

      diff  = diff - (months * MONTH_IN_SECONDS);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }
    else if (diff >= YEAR_IN_SECONDS){
      years = Math.trunc(diff / YEAR_IN_SECONDS);
      years = Math.max(diff, 1);
      since = years + " " + (1 === years ? "year" : "years");
      
      diff  = diff - (years * YEAR_IN_SECONDS);
      if(diff > 0)
        since = since + ", " + difference(diff);                             /* calculate leftover time, recursively */
    }

    return since;
  }
  /*---*/

  now  = new Date();

  from = ("number" === typeof from)                                                   ? Math.max(from, 0)      :
         ("string" === typeof from)                                                   ? Number(new Date(from)) :
         ("object" === typeof from && "date" === from.constructor.name.toLowerCase()) ? Number(from)           : Number(now)
                                                                                      ;

  to   = ("number" === typeof to)                                                     ? Math.max(to, 0)        :
         ("string" === typeof to)                                                     ? Number(new Date(to))   :
         ("object" === typeof to && "date" === to.constructor.name.toLowerCase())     ? Number(to)             : Number(now)
  
  if("nan" === String(from).toLowerCase())  throw new Error("Error While Converting Date (first argument)" );
  if("nan" === String(to).toLowerCase())    throw new Error("Error While Converting Date (second argument)");
  
  diff = Math.abs(from - to);
  console.log(from,to,diff);
  return difference(diff);
}


/*
human_time_diff();                                // 0 seconds
human_time_diff("22:15:40");                      // ERROR (invalid date format)
human_time_diff(  Number(new Date()) - 2000  );   // 33 minutes, 20 seconds
human_time_diff( Number(new Date()) - 300 );      // 5 minutes
human_time_diff( Number(new Date()) - 300.123 );  // 5 minutes, 123 milliseconds
*/

Unix Snippet – Get File’s “Touch Date-Format” One-Liner

Pipelining the modification date from stat into an inline-date-phrase using the date --date="@... directive (using read to locate the pipeline’s output in the middle of the date-phrase),
And you’ve got yourself a nice little converter, which you can customise, to use *any* date-format,
I’ve using the Universal Time Coordinated standard.

file name:          my_file.txt
unix time-format:   1458036575     (Asia/Jerusalem)
touch time-format:  201603151009   (UTC)
stat --format=%Y "my_file.txt" | { read -r tmp_date; date --utc --date="@$tmp_date" "+%C%y%m%d%H%M"; }

*Ps.
You can easily use the touch-date-format with touch command using the following directive:
touch -t 201603151009 --time=mtime my_file.txt;
but make sure first you set the local-timezone to same timezone it was generated on.
TZ=UTC will help you do that,
TZ=UTC; touch -t 201603151009 --time=mtime my_file.txt; will apply it to the current line.
and you can also make sure of it is done right by using:
export TZ=UTC at the start of the script, and at the end of it just re-run
export TZ=Asia/Jerusalem (or whatever your TimeZone is) to come-back to your TimeZone,

Nice!