日時の汎用的なFromTo条件

概要

基本概念

日時の汎用的なFromTo条件を設定します。FromTo は、絞り込み条件を表す ConditionKey です。

内部的に GreaterEqual や LessThan などを利用して日時の範囲条件(foo から bar まで)を表現することができます。 FromTo を利用しないと実現できない検索はありません。業務上よく利用される条件の組み合わせを汎用的に実現したものです。

会話上では、ふろむとぅ と表現します。

実装方法

実装の流れ

query() の後、set[date-column-name]_FromTo() を呼び出し、第一引数に from に相当する条件値、第二引数に to に相当する条件値、第三引数に FromToOption (必須)を指定します。

e.g. FromTo条件の実装手順 (Eclipseでコード補完) {FORMALIZED_DATETIME} @Java
Date fromDate = ...
Date toDate = ...
cb.q // .q と打って enter
--
cb.query()
--
// 1. .set まで打つとカラム選択
// 2. FD (FormalizedDatetime) でカラム確定
// 3. FT (FromTo) で enter
cb.query().setFDFT
--
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate, option);
--
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsMonth());

パターンの比較条件

指定された条件値の精度を調整して、パターン化された範囲条件を組み立てることができます。 これらの機能でなくても同じことはできますが、日付操作のちょっとしたバグの抑制や方式の統一に有効です。

compareAsDate()
日付による範囲条件 (yyyy/MM/dd)
compareAsMonth()
年月による範囲条件 (yyyy/MM)
compareAsYear()
年による範囲条件 (yyyy)
compareAsHour()
日付+時による範囲条件 (yyyy/MM/dd HH)
compareAsQuarterOfYear()
四半期による範囲条件 (デフォルトは1,4,7,10)
compareAsWeek()
週による範囲条件 (デフォルトは日曜始まり)

日付による範囲条件 (yyyy/MM/dd)

compareAsDate() を呼び出すと、時分秒は考慮せずに指定された条件値の日付の範囲内であるかどうかを判定する条件を組み立てます。 指定された toDate の条件値の時分秒が幾つであろうと、次の日に進めて LessThan を利用し、該当の日のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の時分秒は切り捨てられます)

e.g. 日付で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2007/04/10 08:24:53
Date toDate = ... // 2007/04/16 14:36:29
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsDate());
e.g. 日付で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/04/10 00:00:00'
   and FORMALIZED_DATETIME < '2007/04/17 00:00:00'

FromTo の中で、利用頻度の一番高いものと想定されています。このパターンに限定した独立した ConditionKey、DateFromTo が存在し、アプリで複数人・複数箇所で利用するような場合は、オプション指定の不要なそちらのメソッドを利用した方が良いでしょう。

一日の始まりが朝5時とか

もし、一日の始まりを朝5時とかにしたければ、beginDay_...()と組み合わせると良いでしょう。

今日とか昨日とか

もし、現在日時を fromDate と toDate に指定すれば、"今日" という範囲条件になります。 from と to で同じインスタンスを日付を指定も特に問題はありません。(調整されるのは内部でコピー生成された日付インスタンスなので)

e.g. "今日" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/09 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsDate());
// -> today (2012/12/09) is target

もし、"昨日" という範囲条件にしたければ、moveTo...()と組み合わせると良いでしょう。

e.g. "昨日" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/09 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsDate().moveToScope(-1));
// -> yesterday (2012/12/08) is target

年月による範囲条件 (yyyy/MM)

compareAsMonth() を呼び出すと、日および時分秒は考慮せずに指定された条件値の日付の範囲内であるかどうかを判定する条件が組み立てられます。 指定された toDate の条件値の日および時分秒が幾つであろうと、次の月の最初の日に進めて LessThan を利用し、該当の月のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の日および時分秒は切り捨てられます)

e.g. 月で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2007/04/10 08:24:53
Date toDate = ... // 2007/04/16 14:36:29
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsMonth());
e.g. 月で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/04/01 00:00:00'
   and FORMALIZED_DATETIME < '2007/05/01 00:00:00'

年による範囲条件 (yyyy)

