time要素にdatetime属性があったら、それを元に現在時刻からの相対日時を生成し、その内容を差し替えるスクリプトを書いていた。相対日時を得るには、ミリ秒数同士を引き算して1000で割って60以下なら~秒前、60で割って60以下なら~分前、60で割って24以下なら~時間前、24で割って30以下なら~日前、30で割って12以下なら~か月前、12で割って~年前というものをそのままコードにした。車輪だ。

const now = Date.now();
const toRelativeDate = function (then) {
  let diff = 0;

  if (!Number.isInteger(then)) {
    return;
  }

  diff = parseInt((now - then) / 1000, 10);

  if (diff < 0) {
    return;
  }

  if (diff < 60) {
    return diff + "秒前";
  }

  diff = parseInt(diff / 60, 10);

  if (diff < 60) {
    return diff + "分前";
  }

  diff = parseInt(diff / 60, 10);

  if (diff < 24) {
    return diff + "時間前";
  }

  diff = parseInt(diff / 24, 10);

  if (diff < 30) {
    return diff + "日前";
  }

  diff = parseInt(diff / 30, 10);

  if (diff < 12) {
    return diff + "か月前";
  }

  return parseInt(diff / 12, 10) + "年前";
};

Demo: Test Page for reldate.js

日本語だと複数形のことを考えなくて良いのでシンプルなコードになった。2年前以上は日時をそのまま表示したいという場合は、最初の方でdiffが一定数以上の時に抜けるようにしてやると良い。

タイムゾーンのことをまったく考えていない。Date.now()Date.parse()でISO-8601をパースした時はUTCのミリ秒になるはずなので、大丈夫なんじゃないかと楽観している。また相対時刻がそもそもあやふやなので、最大24時間ずれるくらい大したこともなさそうだ。このあたりも含めて確実にしたいならMoment.jsを使うべきだろう。そうすればmoment#toNow()などもあり、何も考える必要はなくなる。


ウェブサイト上で動かすスクリプトを昔のことは忘れて書き、Closure CompilerのJavaScriptバージョンでES5への変換と最小化を同時にやるようにし始めた。もちろんgyp依存とかでもないため、Windowsで普通に動く。いくつか小さなプロジェクトでも使ってみているが、UglifyJS2を差し替えるくらいの感覚で移行できている。ちょっと処理が遅く、最小化後のサイズも大きくはなるが、それを補って余りある簡便さだ。

規模の大きなJavaScriptコードを処理しようとするとメモリーを食い尽くすなど、パフォーマンスの点で問題がいくつかあるようだ。まだプロダクションで使うのは怖いが、小さいところではちょいちょい使っていきたい。