以前のエントリ「String.format」でやりたいと思っていた、書式指定子のサポートをとりあえずやってみた。
こちらから。
元々.NET Frameworkの劣化コピー的な発想なので、DateとNumberに書式指定可能なformatメソッドを追加し、String.formatのプレースホルダに指定した書式指定子をそのまま各クラスのformatに渡すように考えた。
Dateはdateformat.jsを使わせてもらえば楽だったんだけど、クリエイティブ・コモンズのライセンス適用範囲に不安があったので結局自前で実装することにした。 ただし、書式指定文字のエスケープができない、パーサの実装がないなどの面であきらかにdateformat.jsより劣っているのでorz、たいていはそちらを使ったほうがよいと思われる。 (あと、組み込みオブジェクトの拡張が嫌いな場合とか)
なくても実装は可能だったが、prototype.jsを前提に実装したため、prototype.jsを必要とする。完全に確認はしていないが、1.4.0以降なら問題ないはず。
後述の通り、Date#format、Number#formatの実装に加え、String.formatも書式指定子サポートを追加して実装しなおした。
Date、Numberとも似たようなことをするので、formatメソッドはFormattableオブジェクトに実装し、Date.prototoyep、Number.prototypeへ重ねるようにした。 また、書式指定子の定義をそれぞれのコンストラクタ関数のメンバ「formatters」として定義し、formatメソッド内からthis.constructor経由で参照させている。 具体的な実装は以下の通り。
var Formattable = {
defaultFormat : "",
format : function(format) {
var formatters = this.constructor.formatters || [];
return formatters.inject( format || this.defaultFormat, function(result, formatter) {
return result.replace( formatter.pattern, function() {
return formatter.format.apply( this, [ this ].concat( $A( arguments ) ) );
}.bind( this ) );
}.bind( this ) );
}
}
pattern(RegExp)プロパティとformatメソッドをメンバに持つオブジェクトを定義した。
formatメソッドの第一引数はフォーマット対象の値、第二引数以降にpatternプロパティの正規表現でexecした結果が渡される。例えばDate向けの年フォーマットは
{
pattern : /yyyy|yy|y/,
format : function(value, pattern) {
return pattern.length == 1 ?
value.getFullYear().toString() :
( "0000" + value.getFullYear() ).slice(-1 * pattern.length);
}
}
と定義してあり、formatの第二引数patternに一致文字列全体が渡されるので、1文字かそれ以上かを判断して0詰を行うかそのままtoStringするかなどの分岐をしている。
Date、Numberとも同一の設計のフォーマット情報を配列で持たせ、それをループ処理で適用している。
dateformat.jsに酷似しているが、ミリ秒の指定方法と解釈が若干違う
| 書式指定子 | 説明 |
|---|---|
| yyyy、yy、y | 年の指定。yyyyは4桁、yyは下2桁、yは桁詰なしになる。 |
| MM、M | 月の指定。MMは2桁、Mは桁詰なしになる。 |
| dd、d | 日の指定。MMは2桁、Mは桁詰なしになる。 |
| HH、H | 時の指定。HHは2桁、Hは桁詰なしになる。常に24時間書式になる。 |
| mm、m | 分の指定。mmは2桁、mは桁詰なしになる。 |
| ss、s | 秒の指定。ssは2桁、sは桁詰なしになる。 |
| fff、ff、f |
セコメントをする