compareAsYear() を呼び出すと、月日および時分秒は考慮せずに指定された条件値の日付の範囲内であるかどうかを判定する条件が組み立てられます。 指定された toDate の条件値の月日および時分秒が幾つであろうと、次の年の最初の日に進めて LessThan を利用し、該当の年のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の月日および時分秒は切り捨てられます)

e.g. 年で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2007/04/10 08:24:53
Date toDate = ... // 2007/04/16 14:36:29
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsYear());
e.g. 年で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/01/01 00:00:00'
   and FORMALIZED_DATETIME < '2008/01/01 00:00:00'

もし、年を4月始まりとかにしたければ、beginYear_...()と組み合わせると良いでしょう。

日付+時による範囲条件 (yyyy/MM/dd HH)

compareAsHour() を呼び出すと、分秒は考慮せずに指定された条件値の日付と時間(hour)の範囲内であるかどうかを判定する条件を組み立てます。 指定された toDate の条件値の分秒が幾つであろうと、次の時間(hour)に進めて LessThan を利用し、該当の時間(hour)のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の分秒は切り捨てられます)

e.g. 日付と時間(hour)で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2007/04/10 08:24:53
Date toDate = ... // 2007/04/16 14:36:29
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsHour());
e.g. 日付と時間(hour)で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/04/10 08:00:00'
   and FORMALIZED_DATETIME < '2007/04/16 15:00:00'

四半期による範囲条件 (デフォルトは1,4,7,10)

compareAsQuaterOfYear() を呼び出すと、日付と時分秒は考慮せずに指定された条件値の年月を含む期(四半期の期)の範囲内であるかどうかを判定する条件を組み立てます。 指定された toDate の条件値の日付と時分秒が幾つであろうと、次の四半期の最初の日時に進めて LessThan を利用し、該当の四半期のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の日付と時分秒は切り捨てられ、月もその四半期の最初の月になります)

e.g. 四半期(1,4,7,10)で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2007/01/10 08:24:53
Date toDate = ... // 2007/05/16 14:36:29
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsQuaterOfYear());
e.g. 四半期(1,4,7,10)で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/01/01 00:00:00'
   and FORMALIZED_DATETIME < '2007/07/01 00:00:00'

デフォルトでは、"1,4,7,10" という区切りになっていますが、 もし "2,5,8,11" という区切りとかにしたければ、beginYear_...()と組み合わせると良いでしょう。

週による範囲条件 (デフォルトは日曜始まり)

compareAsWeek() を呼び出すと、時分秒は考慮せずに指定された条件値の日付を含む週の範囲内であるかどうかを判定する条件を組み立てます。 指定された toDate の条件値の時分秒が幾つであろうと、次の週の最初の日時に進めて LessThan を利用し、該当の週のデータが完全に含まれるように自動的に調整されます。 (同時に fromDate の時分秒は切り捨てられ、日付もその週の最初の日になります)

e.g. 週(日曜始まり)で FromTo {FORMALIZED_DATETIME} @Java
Date fromDate = ... // 2012/12/10 08:24:53 (Monday)
Date toDate = ... // 2012/12/20 14:36:29 (Thursday)
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().compareAsWeek());
e.g. 週(日曜始まり)で FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2012/12/09 00:00:00'
   and FORMALIZED_DATETIME < '2012/12/23 00:00:00'

週の始まりが月曜とか

デフォルトでは、日曜始まりになっていますが、 もし月曜始まりとかにしたければ、beginWeek_...()と組み合わせると良いでしょう。 また、そもそも週の始まりという概念は一般的に曖昧なものなので、デフォルトと同じ日曜始まりであっても、 明示的に週の始まりが何曜日なのかを指定しておくのも良いでしょう。

今週とか先週とか

もし、現在日時を fromDate と toDate に指定すれば、"今週" という範囲条件になります。 from と to で同じインスタンスを日付を指定も特に問題はありません。(調整されるのは内部でコピー生成された日付インスタンスなので)

e.g. "こんしゅう" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/12 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsWeek());
// -> this week (2012/12/09 - 15) are target

もし、"先週" という範囲条件にしたければ、moveTo...()と組み合わせると良いでしょう。

