こんな感じのプログラムを作った。
日付の範囲を指定して、データベースからデータを取得するような処理だ。
入力情報である日付Fromと日付ToはdateCheckメソッドにてチェックされ、不正な日付の場合は代わりの日付が指定される仕組みだ。
/** データ取得処理 */ public List<Object> select(Connection conn, String dateFrom, String dateTo) { List<Object> result = new ArrayList<Object>(); dateFrom = dateCheck(dateFrom, "2000/01/01"); dateTo = dateCheck(dateTo, "2020/12/31"); String sql = "SELECT * FROM TARGET_TABLE " + " WHERE ENTRY_DATE BETWEEN TO_DATE(?, 'yyyy/mm/dd') AND TO_DATE(?, 'yyyy/mm/dd')"; PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); ps.setString(1, dateFrom); ps.setString(2, dateFrom); rs = ps.executeQuery(); // データ取得&リスト格納処理 // ... } catch (SQLException e) { } finally { // クローズ処理 } return result; } /** 日付チェック */ private String dateCheck(String dateString, String alternativeDateString) { String result = ""; DateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); sdf.setLenient(false); try { sdf.parse(dateString); result = dateString; } catch (ParseException e) { result = alternativeDateString; } return result; }
どこが問題かというと、「20001/1/1」みたいな、西暦年が5桁の日付を与えてみたところ、dateCheckをクリアしてしまった、というところだ。
どうやらSimpleDateFormatは西暦年が5桁でもしれっと処理してしまうらしい。
こんなプログラムも書いてみたが↓
public static void main(String[] args) throws ParseException { String s = "20001/1/1"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); sdf.setLenient(false); Date d = sdf.parse(s); System.out.println(d); }
コンソールには「Mon Jan 01 00:00:00 JST 20001」と表示された。
で、これだけならいいのだが、データベース(発見時はOracleを使用)が西暦年5桁は受け入れてくれなかったりする。すなわち、最初に例示した類のプログラムを実行すると
java.sql.SQLException: ORA-01861: リテラルが書式文字列と一致しません
になってしまう。
解決するためには、正規表現によるチェックか文字列の長さチェックでSimpleDateFormat#parseの穴を補うしかないかもしれない。また、値が画面から入力される類のものであるならば、その画面に入力制限をつけて不正な日付を入力させない、などの処置が必要になると思う。