Java8のLocalDateとLocalDateTimeを変換してみた

javaJava8

Java8のLocalDateとLocalDateTimeを変換してみた

Java8からLocalDateクラスというのが追加されていて色々調べてみました。

LocalDate date = new LocalDate(2019,1,1); // コンストラクタがprivateなのでNG
LocalDate date = LocalDate.of(2019,2,28); // staticメソッドのofを使用してインスタンス生成する
LocalDate date = LocalDate.of(2019,Month.APRIL,1); // Monthも使える

インスタンス生成方法はLocalDate.ofを使って生成します。 デフォルトはyyyy-MM-dd形式になります。ofメソッドでleap year(うるう年)の判定もしてくれるのでnew LocalDate(2019,2,29);なんてするとエラーが発生します。 今の時間でLocalDateを生成するにはnow()メソッドを使用します。

LocalDate date = LocalDate.now();// 2019-03-23

LocalDateもLocalDateTimeもタイムゾーンがないのでOSの日時を変更するとその時刻が表示されます。

LocalDateTimeからLocalDateに変換する

LocalDateTime dateTime = LocalDateTime.now();
LocalDate date = LocalDate.of(dateTime.getYear(), dateTime.getMonth(), dateTime.getDayOfMonth());

2022/05/15追記 java.time.temporal.TemporalAccessorインタフェースを使用して、LocalDateTimeからLocalDateに変換することができるようです。

TemporalAccessor temporalAccessor = LocalDateTime.now();
LocalDate date = LocalDate.from(temporalAccessor);

LocalDateの加算減算

LocalDateに日数を加算したい減算したいというケースが出てくると思います。 その場合はplusDays(int)やminusDays(int)で加減算することができます。基本的に用意されているメソッドは同じです。LocalDateTimeは時分秒を加減算するメソッドがあります。

LocalDate date = LocalDate.now();
date = date.plusDays(2); // 2日加算する
System.out.println(date);

LocalDateとLocalDateを比較する

「2000年1月1日より未来日のデータを取得する」みたいな感じでLocalDate同士を比較するときがあると思います。 その場合はisAfterやisBeforeメソッドを使用します。

LocalDate today = LocalDate.now();
LocalDate tomorrow = LocalDate.now().plusDays(1);

today.isAfter(today); // == falseとなるので注意(同一日はfalseとなる)
today.isAfter(tomorrow); // == false
tomorrow.isAfter(today); // == true

today.isBefore(today); // == falseとなるので注意(同一日はfalseとなる)
today.isBefore(tomorrow); // == true
tomorrow.isBefore(today); // == false

isAfterやisBeforeメソッドは同一日を含まないので、「2000年1月1日含む未来日のデータを取得する」という場合はequalsメソッドを併用しないと期待した動きとなりません。

LocalDate today = LocalDate.of(2000,1,1);
LocalDate date = LocalDate.of(2000,1,1);
today.isBefore(date) || today.equals(date); // == true

日付フォーマットする

LocalDateもLocalDateTimeも日付フォーマットするには、DateTimeFormatterクラスを使用します。戻り値はString型になります。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date = LocalDate.of(2019,3,23);
System.out.println(date.format(formatter)); // 2019/03/23

parseメソッドを使用して日付フォーマットする

formatメソッドだとString型に変換されてしますので、LocalDate.parseメソッドを使用して日付フォーマットします。この場合は戻り値はLocalDate型になります。

LocalDate.parse("2019/02/29",DateTimeFormatter.ofPattern("yyyy/MM/dd")); // 2019/02/28となってインスタンス生成される

良きに計らってくれるようですが、第一引数が文字列で動的に来ると考えると、エラーとしたい場合があると思います。 その場合はyyyyをuuuuに変更してwithResolverStyle(ResolverStyle.STRICT)メソッドを指定します。

LocalDate.parse("2019/11/31",
DateTimeFormatter
.ofPattern("uuuu/MM/dd")
.withResolverStyle(ResolverStyle.STRICT)); // java.time.DateTimeException: Invalid date 'NOVEMBER 31'

DateTimeFormatterのhhとHHの違い

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss");
LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2019, 3, 23), LocalTime.of(23, 50, 1, 1));
System.out.println(dateTime.format(formatter)); // 2019/03/23 11:50:01となってしまう

23と指定するとエラーとならずに11になってしまいます。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2019, 3, 23), LocalTime.of(23, 50, 1, 1));
System.out.println(dateTime.format(formatter)); // 2019/03/23 23:50:01と意図したとおり表示される

