印刷用表示へ切り替え 通常表示へ切り替え 更新履歴を表示 更新履歴を隠す
Linux date コマンドのバグ?

今日は1月31日。date コマンドで2か月前を表示しようとすると,11月30日ではなく12月1日になってしまう。date コマンドのバグだろうか。

■ ■ ■
200601/usage-20060131.png
200601/usage-20060131.png


月末に webalizer の棒グラフを見ると,「歯抜け」状態になることがある。大の月(1か月が31日ある月)の31日に起こる。このサイトでは,webalizer の処理を効率的に行うためにスクリプトを用意している(nlog(n): Webalizer のインクリメンタル設定を使い分ける)。このスクリプトに関係がありそうだ。

調べてみると,date コマンドが期待と違う値を返していることが分かった。1か月前と2か月前を表示してみると次のような結果になった。

$ date -d '1 month ago'
Sat Dec 31 23:15:06 JST 2005
$ date -d '2 month ago'
Thu Dec  1 23:15:07 JST 2005

今日は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 である。

$ date --version
date (GNU sh-utils) 2.0.11
Written by David MacKenzie.

Copyright (C) 2000 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

これとは別の問題として,一番上の webalizer のグラフで4月と2月がちゃんと表示されてしまっている理由が分からないんだけれども。

2006年2月14日追記:
この動作は,仕様でありバグではないということが分かりました(うえちさんのコメントより)。オプションの指定方法によってこの問題は回避することができます。nlog(n): Webalizer のインクリメンタル設定を使い分ける のスクリプトの書き換えを行いました。期待通りの動作になりました。

Posted by n at 2006-01-31 23:45 | Edit | Comments (2) | Trackback(0)
Trackbacks

  • 「手違いで複数トラックバックを送ってしまった!」という場合でも気にしないでください (重複分はこちらで勝手に削除させていただきます)
  • タイムアウトエラーは,こちらのサーバの処理能力不足が原因です (詳細は トラックバック送信時のエラー をご覧ください)
  • トラックバックする記事には,この記事へのリンクを含めてください(詳細は 迷惑トラックバック対策 をご覧ください)
Comments

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

なるほど! そうだったんですね。問題が解決しました。
「深夜25時は翌日の午前1時」に似ています。$(date +%Y-%m-01) という書式を初めて知りました。
ありがとうございました。

Posted by: n at February 01, 2006 22:48
Post a comment
  • 電子メールアドレスは必須ですが,表示されません (気になる場合は「メールアドレスのような」文字列でもOKです)
  • URL を入力した場合はリンクが張られます
  • コメント欄内ではタグは使えません
  • コメント欄内に URL を記入した場合は自動的にリンクに変換されます
  • コメント欄内の改行はそのまま改行となります
  • 「Confirmation Code」に表示されている数字を入力してください (迷惑コメント対策です)


(必須, 表示されます)


(必須, 表示されません)


(任意, リンクされます)


Confirmation Code (必須)


Remember info (R)?