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
*/

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

Code Ripped From WordPress Trunk And Modified To Not Requiring Translator Objects (Use Plain en_US Default).


  // Constants for expressing human-readable intervals
  // in their respective number of seconds.
  define('MINUTE_IN_SECONDS', 60);
  define('HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS);
  define('DAY_IN_SECONDS', 24 * HOUR_IN_SECONDS);
  define('WEEK_IN_SECONDS', 7 * DAY_IN_SECONDS);
  define('YEAR_IN_SECONDS', 365 * DAY_IN_SECONDS);


  /**
   * Retrieve the plural or single form based on the supplied amount.
   *
   * @param string $single The text that will be used if $number is 1.
   * @param string $plural The text that will be used if $number is not 1.
   * @param int    $number The number to compare against to use either $single or $plural.
   *
   * @return string Either $single or $plural translated text.
   */
  function _n($single, $plural, $number) {
    return 1 === $number ? $single : $plural;
  }

  /**
   * Determines the difference between two timestamps.
   *
   * The difference is returned in a human readable format such as "1 hour",
   * "5 mins", "2 days".
   *
   * @param int|string $from Unix timestamp from which the difference begins.
   * @param int|string $to   Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
   *
   * @return string Human readable time difference.
   */
  function human_time_diff($from, $to = '') {
    if (empty($to)) {
      $to = time();
    }


    $diff = (int)abs($to - $from);

    if ($diff < HOUR_IN_SECONDS) {
      $mins = round($diff / MINUTE_IN_SECONDS);
      if ($mins <= 1)
        $mins = 1;
      /* translators: min=minute */
      $since = sprintf(_n('%s min', '%s mins', $mins), $mins);
    }
    elseif ($diff < DAY_IN_SECONDS && $diff >= HOUR_IN_SECONDS) {
      $hours = round($diff / HOUR_IN_SECONDS);
      if ($hours <= 1)
        $hours = 1;
      $since = sprintf(_n('%s hour', '%s hours', $hours), $hours);
    }
    elseif ($diff < WEEK_IN_SECONDS && $diff >= DAY_IN_SECONDS) {
      $days = round($diff / DAY_IN_SECONDS);
      if ($days <= 1)
        $days = 1;
      $since = sprintf(_n('%s day', '%s days', $days), $days);
    }
    elseif ($diff < 30 * DAY_IN_SECONDS && $diff >= WEEK_IN_SECONDS) {
      $weeks = round($diff / WEEK_IN_SECONDS);
      if ($weeks <= 1)
        $weeks = 1;
      $since = sprintf(_n('%s week', '%s weeks', $weeks), $weeks);
    }
    elseif ($diff < YEAR_IN_SECONDS && $diff >= 30 * DAY_IN_SECONDS) {
      $months = round($diff / (30 * DAY_IN_SECONDS));
      if ($months <= 1)
        $months = 1;
      $since = sprintf(_n('%s month', '%s months', $months), $months);
    }
    elseif ($diff >= YEAR_IN_SECONDS) {
      $years = round($diff / YEAR_IN_SECONDS);
      if ($years <= 1)
        $years = 1;
      $since = sprintf(_n('%s year', '%s years', $years), $years);
    }

    if ($to - $from > 0) {
      $since = 'was ' . $since . ' ago';
    }
    elseif ($to - $from < 0) {
      $since = 'will be in ' . $since;
    }

    return $since;

  }

Try it using the output of time()-9000 for "was 3 hours ago", or time()+9000 for "will be in 3 hours".