現在時刻を扱うUT辛くないですか?
Date and Time API(JSR-310)のJavadoc見ていたら使えそうなクラスを見つけました!
その名もjava.time.Clock
。
ちょっとJavadoc引用。
タイムゾーンを使用して現在の時点、日付および時間へのアクセスを提供するクロックです。
このクラスのインスタンスは、現在の時点を検索するために使用されます。現在の時点は、保存済みタイムゾーンを使用して現在の日時を検索することで解釈できます。そのためクロックは、System.currentTimeMillis()およびTimeZone.getDefault()のかわりに使用できます。Clockの使用は任意です。すべての主要な日付/時間クラスには、デフォルト・タイムゾーンのシステム・クロックを使用するnow()ファクトリ・メソッドも用意されています。この抽象の主要目的は、代替クロックを必要なときにプラグインできるようにすることです。アプリケーションはstaticメソッドではなくオブジェクトを使用して現在時間を取得します。これによりテストを単純化できます。
アプリケーションにとってのベスト・プラクティスは、Clockを現在の時点を必要とするメソッドに渡すことです。これを実現するための1つの方法が、Dependency Injectionフレームワークです。
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}このアプローチにより、代替クロック(fixedやoffsetなど)をテストで使用できます。
systemファクトリ・メソッドは、最良の利用可能なシステム・クロックに基づくクロックを提供します。これは、System.currentTimeMillis()またはより分解能の高いクロック(利用できる場合)を使用します。
これってようはClock
インスタンスをDIすることでテストを単純化できるって言っていますよね??
ということはClock
インスタンスを生成さえできればどうとでもなるということで、Clock
インスタンスの生成を試してみましょう!
Scalaで書きますよ!
Javaは1.8.0_131
Scalaは2.12.2
を使っています。
以降はimport java.time._
している前提で。
Clock
インスタンスの生成
一番シンプルな使い方はこの辺かな。
scala> Clock.systemDefaultZone
res0: java.time.Clock = SystemClock[Asia/Tokyo]
scala> res0.instant
res1: java.time.Instant = 2017-06-29T16:26:19.189Z
scala> Clock.systemUTC
res2: java.time.Clock = SystemClock[Z]
scala> res2.instant
res3: java.time.Instant = 2017-06-29T16:27:40.517Z
まぁこの辺は完全に現在時刻取得です。
次に固定化したClock
生成。
scala> val zoneId = ZoneId.systemDefault
zoneId: java.time.ZoneId = Asia/Tokyo
scala> val datetime = ZonedDateTime.of(2017, 6, 27, 22, 0, 0, 0, zoneId).toInstant
datetime: java.time.Instant = 2017-06-27T13:00:00Z
scala> val clock = Clock.fixed(datetime, zoneId)
clock: java.time.Clock = FixedClock[2017-06-27T13:00:00Z,Asia/Tokyo]
こんな感じで固定化したClock
が使えますね。
固定化したClock
インスタンスを使う
これは簡単。ぱっと見た感じ日付、時間、Instant系はnow(Clock clock)
メソッドを持っているので、例えばこんな感じ。
scala> val now = ZonedDateTime.now(clock)
now: java.time.ZonedDateTime = 2017-06-27T22:00+09:00[Asia/Tokyo]
java.util.Date
を使っているって場合は変換すれば良い。
scala> val date = java.util.Date.from(Instant.now(clock))
date: java.util.Date = Tue Jun 27 22:00:00 JST 2017
これを差し込めばUTも楽勝っすね!
あ、でもMinimal Cake Patternを使えばClock
は一切使わずにしかもかなり簡単にいけることに書いて気づいたぞ。。。
既存のアプリケーションにいれようとするとお膳立てしないといけないから、それはそれで大変そう。それならMinimal Cake Pattern化するよね。
Minimal Cake Patternすげーって結論になってしまった。。。
面白いAPIだと思ったのに。