pig's diary

何でも忘れるので万年初心者ね

_.niceDate・・・日付データを与えて「〜分前」を簡単に実装するunderscore.js 拡張

underscore.js は、誰かが「ビルトイン拡張しないPrototype.jsだ」って言っていました。実際、そうだと思います。
window._ に、いろいろユーティリティが詰まってるんです。(「 _ 」じゃなくも、できる)

// 配列が空かどうかしらべたい
_.isEmpty([]) // true
_([]).isEmpty() // true。同じことをしてる。ちょっとjQueryぽい


で、underscore.js は、jQuery みたいに拡張できます。プラグインが作れるんです。

// jQueryなら
$.fn.newPlugin = function () {...};

// underscore.js なら
_.mixin({
  myUtil: function () {...}
});

documentはこれです→ _.mixin


で、これ流行りますかね? underscore.js を拡張するやつ。
流行らないかも知れません。ほとんどの場合、拡張したところで、ただの関数だからです。


でも、サーバ用とかで、あれば便利なちょっとしたものはあるんじゃないでしょうか。
そういうものが、「_ 」に集まるのはいいことだと思います。
これ↓は自分用につくってみました。_.niceDate() という関数がつかえるように、拡張してます。よければ、使ってみてください。

// depends on underscore.js

/**
 * @param {String|Number|Date} date
 * @param {String=} lang 'ja' or 'en'. default is 'ja'
 * @return {String}
 */
_.mixin({
  niceDate: (function(date, lang) {
    var DAY, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR, dateFormat, formatize, langs, pluralize;
    SECOND = 1000;
    MINUTE = 60 * SECOND;
    HOUR = 60 * MINUTE;
    DAY = 24 * HOUR;
    WEEK = 7 * DAY;
    MONTH = 31 * DAY;
    YEAR = 365 * DAY;
    langs = {
      en: {
        AGO: ' ago',
        AFTER: ' after',
        SECOND: ' second',
        MINUTE: ' minute',
        HOUR: ' hour',
        DAY: ' day',
        WEEK: ' week',
        MONTH: ' month',
        YEAR: ' year',
        PLURAL: 's'
      },
      ja: {
        AGO: '前',
        AFTER: '後',
        SECOND: '秒',
        MINUTE: '分',
        HOUR: '時間',
        DAY: '日',
        WEEK: '週',
        MONTH: '月',
        YEAR: '年'
      }
    };
    dateFormat = {
      en: {
        HOUR_MINUTE: "%_:%_",
        MONTH_DATE: "%_.%_",
        YEAR_MONTH_DATE: "%_.%_.%_"
      },
      ja: {
        HOUR_MINUTE: "%_:%_",
        MONTH_DATE: ["%_", langs.ja.MONTH, "%_", langs.ja.DAY].join(''),
        YEAR_MONTH_DATE: ["%_", langs.ja.YEAR, "%_", langs.ja.MONTH, "%_", langs.ja.DAY].join('')
      }
    };
    pluralize = function(num, str, lang) {
      num = Math.floor(num);
      if (lang.PLURAL && num > 1) {
        return num + str + lang.PLURAL;
      } else {
        return num + str;
      }
    };
    formatize = function() {
      var format, value, values, _i, _len;
      format = arguments[0], values = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      for (_i = 0, _len = values.length; _i < _len; _i++) {
        value = values[_i];
        format = format.replace("%_", value);
      }
      return format;
    };
    return function(date, lang_) {
      var agoOrLater, dist, fmt, now;
      if (_.isString(date) || _.isNumber(date)) {
        date = new Date(date);
      }
      if (_.isDate(date)) {
        now = new Date().getTime();
        lang_ = _.isString(lang_) && langs[lang_] ? lang_ : "ja";
        lang = langs[lang_];
        fmt = dateFormat[lang_];
        dist = now - date.getTime();
        agoOrLater = dist > 0 ? lang.AGO : lang.AFTER;
        dist = Math.abs(dist);
        if (dist < MINUTE) {
          return "" + (pluralize(dist / SECOND, lang.SECOND, lang)) + agoOrLater;
        } else if (dist < HOUR) {
          return "" + (pluralize(dist / MINUTE, lang.MINUTE, lang)) + agoOrLater;
        } else if (dist < DAY) {
          return "" + (pluralize(dist / HOUR, lang.HOUR, lang)) + agoOrLater;
        } else if (dist < DAY * 3) {
          return "" + (pluralize(dist / DAY, lang.DAY, lang)) + agoOrLater;
        } else if (dist < YEAR) {
          return "" + (formatize(fmt.MONTH_DATE, date.getMonth() + 1, date.getDate()));
        } else {
          return "" + (formatize(fmt.YEAR_MONTH_DATE, date.getFullYear(), date.getMonth() + 1, date.getDate()));
        }
      }
    };
  })()
});


つかうとき:

// date フォーマットの文字列から
var string = "Sat Nov 05 14:25:39 +0000 2011";
_.niceDate(string); // "2時間前"
_.niceDate(string, 'en'); // "2 hours ago"

// time フォーマットの数値から
var number = new Date().getTime() + 3 * 1000;
_.niceDate(number); // 3秒後

// dateオブジェクトから
var date = new Date(new Date().getTime() - 2 * 24 * 60 * 60 * 1000);
_.niceDate(date); // 2日前