きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Javaで日付の差が何ヵ月かを計算する。あまりは切り上げる (LocalDate, ChronoUnit)

閏年等を考慮しだしたら混乱したのでメモ残します。

環境

  • Java
    • 17

対応

対応自体は簡単で、ChronoUnit.MONTHESで月の差分を取得できます。今回は、1ヵ月に満たない日付がある場合は切り上げるので+1するだけです。

LocalDate start;
LocalDate end;
ChronoUnit.MONTHS.between(start, end) + 1;

テストケース

下のことに気付けていれば、うるう日を考慮して網羅的にテストする必要はありません。

  • 2022/01/01~2022/01/31
    • 1
      • 0ヵ月 + あまり30日
  • 2022/01/01~2022/02/01
    • 2
      • 1ヵ月 + あまり1日
開始日 終了日 差分の月
2020-01-01 2020-01-15 1
2020-01-01 2020-01-31 1
2020-01-01 2020-02-01 2
2020-01-15 2020-02-10 1
2020-01-01 2020-12-31 12
2020-01-01 2021-12-31 24
2020-02-29 2021-02-28 12
2019-02-28 2020-02-29 13
2020-02-29 2024-02-28 48
2020-02-29 2024-02-29 49
2020-02-01 2020-02-28 1
2020-02-01 2020-02-29 1

ソースコード

終わりに

「暦上の1ヵ月」を意識して計算しなければならない、と考えていたので、単純な計算ができていませんでした。時間計算難しいです。

類似記事

Pythonでインスタンスが自作型ではなく、組込み型(built-in types)であることを判別する(isinstance)(primitive types)

Pythonにて処理する際のインスタンスが組込み型(built-in types)であることを判別したかったのですが、簡単にはできませんでした。もし、簡易にできるようになったら、教えてください。

なお、組込み型(built-in types)とは、Pythonが定義しているstr, int等々の最初から定義されている型のことを指します。Code SmellとしてはPrimitive Obsession(基本データ型への執着)という名前が使われているので、Primitive型といっても問題ないと思われますが、今回の記事ではPythonの公式ヘルプで表現されている組込み型(built-in types)を使用します。

環境

  • Python
    • 3.9

対応

Pythonに概念としては組込み型は存在しますが、組込み型一覧を取得できる機能はありません。そのため、必要に応じて判別したい組込み型をすべて定義する必要があります。

isinstanceメソッド

ざきさんに型判別のメソッドisinstance(object, classinfo)を教えていただきました。感謝です。

使い方としては、第一引数にチェックしたいオブジェクトを設定します。第二引数にチェック対象の型を定義します。Python3.10以降より、Turpleだけでなく、Union Typesでも定義できるようになりました。

# 3.9以前
isinstance(target, (str, int, bool))
# 3.10以降
isinstance(target, str | int | bool)

typeメソッド

type(object)を使用すると、チェックしたいオブジェクトの型を取得できます。チェック対象の型を配列に設定すると、比較できます。

Stack Overflowではこちらの方法で定義していました。

type(target) in [str, int, bool]

実運用では自作型はルートに配備せず、適切なディレクトリに配備するでしょう。ですので、「型が.を含む場合は自作型である」と判別しようとしました。

ただし、このやり方だとdatetime.datetime等々も自作型として判別されてしまうので使えません。すべての組込み型を定義するのは面倒ですので、こうできればよかったのですが、現実はうまくいきませんでした。

"." in str(type(value))
# <class 'int'>
# <class 'str'>
# <class 'bool'>
# <class 'list'>
# <class 'datetime.date'>

ソースコード

終わりに

雑にハンドリングしたかったのですが、ダメでした。

正直、この処理をやりたい時というのはかなりトリッキーなやり方ではあるとは自覚しているので、言語側から提供はしてくれないと思うのですが、もう少し簡易にハンドリングしたかったです。

参考情報

Lombokで自動生成されるGetterのprefixを変更したかった(Accessor prefixが使いたかった)

うまくいかなかったことのメモ。

環境

  • IntelliJ IDEA
    • 2021.3.3 (Ultimate Edition)
  • 2022/05/07時点

きっかけになったツイート

twitter.com

APIのリクエストBodyにて、日付フォーマット「yyyyMMdd」で受け取りたい。ただ、LocalDate型に変換することもある。そのようなコードを書く場合、普段は次のコードのように記載しています。

@Value
@AllArgsConstructor
public class RequestDate {
  private String value;
  private LocalDate localDate;

  private static final DateTimeFormatter SHORT_DATE_FORMATTER =
      DateTimeFormatter.ofPattern("uuuuMMdd");

  public static RequestDate of(String value) {
    return new RequestDate(value, LocalDate.parse(value, SHORT_DATE_FORMATTER));
  }
}

getValue()メソッドでString型を返却したり、getLocalDate()メソッドでLocalDate型を返却していました。

受け取ったパラメータをそのまま返却するという意味でgetValue()としていたが、いい感じにString型やLocalDate型を返却することが分かるメソッド名にしたいと思っていた。

ツイートにあるとおりasString()asLocalDateというGetterを生成できれば、Getterであることを示しつつ、返却型を明示できると考えました。

LombokのGetterのPrefixをgetではなく、asにするだけで対応できるかと思い、まさにそれを達成できる@Accessors(prefix = "as")というアノテーションをみつけました。

…が、ここで大きな問題。

私が普段使用しているIDEのIntelliJ IDEAだと次のような警告が発生して、Getterが生成されませんでした。

Not generating getter for this field: It does not fit your @Accessors prefix list

一応、IntelliJ IDEAのプラグインを修正すれば直せそうですが、そこまでして直すモチベーションはないので、この対応はしないことにします。

終わりに

JavaのRecordのGetterのprefixを修正できるならそれでもいいのですが、そういうこともできそうにないです。

もちろん、自作でasString()asLocalDate()というメソッドを作ってもいいのですが、わざわざその工数を確保してまで得られるメリットもないので、とりあえず現状のままにしようと思います。

参考情報