SAFluteの現在日時

SAFluteの特徴の一つです。

現在日時は、TimeManagerで

アプリの現在日時を細工してテストをしたいことがあります。

ピンポイントの時間で動かしたい

アプリには、月初、月末、年末など時間限定でないと動かないプログラムもよくあります。 それらをテストするのにはどうしたらいいでしょう? PCのシステム日付を変える方法もありますが、ファイル更新日時などがひっくり返って戻したときに予期せぬ挙動をしたり、ライセンス周りの挙動がおかしくなったりと、 あまりやりたいことではありません。

統一した現在日時インターフェース

そこでよく利用されるのが、アプリで "現在日時を提供するインターフェース" を用意して、その実装をちょっとだけ差し替えることで自由に "アプリ内の現在日時" を変更する方法です。 実際には、実装の差し替えというよりかは設定ファイルで変更できるするようにします。

そのためには、Javaから現在日時を取ってはいけないのです。

新しい開発プロジェクトに入って現在日時を取得する場面に遭遇したら、まずは聞いてみましょう。 現在日時を取得する統一的なインターフェースがありますでしょうか?

まずは、DIのやり方

SAFluteでは、アプリで TimeManager インターフェースをDIすることができます。

e.g. TimeManagerをDIする (ActionやLogicにて) @Java
@Resource
protected TimeManager timeManager;

現在日時を取得

コードを見ればわかる!

e.g. TimeManagerをDIする (ActionやLogicにて) @Java
@Resource
protected TimeManager timeManager;

@Execute(validator = false)
public void index() {
    LocalDate currentLocalDate = timeManager.getCurrentLocalDate();
    LocalDateTime currentLocalDateTime = timeManager.getCurrentLocalDateTime();
    HandyDate currentHandyDate = timeManager.getCurrentHandyDate();
    long currentMillis = timeManager.getCurrentMillis();
    Date flashDate = timeManager.getFlashDate();
}

まあ、やっぱり説明はしましょう。

getCurrentXxx() で現在日時

getCurrentXxx()メソッドで、現在日時を色々な形式で取得することができます。 アプリ内での現在日時の取得は「必ず」このTimeManagerから取得します。 後述する「現在日時の細工」をするために、Javaから直接日付を取得してはいけません。

getCurrentDate() と getFlashDate() との違いは?

以下のように違います。

getCurrentDate()
トランザクション日時 (業務上の現在日時)
getFlashDate()
メソッド呼び出し日時 (物理的な現在日時)

という感じで、 getCurrentDate()は厳密にはトランザクション内では常に同じ日時が取得されます。 トランザクション内での処理タイミングの前後は業務的にあまり必要ないという考えから、 DBに登録する日時データ(更新日時など)などが、一つのトランザクション内で統一されるようにしています。

トランザクション関係なく本当に今の日時が欲しいときは getFlashDate() を使います。 ただ、あまりそういう状況はめったにないと想定しています。

現在日時の細工

月末・年末のテストや、とある期間内でないと動かない機能のテストのときに役立ちます。

[project]_env.properties のプロパティで指定できます。 properties も HotDeploy が効くので、propertiesを修正してすぐに画面を動かせば反映されます。

固定日付を指定 e.g. $(2015/03/22)

$([日付表現]) で固定日付を指定すると、その日付に対して起動時からの経過時間が足されたものが現在日時になります。

e.g. 2014年7月10日にする (起動時からの経過時間が足されたものが現在日時になる) @maihama_env.properties
# one day: 86400000, three days: 259200000, five days: 432000000, one week: 604800000, one year: 31556926000
# special script :: absolute mode: $(2014/07/10), relative mode: addDay(3).addMonth(4)
# The milliseconds for (relative or absolute) adjust time (set only when test) *dynamic in development
time.adjust.time.millis = $(2015/03/22)

相対日付を指定 e.g. addDay(3).addMonth(4)

addDay()やaddMonth()などで相対日付を指定すると、システムの現在日付に対して常にその分だけ計算されたものが現在日時になります。

e.g. システム日付から、三日足して四ヶ月足す @maihama_env.properties
# one day: 86400000, three days: 259200000, five days: 432000000, one week: 604800000, one year: 31556926000
# special script :: absolute mode: $(2014/07/10), relative mode: addDay(3).addMonth(4)
# The milliseconds for (relative or absolute) adjust time (set only when test) *dynamic in development
time.adjust.time.millis = addDay(3).addMonth(4)

過去に戻すときは、マイナスを付ければOKです。e.g. addDay(-3) これらは、HandyDateの機能を使って実現されています。相対日付の表現は、HandyDateのメソッドそのままです。

本番では絶対に使わないで!

開発時のみの利用を想定しています。使っても結合環境(検証環境)まで。 「うっかり本番でも使っちゃった」みたいなを防ぐために、env管理になっています。 (仮に開発で放置したとしても、本番では影響ないように)

仕組み

XxxFwAssistantDirector の prepareTime() メソッドにて、 TimeManager の挙動を調整するための TimeResourceProvider を設定しています。