Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

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

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

ソースコード

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)
            }
        };
    }
});

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

            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> 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)

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away