HHで指定すると23は23として表示されます。 ちなみにこれをLocalDateTime.parseメソッドで行うとエラーとなります。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("2019/03/23 23:50:01",formatter);
System.out.println(dateTime.format(formatter)); // DateTimeParseException: Text '2019/03/23 23:50:01' could not be parsed: Invalid value for ClockHourOfAmPm (valid values 1 - 12): 23

LocalDateとLocalDateの比較

LocalDateとLocalDateの比較にはequalsメソッドを使用します。(isEqualメソッドでも可能)

LocalDate ld1 = LocalDate.of(2010, 1, 1);
LocalDate ld2 = LocalDate.parse("2010-01-01");
System.out.println(ld1.equals(ld2)); // trueと表示されます
LocalDate ld3 = LocalDate.parse("2019/01/01",DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(ld1.isEqual(ld3)); // falseと表示されます

Periodクラス

日にちを扱っているとjava.time.Periodクラスも出てくると思います。 Periodクラスは日にちの量を表すクラスで、betweenメソッドやofメソッドでインスタンス生成します。以下、betweenメソッドを使用しています。

LocalDate ld1 = LocalDate.of(2010, 1, 1);
LocalDate ld2 = LocalDate.parse("2019/02/03",DateTimeFormatter.ofPattern("yyyy/MM/dd"));

Period period = Period.between(ld1, ld2);
System.out.println(period); // P9Y1M2D 9年1ヶ月2日の差がある
System.out.println(period.getYears()); // 9
System.out.println(period.getMonths()); // 1
System.out.println(period.getDays()); // 2
System.out.println(period.toTotalMonths()); // 109

toTotalMonthsメソッドは9年1ヶ月=109ヶ月を返します。その他はメソッド名通りです。 以下、ofメソッドを使用しています。

Period period = Period.of(10, 9, 8);
System.out.println(period); // P10Y9M8D 10年9ヶ月8日の差がある
System.out.println(period.getYears()); // 10
System.out.println(period.getMonths()); // 9
System.out.println(period.getDays()); // 8
System.out.println(period.toTotalMonths()); // 129

Durationクラス

Periodクラスに対してDurationクラスは時間の量を表すクラスです。 以下、betweenメソッドを使用しています。

LocalDateTime ld1 = LocalDateTime.of(2010, 1, 1, 0, 0);
LocalDateTime ld2 = LocalDateTime.parse("2010/01/02 112040",DateTimeFormatter.ofPattern("yyyy/MM/dd HHmmss"));

Duration duration = Duration.between(ld1, ld2);
System.out.println(duration); //PT35H20M40S -> 35時間20分40秒の差がある
System.out.println(duration.toDays()); // 1日
System.out.println(duration.toHours()); // 35時間
System.out.println(duration.toMinutes()); // 2120分
System.out.println(duration.getSeconds()); // 127240秒

LocalDateをLocalDateTimeの00:00を求めるatStartOfDayメソッド

LocalDateからLocalDateTimeに変換する方法として、00:00にするのならatStartOfDayメソッドを使用することでその日にちの00:00のLocalDateTimeに変換することができます。

LocalDate ld = LocalDate.of(2019, Month.APRIL, 1);
System.out.println((ld.atStartOfDay())); // 2019-04-01T00:00

OffsetDateTimeクラス

OffsetDateTimeクラスは時差ありの日付を表すクラスです。 2020-09-15T19:16:39.265866700+09:00というようにJSTの場合+09:00がついたり、UTCの場合は2020-09-15T10:16:39.265866700Zというように末尾にZが付きます。 また、LocalDateTimeのインスタンスをOffsetDateTimeクラスのインスタンスに変換することも可能です。

package jp.co.offsetdatetimes;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;

public class OffsetDateTimeSample {
  public static void main(String[] args) {
    // JST表示
    OffsetDateTime odtjst = OffsetDateTime.now(ZoneId.of("Asia/Tokyo")); // JST
    System.out.println(odtjst); // 2020-09-15T19:16:39.265866700+09:00

    // JSTをUTCに変換
    OffsetDateTime odtutc = OffsetDateTime.ofInstant(odtjst.toInstant(), ZoneId.of("UTC")); // UTC
    System.out.println(odtutc); // 2020-09-15T10:16:39.265866700Z

    // LocalDateTimeをOffsetDateTimeに変換
    LocalDateTime ldt = LocalDateTime.now();
    ZoneOffset offset = ZoneId.of("UTC").getRules().getOffset(odtjst.toInstant());
    OffsetDateTime odt = OffsetDateTime.of(ldt, offset);
    System.out.println(odt); // 2020-09-15T19:16:39.265866700Z
  }
}

タイムゾーン付きの文字列の日付をLocalDateTimeに変換する

タイムゾーン付きの文字列の日付をLocalDateTimeに変換する例です。 ZonedDateTime,ZoneIdクラスを使用します。

package jp.co.confrage;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;

public class DateTest {
  public static void main(String[] args) throws InterruptedException {
    String inputValue = "20201230T000000+0800";
    ZonedDateTime value =
      ZonedDateTime.parse(
        inputValue,
        DateTimeFormatter.ofPattern("uuuuMMdd'T'HHmmssZ")
          .withChronology(IsoChronology.INSTANCE)
          .withResolverStyle(ResolverStyle.STRICT));
    ZonedDateTime jst = value.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
    System.out.println(jst); // 2020-12-30T01:00+09:00[Asia/Tokyo]
    var dt = jst.toLocalDateTime();
    System.out.println(dt); // 2020-12-30T01:00
  }
}

一旦UTCに変換すれば時間はわかりやすいと思います。 |JST|UTC|結果| |:–|:–|:–| |2020/12/30 09:00:00+0900|2020/12/30 00:00:00Z|違う時間| |2020/12/30 10:00:00+0900|2020/12/30 01:00:00Z|同じ時間| |2020/12/30 09:00:00+0800|2020/12/30 01:00:00Z|同じ時間| UTCに変換すれば同じ時間であることがわかります。

文字列の日付妥当性チェック

文字列の日付が厳密に妥当かどうかをparseメソッドでチェックするメソッドです。

private boolean validateLocalDateTime(final String inputDateValue) { // "20200229T124440+0900"
  try {
    final LocalDateTime value =
    LocalDateTime.parse(
      inputDateValue,
      DateTimeFormatter.ofPattern("uuuuMMdd'T'HHmmssZ")
        .withChronology(IsoChronology.INSTANCE)
        .withResolverStyle(ResolverStyle.STRICT));
    return true;
  } catch (final DateTimeParseException e) {
    e.printStackTrace();
    return false;
  }
}

LocalDateTimeをjava.util.Dateに変換

あまりこの変換をするケースはないと思いますが、以下のようにtoDateメソッドを使用して変換することが出来ます。

 LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date dt = Date.from(zdt.toInstant()); // Fri Oct 30 11:30:15 GMT+09:00 2020

日付の過去未来の複数条件の判定

複数条件の日付判定を書くときは、isAfterやisBeforeを使います。 「父の誕生日>母の誕生日>兄の誕生日>弟の誕生日」の整合性が取れている確認です。 streamとallMatchメソッドを使って書いています。

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Main {

  public static void main(String[] args) {
    final LocalDateTime dt1 = LocalDateTime.of(1970, 1, 1, 0, 0); // 父 一番過去日
    final LocalDateTime dt2 = LocalDateTime.of(1975, 1, 1, 0, 0); // 母
    final LocalDateTime dt3 = LocalDateTime.of(2000, 1, 1, 0, 0); // 兄
    final LocalDateTime dt4 = LocalDateTime.of(2003, 1, 1, 0, 0); // 弟 一番未来日

    final List<LocalDateTime> lists = new LinkedList<>();
    lists.add(dt1);
    lists.add(dt2);
    lists.add(dt3);
    lists.add(dt4);
    if (lists.size() == 1) {
      return;
    }
    // 父 < 母 < 兄 < 弟となっている事のチェック
    final List<Boolean> checkResults = new ArrayList<>();
    for (int i = 0; i < lists.size() - 1; i++) {
      System.out.println(lists.get(i).isBefore(lists.get(i + 1)));
      checkResults.add(lists.get(i).isBefore(lists.get(i + 1))); // true,true,true
    }
    final boolean result = checkResults.stream().allMatch(e -> e.equals(Boolean.TRUE));
    System.out.println(result); // true
  }
}

コメント

  1. 佐藤哲也 より:

    LocalDateTime から LocalDate に変換するのは、 LocalDate.from(TemporalAccessor) を使うのが手っ取り早いと思います。
    LocalTime への変換も同様ですね。

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