開発者Gです。
前回はDateを文字列にフォーマットする拡張関数と、文字列をパースしてDateを取得する拡張関数を追加しました。
今回はDateに年月日時分秒ミリ秒を加算、減算するのに便利な拡張関数を追加します。
なお、ソースコードは全部、または一部を抜粋して表示します。
また、前回までに作成した拡張関数と拡張プロパティを使用するので、本記事に掲載のサンプルコードを動作させるには必要に応じて前回までのサンプルコードを取り込んでください。
Calendarで加算、減算を行う方法
Javaの標準ライブラリで日付の計算をするにはCalendarクラスを使用します。
Calendarには加算用のメソッドのみ用意されています。
減算するにはマイナス値を加算します。
CalendarPlusMinus1.kt
package samples import extensions.date.format.format import extensions.date.property.calendar import extensions.string.date.toDate import org.junit.Test import java.util.* class CalendarPlusMinus1 { @Test fun plus() { val date = "2021/09/01".toDate() // (その5)の記事で作成した拡張関数toDate()を使用します println("date: ${date.format()}") val calendar = date.calendar // (その4)の記事で作成した拡張プロパティcalendarを使用します // 年を加算します calendar.add(Calendar.YEAR, 1) val date2 = Date.from(calendar.toInstant()) println("YEAR + 1: ${date2.format()}") // 月を加算します calendar.add(Calendar.MONTH, 1) val date3 = Date.from(calendar.toInstant()) println("MONTH + 1: ${date3.format()}") // 日を加算します calendar.add(Calendar.DAY_OF_MONTH, 1) val date4 = Date.from(calendar.toInstant()) println("DAY_OF_MONTH + 1: ${date4.format()}") // 時を加算します calendar.add(Calendar.HOUR_OF_DAY, 1) val date5 = Date.from(calendar.toInstant()) println("HOUR_OF_DAY + 1: ${date5.format()}") // 分を加算します calendar.add(Calendar.MINUTE, 1) val date6 = Date.from(calendar.toInstant()) println("MINUTE + 1: ${date6.format()}") // 秒を加算します calendar.add(Calendar.SECOND, 1) val date7 = Date.from(calendar.toInstant()) println("SECOND + 1: ${date7.format()}") // ミリ秒を加算します calendar.add(Calendar.MILLISECOND, 1) val date8 = Date.from(calendar.toInstant()) println("MILLISECOND + 1: ${date8.format()}") } @Test fun minus() { println() val date = "2021/09/01".toDate() // (その5)の記事で作成した拡張関数toDate()を使用します println("date: ${date.format()}") val calendar = date.calendar // (その4)の記事で作成した拡張プロパティcalendarを使用します // 年を減算します calendar.add(Calendar.YEAR, -1) val date2 = Date.from(calendar.toInstant()) println("YEAR - 1: ${date2.format()}") // 月を減算します calendar.add(Calendar.MONTH, -1) val date3 = Date.from(calendar.toInstant()) println("MONTH - 1: ${date3.format()}") // 日を減算します calendar.add(Calendar.DAY_OF_MONTH, -1) val date4 = Date.from(calendar.toInstant()) println("DAY_OF_MONTH - 1: ${date4.format()}") // 時を減算します calendar.add(Calendar.HOUR_OF_DAY, -1) val date5 = Date.from(calendar.toInstant()) println("HOUR_OF_DAY - 1: ${date5.format()}") // 分を減算します calendar.add(Calendar.MINUTE, -1) val date6 = Date.from(calendar.toInstant()) println("MINUTE - 1: ${date6.format()}") // 秒を減算します calendar.add(Calendar.SECOND, -1) val date7 = Date.from(calendar.toInstant()) println("SECOND - 1: ${date7.format()}") // ミリ秒を減算します calendar.add(Calendar.MILLISECOND, -1) val date8 = Date.from(calendar.toInstant()) println("MILLISECOND - 1: ${date8.format()}") } }
実行結果
date: 2021/09/01 YEAR + 1: 2022/09/01 MONTH + 1: 2022/10/01 DAY_OF_MONTH + 1: 2022/10/02 HOUR_OF_DAY + 1: 2022/10/02 01:00:00 MINUTE + 1: 2022/10/02 01:01:00 SECOND + 1: 2022/10/02 01:01:01 MILLISECOND + 1: 2022/10/02 01:01:01.001 date: 2021/09/01 YEAR - 1: 2020/09/01 MONTH - 1: 2020/08/01 DAY_OF_MONTH - 1: 2020/07/31 HOUR_OF_DAY - 1: 2020/07/30 23:00:00 MINUTE - 1: 2020/07/30 22:59:00 SECOND - 1: 2020/07/30 22:58:59 MILLISECOND - 1: 2020/07/30 22:58:58.999
いちおうひと通りの加減算はできますが、
calendar.add(Calendar.YEAR, -1) val date2 = Date.from(calendar.toInstant())
のようにインデックス指定したり、CalendarのインスタンスからDateのインスタンスに変換したりする必要があり、使い勝手がよくありません。
LocalDateやLocalDateTimeのplus系とminus系のメソッド
Date and Time APIのLocalDateやLocalDateTimeでは上記のような不便さが解消されています。
LocalDateTimeには加減算するためのメソッドとして以下のものが用意されています。
- plusYears, minusYears
- plusMonths, minusMonths
- plusDays, minusDays
LocalDateTimeにはさらに以下のものが用意されています。
- plusHours, minusHours
- plusMinutes, minusMinutes
- plusSeconds, minusSeconds
- plusNano, minusNano
Dateについてもこれらと同等の機能を利用できるように拡張関数を追加しましょう。
ただし、LocalDateTimeにはmillisecond(ミリ秒)がなく、利用できるのはnano(ナノ秒)ですが、Dateは精度がミリ秒までなので、plusMillisecond, minusMillisecondを実装します。
DatePlusMinusExtension.kt
package extensions.date.plusminus import extensions.date.property.calendar import java.util.* /** * Dateに年を加えます。 */ fun Date.plusYears(years: Int): Date { val calendar = this.calendar calendar.add(Calendar.YEAR, years) return Date.from(calendar.toInstant()) } /** * Dateから年を引きます。 */ fun Date.minusYears(years: Int): Date { return this.plusYears(-years) } /** * Dateに月を加えます。 */ fun Date.plusMonths(months: Int): Date { val calendar = this.calendar calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + months) return Date.from(calendar.toInstant()) } /** * Dateから月を引きます。 */ fun Date.minusMonths(months: Int): Date { return this.plusMonths(-months) } /** * Dateに日を加えます。 */ fun Date.plusDays(days: Int): Date { val calendar = this.calendar calendar.add(Calendar.DAY_OF_MONTH, days) return Date.from(calendar.toInstant()) } /** * Dateから日を引きます。 */ fun Date.minusDays(days: Int): Date { return this.plusDays(-days) } /** * Dateに時を加えます。 */ fun Date.plusHours(hours: Int): Date { val calendar = this.calendar calendar.add(Calendar.HOUR_OF_DAY, hours) return Date.from(calendar.toInstant()) } /** * Dateから時を引きます。 */ fun Date.minusHours(hours: Int): Date { return this.plusHours(-hours) } /** * Dateに分を加えます。 */ fun Date.plusMinutes(minutes: Int): Date { val calendar = this.calendar calendar.add(Calendar.MINUTE, minutes) return Date.from(calendar.toInstant()) } /** * Dateから分を引きます。 */ fun Date.minusMinutes(minutes: Int): Date { return this.plusMinutes(-minutes) } /** * Dateに秒を加えます。 */ fun Date.plusSeconds(seconds: Int): Date { val calendar = this.calendar calendar.add(Calendar.SECOND, seconds) return Date.from(calendar.toInstant()) } /** * Dateから秒を引きます。 */ fun Date.minusSeconds(seconds: Int): Date { return this.plusSeconds(-seconds) } /** * Dateにミリ秒を加えます。 */ fun Date.plusMilliseconds(milliseconds: Int): Date { val calendar = this.calendar calendar.add(Calendar.MILLISECOND, milliseconds) return Date.from(calendar.toInstant()) } /** * Dateからミリ秒を引きます。 */ fun Date.minusMilliseconds(milliseconds: Int): Date { return this.plusMilliseconds(-milliseconds) }
使用例
package extensions.date.plusminus import extensions.date.format.format import extensions.string.date.toDate import org.junit.Test class DatePlusMinusExtensionTest { @Test fun plusAndMinus() { val date = "2021/09/01".toDate() val date2 = date.plusYears(1) val date3 = date2.plusMonths(1) val date4 = date3.plusDays(1) val date5 = date4.plusHours(1) val date6 = date5.plusMinutes(1) val date7 = date6.plusSeconds(1) val date8 = date7.plusMilliseconds(1) println("date: ${date.format()}") println("plusYears(1): ${date2.format()}") println("plusMonths(1): ${date3.format()}") println("plusDays(1): ${date4.format()}") println("plusHours(1): ${date5.format()}") println("plusMinutes(1): ${date6.format()}") println("plusSeconds(1): ${date7.format()}") println("plusMilliseconds(1): ${date8.format()}") // メソッドチェーンで連続的に呼び出すことができます。 // 作業用の変数を使用しなくてよいのでコードがコンパクトになります。 val date9 = date.plusYears(1) .plusMonths(1) .plusDays(1) .plusHours(1) .plusMinutes(1) .plusSeconds(1) .plusMilliseconds(1) println("Method chain result: ${date9.format()}") } }
実行結果
date: 2021/09/01 plusYears(1): 2022/09/01 plusMonths(1): 2022/10/01 plusDays(1): 2022/10/02 plusHours(1): 2022/10/02 01:00:00 plusMinutes(1): 2022/10/02 01:01:00 plusSeconds(1): 2022/10/02 01:01:01 plusMilliseconds(1): 2022/10/02 01:01:01.001 Method chain result: 2022/10/02 01:01:01.001
追加した関数では、plusやminusの計算後の新しいDateのインタンスを返すので、メソッドチェーンが利用できます。
これにより、作業用の変数を使用しなくてよいのでコードがコンパクトになります。
次回予告
Date、LocalDate、LocalDateTimeの相互運用(仮)
乞うご期待。