e.g. "先週" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/12 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsWeek().moveToScope(-1));
// -> last week (2012/12/02 - 08) are target

始まりの基準を調整

例えば、年の始まりを 4月からにして範囲条件したいというとき、FromToOptionにその4月始まりの基準を指定して、スライドした範囲条件にすることができます。

e.g. 2007年度の FromTo {FORMALIZED_DATETIME} @Java
Date targetYear = ... // 2007/01/01
cb.query().setFormalizedDatetime_FromTo(targetYear, targetYear
        , new FromToOption().compareAsYear().beginYear_Month04_April());
e.g. 2007年度の FromTo {FORMALIZED_DATETIME} @DisplaySql
...
 where FORMALIZED_DATETIME >= '2007/04/01 00:00:00'
   and FORMALIZED_DATETIME < '2008/04/01 00:00:00'

様々な、単位で基準の調整が用意されています。

beginDay_Hour(dayBeginHour)
  • 一日の始まりを調整 (朝6時からなど)
  • dayBeginHourはint型で時間指定(1-23)。Date型指定は、そのDateのHour部分が設定される
  • beginDay_PreviousHour() で "前日の夜22時から" という設定もできる
  • 日付を意識する全ての範囲条件パターンと組み合わせての利用が想定される
beginMonth_Day(monthBeginDay)
  • 月の始まりを調整 (3日からなど)
  • monthBeginDayはint型で日付指定(1-31)。Date型指定は、そのDateのDay部分が設定される
  • beginMonth_PreviousDay() で "前月の25日から" という設定もできる
  • compareAsYear(), compareAsQuarterOfYear(), compareAsMonth() と組み合わせての利用が想定される
beginYear_Month(yearBeginMonth)
  • 年の始まりを調整 (4月からなど)
  • yearBeginMonthはint型で日付指定(1-12)。Date型指定は、そのDateのMonth部分が設定される
  • beginYear_PreviousMonth() で "前年の11月から" という設定もできる
  • beginYear_Month01_January() というようにメソッド名解決もできる
  • compareAsYear(), compareAsQuarterOfYear() と組み合わせての利用が想定される
beginWeek_DayOfWeek(weekBeginDayOfWeek)
  • 週の始まりを調整 (月曜からなど)
  • weekBeginDayOfWeekはDate型で、そのDateが示す日の曜日が設定される
  • beginWeek_DayOfWeek2nd_Monday() というようにメソッド名解決の方が実用的である
  • compareAsWeek() と組み合わせての利用が想定される
  • 一応デフォルトは日曜だが、わかりやすさのために必ず明示的に指定する方がいい

何かの集計処理をするときなど、通常の日付や時間の区切りではなく、業務的な区切りで範囲条件するときに有効です。 同時に組み合わせることもでき、極端な話、年の始まりが "3月4日朝5時から" というのも可能です。

スコープを移動

今日や今週ではなく昨日や先週を範囲条件にしたい場合、現在日時を手動で調整すれば実現できますが、 moveToScope()メソッドを使うことで簡単に範囲条件を移動させることができます。

例えば、"今日" という条件になっているFromToに moveToScope(-1) と指定すれば "昨日" という条件になります。もし、"今週" という条件になっている場合は "先週" になります。 moveToScope() は、そのとき指定されている Scope (Date, Year, Week, ...) に合わせて指定された数の分だけ移動します。

e.g. "昨日" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/09 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsDate().moveToScope(-1));
// -> yesterday (2012/12/08) is target
e.g. "先週" で FromTo {FORMALIZED_DATETIME} @Java
Date currentDate = ... // 2012/12/12 21:10:53
cb.query().setFormalizedDatetime_FromTo(currentDate, currentDate
        , new FromToOption().compareAsWeek().moveToScope(-1));
// -> last week (2012/12/02 - 08) are target

ただし、二週間の範囲条件だとしても、+1 や -1 で移動するのは一週間です。 (範囲条件の範囲の分、移動する方がいいかも...と後から思っています)

手動で比較条件を調整

