読者です 読者をやめる 読者になる 読者になる

joda-timeでの安全な時間処理(2) -基本編-

前回はjava.util.Date, java.util.Calendarの問題点と、joda-timeのさわりについて書きました。今回のエントリでは実際にjoda-timeを使ってみましょう。

joda-timeでの基本的の時間操作は以下の2つのクラスを用いて行います。

  • org.joda.time.DateTime
  • org.joda.time.DateMidnight

DateTimeとDateMidnight

特徴

DateTimeは任意の時間をミリ秒まで表現するクラスです。それに対して、DateMidnightは任意の時間の00:00:00をミリ秒まで表現するクラスです。java.util.Calendarでは00:00:00を取得するために面倒な丸め処理(round)が必要でしたが、joda-timeではDateMidnightを利用すれば良いです。

性質

DateTimeのJavaDocには以下の記述がされています。

* DateTime is thread-safe and immutable, provided that the Chronology is as well.
* All standard Chronology classes supplied are thread-safe and immutable.

スレッドセーフ(安全)でイミュータブル(変更不可能)な事を保証します。

加減算

加減算用のメソッドとして、plus系とminus系メソッドが用意されています。ここでは加算処理の例を示します。

@Test
public void plus() {
    DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss:SSS");
        
    DateTime now = DateTime.now(); // nowした時間
    DateTime plusMills = now.plus(1); // nowに1ミリ秒加算
    DateTime plusSeconds = now.plusSeconds(1); // nowに1秒加算
    DateTime plusMinutes = now.plusMinutes(1); // nowに1分加算
    DateTime plusHour = now.plusHours(1); // nowに1時間加算
    DateTime plusDay = now.plusDays(1); // nowに1日加算
    DateTime plusWeek = now.plusWeeks(1); // nowに1週間加算
    DateTime plusMonth = now.plusMonths(1); // nowに1ヶ月加算
    DateTime plusYear = now.plusYears(1); // nowに1年加算
}

Calendar#addではCalendar.YEAR等定数を渡して加減算単位をハンドリングするという手法ですが、joda-timeではそのようなレガシーな手法ではなく加減算単位毎のメソッドが用意されています。このインタフェースの方が直感的でミスも少ないですね。
また、加減算メソッドはDateTimeやDateMidnight型を返すのでメソッドチェインで利用できます。

  DateTime.now().plusHours(1).plusMinutes(30); // nowの1時間30分後を取得

メソッドチェインな実装において、builderパターンでつなぎ終えた後にbuildして目的のメソッドを得る手法がありますが、joda-timeの加減算メソッドでは元のインスタンスに変更を加えず都度新しいインスタンスを生成するためこのような形になるためスレッドセーフを保証できます。

判定系

判定系としてis系メソッドが用意されています。

@Test
public void is() {
    // target < nowとする
    DateTime target = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime("2013-07-24 12:00:00");
    DateTime now = DateTime.now();
        
    target.isAfter(now); // targetはnowより未来 => false
    target.isBefore(now); // targetはnowより過去 => true
}

nowとの比較であれば、isAfterNowやisBeforeNowでもOKですね。ちなみにis系メソッドは引数としてlong(エポックからのオフセットミリ秒)とorg.joda.time.ReadableInstantを引数として取ることができます。
DateTimeやDateMidnightはReadableInstantをimplementsしているため、is系メソッドの引数としてとることができます。なお、java.util.Dateやjava.util.Calendarを引数として受け付けてはいません。joda-timeのimmutable思想から逸脱するからです。DateやCalandarを使用しているコードにjoda-timeのis系メソッドを導入するのであれば、一度Calendar#getTime等でエポック(long型)に直してから渡す必要がありますね。

相互変換系

DateTimeをDateMidnightに、またはjava.util.Dateに変換したいというケースもあるでしょう。その場合はto系のメソッドを使用します。

@Test
public void to() {
    DateTime now = DateTime.now(); // nowした時間
    DateMidnight today = now.toDateMidnight(); // nowした日(00:00:00);
    
    DateTime plusOneHour = today.toDateTime().plusHours(1); // nowした日から1時間後
    Date date = plusOneHour.toDate(); // java.util.Dateにする
}

このような感じでスマートに相互変換ができます。もうつまらない丸め処理からは卒業ですね!

まとめ

今回はDateTimeとDateMidnightの基礎中の基礎についてまとめました。これだけでも十分実践で使えますし、Calendarでの煩雑な実装から開放されることは間違いないでしょう。次回はさらに突っ込んだ使い方を紹介します。