【Scala】Scala ~ Joda-Time ~

■ はじめに

https://dk521123.hatenablog.com/entry/2023/03/08/000000

の続き。

今回は、Scala で、Joda-Time を使うことを扱う。

目次

【1】Joda-Time
 1)使用上の注意
【2】インストール
【3】基本動作
 0)現在時間
 1)Datetime⇒文字列
 2)文字列⇒Datetime
【4】期間クラス
 1)Duration
 2)Period
 3)Interval
【5】サンプル
 1)一日・末日
 2)日付の比較

【1】Joda-Time

* 読み方は、ジョーダ・タイム(だと思う)

https://www.joda.org/joda-time/

より抜粋
~~~~~~~~~
The standard date and time classes prior to Java SE 8 are poor.
By tackling this problem head-on, Joda-Time became
 the de facto standard date and time library for Java prior to Java SE 8.
~~~~~~~~~

日本語訳

Java SE 8以前の標準日付・時刻クラスは貧弱である。
この問題に真正面から取り組むことで、
Joda-TimeはJava SE 8以前のJavaの事実上の標準日付・時刻ライブラリとなった。

1)使用上の注意

* 紹介しておいてなんなんだが、
 標準的なDate and Time APIであるjava.time(JSR-310)
 を使った方がいいらしい

https://www.joda.org/joda-time/

より抜粋
~~~~~~~~~
Note that from Java SE 8 onwards,
 users are asked to migrate to java.time (JSR-310) 
- a core part of the JDK which replaces this project.
~~~~~~~~~

日本語訳

Java SE 8以降では、
ユーザーは、java.time(JSR-310)
-このプロジェクトを置き換えるJDKのコア部分-
に移行するよう求められていることに注意されたい。

補足:java.time との絡みに関して

* 以下のような差があるらしい
~~~~
Joda-Timeとjava.timeでは書式文字列が
同じでもパース結果が異なる場合がある
~~~~

https://qiita.com/kijuky/items/136586048b4feceea04a

* Joda-Timeをjava.timeに移行に関しては以下。

https://ja.securecodewarrior.com/article/migrating-joda-time-to-java-time

【2】インストール

libraryDependencies ++= Seq(
  "joda-time" % "joda-time" % "2.12.7"
)

https://mvnrepository.com/artifact/joda-time/joda-time

【3】基本動作

* 主なクラスは以下の通り。

org.joda.time.DateTime
https://www.joda.org/joda-time/apidocs/org/joda/time/DateTime.html
org.joda.time.format.DateTimeFormat
https://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html

0)現在時間

import org.joda.time.{DateTime, DateTimeZone}
import org.joda.time.format.DateTimeFormat

object Hello {
  def main(args: Array[String]): Unit = {
    // ★ここ
    val now = DateTime.now
    
    val datetimeFormat = DateTimeFormat.forPattern(
      "yyyyMMdd").withZone(DateTimeZone.UTC)

    println(datetimeFormat.print(now))
  }
}

1)Datetime⇒文字列

import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat

object Hello {
  def main(args: Array[String]): Unit = {
    val now = DateTime.now
    val datetimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
    // 2024-07-22 16:25:06
    println(datetimeFormat.print(now))

    val datetimeFormat2 = DateTimeFormat.forPattern("yyyy-M-d h:mm:ss.SSS a").withLocale(Locale.UK)
    // 2024-7-22 4:25:06.931 pm
    println(datetimeFormat2.print(now))
  }
}

2)文字列⇒Datetime

import org.joda.time.format.DateTimeFormat
import org.joda.time.{DateTime, DateTimeZone}

object Hello {
  def main(args: Array[String]): Unit = {
    val targetDate = "2024-07-09"

    // Pattern-1: datetimeFormat.parseDateTime
    val datetimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd")
    // 2024-07-09T00:00:00.000+09:00
    println(datetimeFormat.parseDateTime(targetDate))
    // 2024-07-08T15:00:00.000Z
    println(datetimeFormat.parseDateTime(targetDate).withZone(DateTimeZone.UTC))

    // Pattern-2: DateTime.parse
    // 2024-07-08T15:00:00.000Z
    println(DateTime.parse(targetDate).withZone(DateTimeZone.UTC))
  }
}

【4】期間クラス