用意されているパターンの比較条件に合致しない場合は、手動で比較条件を調整することができます。

何もオプションが設定されていない FromToOption を指定した場合は、指定された条件値そのままに対して、単なる GreaterEqual と LessEqual を利用した範囲検索になります。Equal を Than に変えたい場合は、greaterThan(), lessThan() を呼び出すことで変更できます。

e.g. from を GreaterThan に変更して FromTo {FORMALIZED_DATETIME} @Java
cb.query().setFormalizedDatetime_FromTo(fromDate, toDate
        , new FromToOption().greaterThan());

さらに、FromToOption には日付を操作する幾つかの便利メソッドが用意されています。 これらを活用して、業務にフィットする FromTo 条件を作り上げると良いでしょう。

fromPattern[Hour/Day/Month/...]Just()
fromDate を、その時間や日や月などの始まりの瞬間にして比較
例えば、fromPatternDayJust() であれば fromDate は時分秒ミリ秒が切り捨てられる
toPatternNext[Hour/Day/Month/...]Just()
toDate を、その時間や日や月などの次の始まりの瞬間にして比較
例えば、toPatternNextDayJust() であれば toDate は次の日に移動して時分秒ミリ秒が切り捨てられる

手動で設定する内容がアプリの複数箇所で利用される場合は、FromToOption 自体を拡張して、業務に合わせたアプリ独自の FromToOption を作って横展開すると良いでしょう。

e.g. アプリ独自の FromToOption を作成 @Java
public class AppFromToOption extends FromToOption {

    public AppFromToOption() {
        // アプリ全体で統一の比較条件の調整
        ...
    }

    public AppFromToOption arrangeAsPurchase...() {
        // 業務ごとに独自の比較条件の調整
        clearAll();
        ...
        return this;
    }

    public AppFromToOption arrangeAsLogin...() {
        // 業務ごとに独自の比較条件の調整
        clearAll();
        ...
        return this;
    }
}

もしくは null

例えば、"1969年までに生まれた、もしくは null" というような条件を設定できます。 不明な値(null)を業務的に小さい方に含めたり、はたまた大きい方に含めたりする場合に有効です。

e.g. fromDate を LessThan に変更して FromTo {BIRTHDATE} @Java
Date toDate = ... // 1969/01/01
cb.query().setBirthdate_FromTo(null, toDate
        , new FromToOption().compareAsYear().orIsNull());

// e.g. (BIRTHDATE < '1970-01-01' or BIRTHDATE is null)

OrScopeQuery を使っても同じことは実現できますが、こちらの方がすっきり書けます。また、OrScopeQuery は、AndPart の中でさらに OrScopeQuery をネストさせることができないため、AndPart 内でこういった or 条件を利用する必要がある場合に有効です。

メソッド仕様

基本仕様

nullの指定
引数の fromDate と toDate が両方とも null の場合は、その条件は無効となります。片方だけが null の場合はもう一つの条件だけが有効になります。
同カラムに対する複数条件の指定
内部的に、GreaterEqual や LessThan など、別のメソッドを呼び出しているだけなので、その利用した条件の仕様通りです。
同カラムに対する同じ条件値での複数指定
内部的に、GreaterEqual や LessThan など、別のメソッドを呼び出しているだけなので、その利用した条件の仕様通りです。

条件値の型は java.util.Date

プロパティの型が java.sql.Timestamp であっても、引数の条件値の型は java.util.Date です。java.sql.Timestamp は java.util.Date の継承クラスなので特に問題ありません。

条件値の日付の値はそのまま

内部での日付調整があっても、条件値として指定した日付オブジェクトの値はそのままです。 内部では新たに日付オブジェクトを作成して利用しています。

指定されたオプションの再利用

FromToOption はスレッドセーフではありませんが、同じスレッド内であれば別のカラムに再利用することはできます。 全く同じオプションで、別のカラムも曖昧検索する場合に利用できます。

サポートされる型

  • Date 全般 (DateやTimestampなど)

数値の FromTo は、RangeOf という別の名前で用意されています。 ただ、数値の方は日付ほど業務上の特殊なパターンが存在しないため、シンプルなものとなっております。