DBFlute: ConditionBeanの日付範囲検索の妙技


DBFluteは現場で困ったことを解決するのに注力しています。

例えば、売上一覧検索画面で「計上日が07月06日から07月15日まで」という条件で検索したい場合:

通常、人間が「07月06日から7月15日まで」と言った場合07月15日を丸々含みます。
また、画面に「FROMの日付」と「TOの日付」の入力コントロールがあって、
運用者は以下のように入力をして上記の条件を期待します。(大抵はこうだと思います)
= = = = = = = = = = = = = =
「FROMの日付」 → 07月06日
「TOの日付」 → 07月15日
= = = = = = = = = = = = = =

すると、サーバ側に飛んでくる値は、以下のようになります。
= = = = = = = = = = = = = = = = = = = =
FROM → 07月06日00時00分00秒000ミリ秒
TO → 07月15日00時00分00秒000ミリ秒
= = = = = = = = = = = = = = = = = = = =
なので、これをそのまま 「計上日 >= FROM and 計上日 <= TO」とすると
最終日の「07月15日00時00分01秒」以降のデータが検索されません。
最終日を含めるためには、以下のどちらかを施さなければなりません。
A. 計上日 <= 「07月15日00時00分00秒000ミリ秒」 → 計上日 <= 「07月15日23時59分59秒999ミリ秒」
B. 計上日 <= 「07月15日00時00分00秒000ミリ秒」 → 計上日 < 「07月16日00時00分00秒000ミリ秒」

実はこういうBUGは世にたくさんあるのです。
やり方の不統一、プログラマの実装忘れ、日付操作ミス、
そもそもプロジェクト全体でこういった処理を全く忘れているなどなど。
また、ミリ秒の扱いはDBによってかなり微妙です。「A」の方法は結構危険です

DBFluteではConditionBeanの以下のように、ミスの無い様に実装します。

/- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Date fromDate = [画面入力値そのまま] → 07月06日00時00分00秒000ミリ秒
Date toDate = [画面入力値そのまま] → 07月15日00時00分00秒000ミリ秒
UriageCB cb = new UriageCB();
cb.query().setKeijoDate_FromTo(fromDate, toDate, new FromToOption().compareAsDate());// ☆Point!
List ls = uriageBhv.selectList(cb);
- - - - - - - - -/

↓↓↓

where 計上日 >= [07月06日00時00分00秒000ミリ秒] and 計上日 < 07月16日00時00分00秒000ミリ秒

TOの日付の操作をDBFluteが解決して条件を設定します。

正確には、compareAsDate()は「日付として比較する」という意味のOptionで
FROMの時分秒を削ぎ落として GreaterEqual
TOを一日足して時分秒を削ぎ落として LessThan
という動作を行います。


こないだのSeasarカンファレンスで出羽さんが発表した内容の中に上記の機能が紹介されています。
とても評判の良いセッションだったようです。取り上げて頂き本当にありがとうございます。
http://event.seasarfoundation.org/sc2007spring/viewAttachment.do?_pageName_=Session&_fileName_=sc2007spring_S5_TIPS.pdf



【参考】
http://dbflute.sandbox.seasar.org/ja/tips-condition_bean_comparison.html

compareAsDate()以外にcompareAsMonth()/compareAsYear()/compareAsHour()など予定していますが、
今のところ必要に迫られることがないため、まだ未実装です。
(デフォルトcompareAsDate()でも良かったのかな???)