Javaの日付操作【プログラミング初心者向け教材】

プログラミング

日付の生成、加算・減算、比較、フォーマットなどの操作は、プログラミングでよくある操作の1つです。

データベースやAPIなどで外部と日付データを送受信する際には、決まったフォーマットにする必要があります。

そのため、プログラミング初心者にとって、日付操作は学習すべき事項の1つとなります。

本記事では、Javaでよく使われる日付操作について解説します。

プログラミング初心者の方の学習や、忘れてしまった方の復習として、参考にしていただければ幸いです。

記載しているプログラムは、Java11を使って動作確認をしています。

Javaの日付操作

よくある日付操作として、以下の内容を採り上げます。

  • 日付を扱うクラス
  • 日時の生成
  • 日時・曜日の取得
  • 日時の加算・減算
  • 日時の差分
  • 日時の比較
  • 日時から文字列へのフォーマット
  • 文字列から日時への変換
  • ライブラリの利用

日付を扱うクラス

Javaで日付を扱う主なクラスには以下があります。

  • Dateクラス:日時を扱うクラスとしてJavaの初期から使われているクラスです。
  • Calendarクラス:日付(カレンダー)を扱うクラスとして、Dateクラスではできない加算などの日付操作を行うクラスです。
  • LocalDateTimeクラス:Java8から導入され、日時を扱い、かつ加算などの日付操作も行えるクラスです。

上記の3クラスはいずれもJavaの標準で提供されるクラスです。(LocalDateTimeはJava8以降で提供)
Java8以降の環境では、LocalDateTimeの利用を推奨します。
今回はLocalDateTimeクラスを中心に解説していきます。

日時の生成

LocalDateTimeクラスを生成するには、大きく3つの方法があります。

現在時刻で生成する方法

d = LocalDateTime.now();
System.out.println(d); // -> 2020-10-21T09:07:48.225

任意の日時を指定して生成する方法

d = LocalDateTime.of(2020, 10, 21, 9, 1, 2);
System.out.println(d); // -> 2020-10-21T09:01:02

別々の日付と時刻から生成する方法

date = LocalDate.of(2020, 10, 21);
time = LocalTime.of(9, 1, 2);
d = LocalDateTime.of(date, time);
System.out.println(d); // -> 2020-10-21T09:01:02

日時・曜日の取得

日時の各属性値を取得する方法

日時取得用の各メソッドを呼出すことで取得することができます。

d = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
System.out.println(d.getYear()); // -> 2020
System.out.println(d.getMonthValue()); // -> 10
System.out.println(d.getDayOfMonth()); // -> 21
System.out.println(d.getHour()); // -> 9
System.out.println(d.getMinute()); // -> 1
System.out.println(d.getSecond()); // -> 2
System.out.println(d.getNano()); // -> 3

曜日を取得する方法

曜日の取得は、getDayOfWeekメソッドを使います。戻り値はDayOfWeekクラスとなります。
そのまま出力しても英語表記となるため、数値や日本語表記とする場合には、さらにメソッドを呼出します。

数値で取得する場合は、以下の意味となります。

1:月曜日、2:火曜日、3:水曜日、4:木曜日、5:金曜日、6:土曜日、7:日曜日
d = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3); // -> 2020/10/21は水曜日
System.out.println(d.getDayOfWeek()); // -> WEDNESDAY
System.out.println(d.getDayOfWeek().getValue()); // -> 3
System.out.println(d.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.JAPANESE)); // -> 水曜日

日付情報(LocalDate)を取得する方法

LocalDateTimeからLocalDateに変換するにはtoLocalDateメソッドを使います。

d = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
date = d.toLocalDate();
System.out.println(date); // -> 2020-10-21

時刻情報(LocalTime)を取得する方法

LocalDateTimeからLocalTimeに変換するにはtoLocalTimeメソッドを使います。

d = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
time = d.toLocalTime();
System.out.println(time); // -> 09:01:02.000000003

日時の加算・減算

日時の加算や減算を行うには、LocalDateTimeオブジェクトに対して、plusXXXメソッドやminusXXXメソッドを使います。

日時の加算

d = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
System.out.println(d.plusYears(1)); // -> 2021-01-01T00:00
System.out.println(d.plusMonths(2)); // -> 2020-03-01T00:00
System.out.println(d.plusDays(3)); // -> 2020-01-04T00:00
System.out.println(d.plusHours(4)); // -> 2020-01-01T04:00
System.out.println(d.plusMinutes(5)); // -> 2020-01-01T00:05
System.out.println(d.plusSeconds(6)); // -> 2020-01-01T00:00:06
System.out.println(d.plusNanos(7)); // -> 2020-01-01T00:00:00.000000007
System.out.println(d.plusWeeks(8)); // -> 2020-02-26T00:00

plusWeeksメソッドは週を加算します。

日時の減算

d = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
System.out.println(d.minusYears(1)); // -> 2019-01-01T00:00
System.out.println(d.minusMonths(2)); // -> 2019-11-01T00:00
System.out.println(d.minusDays(3)); // -> 2019-12-29T00:00
System.out.println(d.minusHours(4)); // -> 2019-12-31T20:00
System.out.println(d.minusMinutes(5)); // -> 2019-12-31T23:55
System.out.println(d.minusSeconds(6)); // -> 2019-12-31T23:59:54
System.out.println(d.minusNanos(7)); // -> 2019-12-31T23:59:59.999999993
System.out.println(d.minusWeeks(8)); // -> 2019-11-06T00:00

日時の差分

日時の差分を取得するには、ChronoUnitを使う方法と、Perios/Durationを使う方法があります。
それぞれ場面によって使い分けると良いと思います。

ChronoUnitを使った日時の差分取得

ChronoUnitに指定する単位に従って2つの日時の差分を取得することができます。

d1 = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
d2 = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
System.out.println(ChronoUnit.YEARS.between(d1, d2)); // -> 0
System.out.println(ChronoUnit.MONTHS.between(d1, d2)); // -> 9
System.out.println(ChronoUnit.DAYS.between(d1, d2)); // -> 294
System.out.println(ChronoUnit.HOURS.between(d1, d2)); // -> 7065
System.out.println(ChronoUnit.MINUTES.between(d1, d2)); // -> 423901
System.out.println(ChronoUnit.SECONDS.between(d1, d2)); // -> 25434062

Period/Durationを使った日時の差分取得

Periodは、2つの日時の差分を年、月、日に分割して取得します。そのためトータル日数を取得するような場面では利用できません。
Durationは、2つの日時の差を時間ベースで取得します。トータル日数やトータル時間などを取得することができます。トータル年とトータル月を取得するようなメソッドは提供されていません。

d1 = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
d2 = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
Period p = Period.between(d1.toLocalDate(), d2.toLocalDate());
System.out.println(p); // -> P9M20D
System.out.println(p.getYears()); // -> 0
System.out.println(p.getMonths()); // -> 9
System.out.println(p.getDays()); // -> 20

Duration duration = Duration.between(d1, d2);
System.out.println(duration); // -> PT7065H1M2.000000003S
System.out.println(duration.toDays()); // -> 294
System.out.println(duration.toHours()); // -> 7065
System.out.println(duration.toMinutes()); // -> 423901
System.out.println(duration.getSeconds()); // -> 25434062

日時の比較

日時の前後を比較にするには、isAfter/isBefore/isEqualメソッドを使います。

d1 = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
d2 = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
System.out.println(d1.isBefore(d2)); // -> true
System.out.println(d1.isEqual(d2)); // -> false
System.out.println(d1.isAfter(d2)); // -> false

日時から文字列へのフォーマット

日時から文字列へ変換してフォーマットするには、DateTimeFormatterクラスを使います。

d = LocalDateTime.of(2020, 10, 21, 9, 1, 2, 3);
f = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(d.format(f)); // -> 2020/10/21 09:01:02

DateTimeFormatterクラスに指定できる書式の詳細はAPIをご覧ください。

文字列から日時への変換

文字列からLocalDateTimeオブジェクトへ変換するには、parseメソッドを使います。
ISO日付/時間フォーマットで指定する方法と、DateTimeFormatterでフォーマットを指定する方法があります。

s = "2020-10-21T09:01:02";
d = LocalDateTime.parse(s);
System.out.println(d); // -> 2020-10-21T09:01:02

s = "2020/10/21 09:01:02";
f = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
d = LocalDateTime.parse(s, f);
System.out.println(d); // -> 2020-10-21T09:01:02

こちらもDateTimeFormatterクラスに指定できる書式の詳細はAPIをご覧ください。

ライブラリの利用

Javaで日付操作をするための外部ライブラリを2つ紹介します。
どちらもJava7以前はよく使われていたものですが、LocalDateTimeを始めとするDate and Time APIがJava8で登場してからは、積極的に使う意味はないかと思います。

Apache Commons Lang

Apache Commonsが提供するLang APIにあるDateUtilsクラスには日付処理を便利に扱うためのメソッドが数多く提供されています。

Joda Time

Joda Time API は、Java7以前にDate and Time APIの元となるようなAPIを提供していました。
現在は、Java8以降はDate and Time APIへ移行するように案内しています。

日付操作には十分なテストが必要

日付と文字列を変換していると、環境によって時間が変わってしまうことがあります。

また、クライアントとサーバで日時をやり取りすると時間がが変わってしまうなどもあります。

こうした環境による時間の変化は、タイムゾーンが原因であることが考えられます。

実際のプロジェクトでは、データベースはどのタイムゾーンなのか、サーバはどのタイムゾーンで設定するのか、クライアントはどのタイムゾーンで表示するのかといったことを最初に決めておく必要があります。

これらを統一しておかないと、意図せず異なった日時で保存されてしまったり、間違った時間を元に処理が動いてしまったりといったことが発生します。

また、うるう年やうるう秒など、特定の時にだけ発生する時間差などもあります。

過去にもこうした対策がきちんと行われていないことが原因で、大きなトラブルになった事例もあります。

日付を扱う際には、実装時にも注意し、テストを十分に行うことをおすすめします。

今回はJavaの日付操作について紹介しました。

参考になれば幸いです。

コメント

タイトルとURLをコピーしました