MongoDBにおけるISODateの実装を調べてみた

> ISODate;
function (isoDateStr) {
    if (!isoDateStr) {
        return new Date;
    }
    var isoDateRegex = /(\d{4})-?(\d{2})-?(\d{2})([T ](\d{2})(:?(\d{2})(:?(\d{2}(\.\d+)?))?)?(Z|([+-])(\d{2}):?(\d{2})?)?)?/;
    var res = isoDateRegex.exec(isoDateStr);
    if (!res) {
        throw "invalid ISO date";
    }
    var year = parseInt(res[1], 10) || 1970;
    var month = (parseInt(res[2], 10) || 1) - 1;
    var date = parseInt(res[3], 10) || 0;
    var hour = parseInt(res[5], 10) || 0;
    var min = parseInt(res[7], 10) || 0;
    var sec = parseFloat(res[9]) || 0;
    var ms = Math.round(sec % 1 * 1000);
    sec -= ms / 1000;
    var time = Date.UTC(year, month, date, hour, min, sec, ms);
    if (res[11] && res[11] != "Z") {
        var ofs = 0;
        ofs += (parseInt(res[13], 10) || 0) * 60 * 60 * 1000;
        ofs += (parseInt(res[14], 10) || 0) * 60 * 1000;
        if (res[12] == "+") {
            ofs *= -1;
        }
        time += ofs;
    }
    return new Date(time);
}
最後がreturn new Date(time)となっているので、これはつまりDateオブジェクトを生成するただのユーティリティ関数なんですね。

ISODateの呼び出し方法

ISODate関数を使ってオブジェクトを生成するには、次のようなルールがあることがわかります。

  • 年月日の間のハイフン"-"は、あってもなくても同じ。
  • 年月日と時間を区切るのは、"T"または" "どちらもでよい。
  • 自分秒を区切る":"は、あってもなくても同じ
  • ミリ秒を表現する場合は、秒の後に".123"のように書く。
  • 日本標準時でオブジェクト生成する場合は、最後に'+09:00'と書く。
つまり、下記の書き方はどれも等価になります。

# 日付と時間の区切りはT,空白のどちらか (区切りなしはダメ)

ISODate('2011-12-23T04:41:00') 
ISODate('2011-12-23 04:41:00')

# 年月日の区切りはあってもなくても同じ

ISODate('2011-1223 04:41:00');
ISODate('201112-23 04:41:00');
ISODate('20111223 04:41:00');  

# 時分秒の区切りはあってもなくても同じ

ISODate('2011-12-23 0441:00');
ISODate('2011-12-23 04:4100');
ISODate('2011-12-23 044100');

# 区切りを全部省略できる

ISODate('20111223 044100');

# ミリ秒を表現する場合はこう (ただし有意なのは1ms単位まで)

ISODate('20111223 044100.000000');

# 日本標準時の表現でオブジェクト生成する

ISODate('2011-12-23 13:41:00+09:00');

世界標準時と日本標準時について

下記の2つの表現が等価であると覚えておけばよいでしょう。

ISODate('2012-01-01 00:00:00')
ISODate('2012-01-01 09:00:00+09:00')

実際、MongoShellで比較してみると同じ値であることがわかります。
> var t1 = ISODate('2012-01-01 00:00:00');
> var t2 = ISODate('2012-01-01 09:00:00+09:00');
> t1.toString() === t2.toString();
true

イギリス人が「ハッピーニューイヤー」の花火を打ち上げている瞬間に、日本人が元旦朝9時のお雑煮を食べている絵を想像すると、 覚えやすいと思います。

まとめ

MongoDBのISODateの実体について掘り下げてみました。
MongoShellを直接叩くとき、このような知識があるとちょっと便利ですね。
カテゴリ: