DevDevデブ!!

プログラミングのこととか書きます。多分。。。

ZoneDateTimeにいろいろメソッドを生やしたいわけです。

Qiitaに書いたけど、ブログにも書いとく

qiita.com

ZoneDateTimeからいろいろ取り出したい

まあ、色々ありますよね。

  • 年月日以外のフィールドを0で切り捨てたいとか
  • 各フィールドをハイフン区切りで文字列にしたいとか
  • 年月だけyyyyMMのフォーマットで欲しいとか

うちはMySQLで年月日でパーティションを切っている(20190101, 20190102とか)ので、yyyyMMddフォーマットでInt型で取り出したいケースが頻繁にある。

そういう場合、こういうコードを書くことになる

val now = ZonedDateTime.now() //とりあえず現在時刻
val formatter = DateTimeFormatter.ofPattern("yyyyMMdd")
val yyyyMMdd = now.format(formatter).toInt

DateTimeFormatterのインスタンスはスレッドセーフなので、定数に定義しとけばいい。

以下のような感じで

object Formatters {
    val yyyyMMddFormatter = DateTimeFormatter.ofPattern("yyyyMMdd")
}

そしたら次のように実質1行でかける

import Formatters._
//mainとかは省略
val now = ZonedDateTime.now() //とりあえず現在時刻
val yyyyMMdd = now.format(yyyyMMddFormatter).toInt

now.format(yyyyMMddFormatter).toInt

どうでしょう?まだめんどくさくないですか?

私は実務で本当によくこのフォーマットを使うので、これを何回も書くのはダルいです。

ZoneDateTimeが持つ情報で完結しているわけなので、ZoneDateTimeのインスタンスメソッドとして生えててほしい!!!!!

(カプセル化的な観点でもね)

(私は「メソッドを生やす」という表現をよく使いますが、みなさんはどのようにいいますか?)

implicit classで拡張してしまう

implicit classについてはググって。早い話が既存クラスを拡張する仕組みです。

Scala 2.10からしか使えないので注意 (implicit conversion使えば同じことできるけど

今回のケースだと、ZoneDateTimeをラッパークラスに自動変換するものだと思ってもらえばいいはず。

次のようなクラスを作ります。

implicit class RichZoneDateTime(val self: ZoneDateTime) extends AnyVal {
    private val yyyyMMddFormatter = DateTimeFormatter.ofPattern("yyyyMMdd")
    def yyyyMMdd: Int = {
        self.format(yyyyMMddFormatter).toInt
    }
}

コンストラクタ引数のselfの型は拡張したいクラスにする(今回はZoneDateTime)

クラス名は拡張元のクラス名の頭にRichとプレフィックスをつけるのが定番(だと思う

上記クラスをスコープ内に入れた状態だと、以下のコードが実行可能になる

val now = ZoneDateTime.now()
now.yyyyMMdd

実行時に、yyyyMMddメソッドを叩いたときに、暗黙的にRichZoneDateTimeに変換されてる(はず

頻繁に使う変換処理とか、文字列フォーマット処理とかをimplicit classに生やすとスッキリする。

まとめ

implicit classで最強のオレオレZoneDateTimeを作ろう!!!