JavaScript のDateオブジェクトにフォーマット関数 と 加減算の関数を追加

日付のフォーマット関数と、MySQLのDATE_ADDに似せた、日付の加減算を行う関数です。
フォーマット関数はともかくとして、日付関数をわざわざ作ったのは、Javascriptの日付の加減算はタイムゾーンの変換の関係か、バグがあるので、ちょっと信用できないので作り直しました。

Javascriptの``Object.defineProperty(Object.prototype, 'name', function)``が既存クラスをカスタマイズできて面白いので、``Date()``に直接追加するライブラリにしてみました。

```Javascript:formatサンプル
var objDate = new Date();
objDate.format(); //"2016-12-08 00:08:39.873"
objDate.format('yyyy-MM-dd (ddd) hh-mm:ss'); //"2016-12-08 (木) 00:08:39"
objDate.format('yyyy-MM-dd (dddd) hh-mm:ss'); //"2016-12-08 (木曜) 00:08:39"
objDate.format('yyyy-MM-dd (ddddd) hh-mm:ss'); //"2016-12-08 (木曜日) 00:08:39"
```

```Javascript:addサンプル
var objDate = new Date();
objDate.format(); //"2016-12-08 00:08:39.873"
objDate.add(1, 'y').format(); //"2017-12-08 00:08:39.873"
objDate.add(1, 'M').format(); //"2017-01-08 00:08:39.873"
objDate.add(1, 'd').format(); //"2016-12-09 00:08:39.873"
objDate.add(1, 'h').format(); //"2016-12-08 01:08:39.873"
objDate.add(1, 'm').format(); //"2016-12-08 00:09:39.873"
objDate.add(1, 's').format(); //"2016-12-08 00:08:40.873"
```

ソースコード

```Javascript:add_date.js
Object.defineProperty(Date.prototype, 'format', {
	get: function () {
		return function (str_format) {
			var f = str_format;
			var o = this;
			if (!f) f = 'yyyy-MM-dd hh:mm:ss.SSS';
			var week = ["日","月","火","水","木","金","土"];
			f = f.replace(/yyyy/g, o.getFullYear());
			f = f.replace(/MM/g, ('0' + (o.getMonth() + 1)).slice(-2));
			f = f.replace(/hh/g, ('0' + o.getHours()).slice(-2));
			f = f.replace(/mm/g, ('0' + o.getMinutes()).slice(-2));
			f = f.replace(/ss/g, ('0' + o.getSeconds()).slice(-2));
			f = f.replace(/ddddd/g, week[o.getDay()] + "曜日");
			f = f.replace(/dddd/g, week[o.getDay()] + "曜");
			f = f.replace(/ddd/g, week[o.getDay()]);
			f = f.replace(/dd/g, ('0' + o.getDate()).slice(-2));
			if (f.match(/S/g)) {
				var milliSeconds = ('00' + o.getMilliseconds()).slice(-3);
				var length = f.match(/S/g).length;
				for (var i = 0; i < length; i++) f = f.replace(/S/, milliSeconds.substring(i, i + 1));
			}
			return f;
		};
	}
});

Object.defineProperty(Date.prototype, 'add', {
	get: function () {
		return function (interval, str_target) {

			var t = str_target.toLowerCase();
			var o = this;

			if(t=='y' || str_target=='M'){
				var y, M, d;
				y = o.getFullYear();
				M = o.getMonth(); /* between 0 to 11 */
				d = o.getDate();

				if(t=='y'){/*Year*/
					y += interval;
				}
				else{ /*Month*/
					M += interval;
					y += Math.floor(M / 12);
					M = M % 12;
				}
				var max = [31, 28 + (M == 1 && y % 4 == 0 && (y % 100!=0 || y % 400==0)), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
				if(d > max[M]){ d = max[M]}

				return new Date(
					y + '-' +
					('0' + (M + 1)).slice(-2) + '-' +
					('0' + d).slice(-2) + ' ' +
					('0' + o.getHours()).slice(-2) + ':' +
					('0' + o.getMinutes()).slice(-2) + ':' +
					('0' + o.getSeconds()).slice(-2) + '.' +
					('00' + o.getMilliseconds()).slice(-3)
				);
			}

			else if(t=='d'){/*Day*/
				return new Date(o.getTime() + interval * 86400000);
			}
			else if(t=='h'){/*Hour*/
				return new Date(o.getTime() + interval * 3600000);
			}
			else if(str_target=='m'){/*Minute*/
				return new Date(o.getTime() + interval * 60000);
			}
			else if(t=='s'){/*Second*/
				return new Date(o.getTime() + interval * 1000);
			}
			else {
				return new Date(o)
			}
		};
	}
});
```

年と月の計算の部分は、最初はグレゴリオ暦の日数取得の公式を使って計算しようとしていました。

```Javascript:
			if(t=='y' || str_target=='M'){
				var y, Y, M,MM, d, before_days, after_days;
				y = this.getFullYear();
				M = this.getMonth(); /* between 0 to 11 */
				d = this.getDate();
				
				/*days from DC1 */
				before_days = 365 * y + ~~(30.6 * (M + 2)) - 428 + ~~(y / 4) - ~~(y / 100) + ~~(y / 400) + d;

				if(t=='y'){/*Year*/
					y += interval;
				}
				else{ /*Month*/
					M += interval;
					y += ~~(M / 12);
					M = M % 12;
				}
				after_days = 365 * y + ~~(30.6 * (M + 2)) - 428 + ~~(y / 4) - ~~(y / 100) + ~~(y / 400) + d;

				/* incremented - before days */
				after_days -= before_days;

				return new Date(this.getTime() + after_days * 86400000);
			}
```

しかし、これを使うと、Javascriptオリジナルの月の加減算と同じく、日がその月の最大日数から溢れた場合等におかしな挙動を起します。
``2016-01-31``
1ヶ月加算 
``2016-03-01``

普通の月の加減算でこんな結果を望む人はいない・・・

MySQLの月の加減算はこんな感じ。

```MySQL:
mysql> select DATE_ADD('2016-01-31', interval 1 month);
+------------------------------------------+
| DATE_ADD('2016-01-31', interval 1 month) |
+------------------------------------------+
| 2016-02-29                               |
+------------------------------------------+
1 row in set (0.00 sec)
```

うん。素直。これですよね。