言語別に範囲外の日付のフォーマットに対する挙動を試した、あるいは言語別 Repl について
以前の記事にも書いたとおり、JavaScript の場合 new Date(2014,1,32)
のように、存在しない日付を指定してDateを初期化しても、よきに計らった(おせっかいな?)値を返してくれる。例えばこの例のように1月32日を指定すると、2月1日のDateオブジェクトが返ってくる。実は、存在しないような不正な入力に対しては Type Error が投げられると勝手に思い込んでいたので驚いた。
という経緯もあって、良い機会なので他のスクリプト言語で同様のことをしたときの挙動を調べることにした。この手の実験はReplを使うと便利なのでそのあたりの言及もする。対象言語は Perl, Ruby, Python, PHP あたりのよく使われているスクリプト言語を選んだ。先に結果を言ってしまうと、前者2つは日付のフォーマットチェックがあるが、他は JavaScript と同じような挙動だと分かった。具体的な挙動は以下に示す通り。
Perl
僕の知る限り Perl には標準でRepl環境が無い。とはいうものの、CPAN を用いて簡単にRepl環境を構築することが可能だ。今回は依存関係が少なくインストールの容易な Carp::Reply
を利用した。他に有名で補完機能などが充実したさらに高機能なものにDevel::REPL
が存在している。けれども、僕の環境では、その依存関係の多さからなのか、これまで一度もうまくインストールできたことがない…。
% perl -v This is perl 5, version 12, subversion 4 (v5.12.4) built for darwin-thread-multi-2level Copyright 1987-2010, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at http://www.perl.org/, the Perl Home Page. % reply 0> use Time::Local qw(timelocal); 1> timelocal(0,0,0, 32, 0, 2014) Day '32' out of range 1..31 at reply input line 1. 2> timelocal(0,0,0, 31, 1, 2014) Day '31' out of range 1..28 at reply input line 1.
見ての通りちゃんとフォーマットチェックが走る理想的な動作だ。もちろん、2月などの月の最終日が31日以外でもきちんとしたチェックになっている。
Ruby
結論から言えば、今回に限っての Ruby はドジっ子かわいい。Repl環境は標準搭載の irb を利用した。他に高機能なpryというReplがあるけれど、僕は使っていない。
% ruby -v ruby 2.0.0p247 (2013-06-27 revision 41674) [universal.x86_64-darwin13] % irb irb(main):001:0> Time.local(2014, 1, 32) ArgumentError: argument out of range from (irb):1:in `local' from (irb):1 from /usr/bin/irb:12:in `<main>' irb(main):002:0> Time.local(2014, 2, 31) => 2014-03-03 00:00:00 +0900
一応チェックはしているようだが、dd が 1..31 かどうかしかチェックしていないようである。ドジっ子である。
Python
PythonはCLIで引数無しで起動するとデフォルトでRepl環境になる。これは嬉しい。
% python Python 2.7.5 (default, Aug 25 2013, 00:04:04) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import time >>> time.mktime([2014,1,32,0,0,0,0,0,0]) 1391180400.0
特にチェックは走っていない。Rubyの挙動を見たあとだと、これはこれで割り切っていて自然な実装に思えてくる。例えば特定の日付から2ヶ月分取ってくるみたいなときに便利だ(86400ずつ UNIX TIME をインクリメントするのが正攻法だが)。
PHP
どこに行ってもお騒がせな感があるPHP。実はデフォルトで Repl 環境があることはあまり知られていない(かも?)。ただし、行末のセミコロンが必要だったり、返り値の表示がされなかったり、機能的にはアレである。
% php -v PHP 5.4.17 (cli) (built: Aug 25 2013 02:03:38) Copyright (c) 1997-2013 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies % php -a php > echo mktime(0, 0, 0, 1, 32, 2014); 1391180400
こちらも Python と同様の動作だ。
まとめ
Ruby しっかりしろ。