今日は1月31日。date コマンドで2か月前を表示しようとすると,11月30日ではなく12月1日になってしまう。date コマンドのバグだろうか。
月末に webalizer の棒グラフを見ると,「歯抜け」状態になることがある。大の月(1か月が31日ある月)の31日に起こる。このサイトでは,webalizer の処理を効率的に行うためにスクリプトを用意している(nlog(n): Webalizer のインクリメンタル設定を使い分ける)。このスクリプトに関係がありそうだ。
調べてみると,date コマンドが期待と違う値を返していることが分かった。1か月前と2か月前を表示してみると次のような結果になった。
今日は2006年1月31日なので,1か月前は2005年12月31日で正しい。しかし,2か月前が12月1日になってしまっている。11月30日になって欲しいのに。
スクリプトを書いて「がーっ」と走らせてみると,次のような結果になった。期待と違う箇所にピンクに色づけしてある。
指定 | 得られた値 | 期待する値 |
1 month ago | Dec 31, 2005 | Dec 31, 2005 |
2 month ago | Dec 1, 2005 | Nov 30, 2005 |
3 month ago | Oct 31, 2005 | Oct 31, 2005 |
4 month ago | Oct 1, 2005 | Sep 30, 2005 |
5 month ago | Aug 31, 2005 | Aug 31, 2005 |
6 month ago | Jul 31, 2005 | Jul 31, 2005 |
7 month ago | Jul 1, 2005 | Jun 30, 2005 |
8 month ago | May 31, 2005 | May 31, 2005 |
9 month ago | May 1, 2005 | Apr 30, 2005 |
10 month ago | Mar 31, 2005 | Mar 31, 2005 |
11 month ago | Mar 3, 2005 | Feb 28, 2005 |
12 month ago | Jan 31, 2005 | Jan 31, 2005 |
13 month ago | Dec 31, 2004 | Dec 31, 2004 |
14 month ago | Dec 1, 2004 | Nov 30, 2004 |
小の月の場合が,見事に期待と違う値が返ってきている。2月は特にひどい。2月28日のところが,3月3日になってしまっている。date コマンドのバグなのだろう。
考えてみれば,難しい点もあることは確かだ。12月30日の1か月前は11月30日で,12月31日の1か月前も11月30日にしなければならないからだ。バグの原因は,1か月の日数の適用方法だと想像できる(ソースが公開されているので読めば分かるのだろうが…)。例えば,12月30日の1か月前としては11月の日数である30日を引いて答を出しているのはいいのだが,12月31日の場合も30を引いてしまっているのがいけないのだ(だから12月1日になってしまう)。12月31日の場合は,12月の日数である31を引かなければならないのである。
難しいのは3月である。3月28日,29日,30日,31日の1か月前は全部2月28日にしなければならないからである。ここまで同じ日に集中してしまうことを考えると,3月31日の1か月前は3月3日でもいいか,という気分にもなってくる。完全に「バグだ」とは言いにくい一面もある。3月の1か月前が3月というのは困るけど…。今日が3月の何日であっても,1か月前は2月になって欲しい。上手く計算する方法はないだろうか?
OS は Vine Linux 3.2 で,date コマンドのバージョン (GNU sh-utils のバージョン) は 2.0.11 である。
これとは別の問題として,一番上の webalizer のグラフで4月と2月がちゃんと表示されてしまっている理由が分からないんだけれども。
2006年2月14日追記:
この動作は,仕様でありバグではないということが分かりました(うえちさんのコメントより)。オプションの指定方法によってこの問題は回避することができます。nlog(n): Webalizer のインクリメンタル設定を使い分ける のスクリプトの書き換えを行いました。期待通りの動作になりました。
Master Archive Index
Total Entry Count: 1957
30日で計算というわけではないようです。ソースを読んだわけではないですが、何故か tar のマニュアルhttp://www.gnu.org/software/tar/manual/html_chapter/tar_7.html#SEC115
に「The fuzz in units can cause problems with relative items. For example, `2003-07-31 -1
month' might evaluate to 2003-07-01, because 2003-06-31 is an invalid date.」とあります。
単純に月をマイナス(必要なら年も)した値から日付を計算しているのではないでしょうか。例えば
% date -d '2005-03-31 -1 month'
Thu Mar 3 00:00:00 JST 2005
% date -d '2005-02-31'
Thu Mar 3 00:00:00 JST 2005
となります。前月の年月がほしい場合は、上記のマニュアルにもありますが
date -d "$(date +%Y-%m-01) -1 month" +%Y%m
のような感じでしょうか。
Posted by: うえち at February 01, 2006 10:29なるほど! そうだったんですね。問題が解決しました。
Posted by: n at February 01, 2006 22:48「深夜25時は翌日の午前1時」に似ています。$(date +%Y-%m-01) という書式を初めて知りました。
ありがとうございました。