* Java の記事だが、以下が参考になる

https://shinsuke789.hatenablog.jp/entry/2013/08/06/195436

1)Duration

* 日時から日時分秒の差を扱うクラス

org.joda.time.Duration
https://www.joda.org/joda-time/apidocs/org/joda/time/Duration.html

2)Period

* 単純に年月日時分秒毎に日時の差を扱うクラス

org.joda.time.Period
https://www.joda.org/joda-time/apidocs/org/joda/time/Period.html
https://www.joda.org/joda-time/key_period.html

例1

import org.joda.time.{DateTime, Period}

object Hello {
  def main(args: Array[String]): Unit = {
    val start = new DateTime("1977-10-10T00:00:00.000")
    val end = new DateTime("2024-07-10T00:00:00.000")
    val period = new Period(start, end)
    // 46
    println(s"Year = ${period.getYears}")
  }
}

例2

import org.joda.time.format.DateTimeFormat
import org.joda.time.{DateTime, DateTimeZone, Period}

object Hello {
  def main(args: Array[String]): Unit = {
    val dateTime = getDateTime("2005-07-09", "yyyy-MM-dd")
    // 2024-07-10
    val now = DateTime.now()

    val age = getAge(dateTime, now)
    // 19
    println(age)
  }

  private def getDateTime(inputValue: String, inputPattern: String): DateTime = {
    val format = DateTimeFormat.forPattern(inputPattern)
    format.parseDateTime(inputValue).withZone(DateTimeZone.UTC)
  }

  private def getAge(fromDateTime: DateTime, toDateTime: DateTime): Int = {
    // !!注目!!
    new Period(fromDateTime, toDateTime).getYears
  }
}

3)Interval

* 2つの期間の差を扱うクラス

org.joda.time.Interval
https://www.joda.org/joda-time/apidocs/org/joda/time/Interval.html

overlap()

* 2つの期間の重なった範囲を確認することが可能

【5】サンプル

1)一日・末日

import org.joda.time.DateTime.now

object Hello {
  def main(args: Array[String]): Unit = {
    val first = now.dayOfMonth().withMinimumValue()
    val end = now.dayOfMonth().withMaximumValue()

    // 2024-07-09
    println(now.toString("yyyy-MM-dd"))
    // 2024-07-01
    println(first.toString("yyyy-MM-dd"))
    // 2024-07-31
    println(end.toString("yyyy-MM-dd"))
  }
}

2)日付の比較

import org.joda.time.DateTime

object Hello {
  def main(args: Array[String]): Unit = {
    // true
    println(isValid(new DateTime("2021-12-31T00:00:00.000")))
    // false
    println(isValid(new DateTime("1899-12-31T00:00:00.000")))
    // false
    println(isValid(new DateTime("2999-12-31T00:00:00.000")))
  }

  private def isValid(target: DateTime): Boolean = {
    val minDate = new DateTime("1900-01-01T00:00:00.000")
    val maxDate = DateTime.now

    // ★
    target.isAfter(minDate) && target.isBefore(maxDate)
  }
}

補足:比較用メソッド

Method Explanations
isAfter() 対象日時が未来の場合 true
isBefore() 対象日時が過去の場合 true
isEqual() 対象日時が同じ場合 true

参考文献

https://qiita.com/rubytomato@github/items/a9abb4f766e905c0c227
https://developers.cyberagent.co.jp/blog/archives/24735/
https://qiita.com/suin/items/db5963c2f5c8dddd417b
https://qiita.com/suin/items/2c111601b6501f4586f7
https://terasolunaorg.github.io/guideline/public_review/ArchitectureInDetail/Utilities/JodaTime.html

関連記事

Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ コレクションで使えるメソッド ~
https://dk521123.hatenablog.com/entry/2023/09/07/223422
Scala ~ mutable collection ~
https://dk521123.hatenablog.com/entry/2024/07/09/223730
Scala ~ 基本編 / 日付・日時 ~
https://dk521123.hatenablog.com/entry/2023/03/08/000000
Scala ~ 基本編 / 例外処理 ~
https://dk521123.hatenablog.com/entry/2023/10/05/000135
Scala ~ 基本編 / パターンマッチング ~
https://dk521123.hatenablog.com/entry/2023/06/06/233614