【Java】日付/時刻の操作

#日付/時刻の操作
* **Date-Time-API**を使おう(Java8以降)

#現在時間取得
* **LocalDate**は日時
* **LocalTime**は時間

```java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;

public class Main {
  //現在日時・時刻を取得
  public static void main(String[] args) {
    System.out.println(LocalDateTime.now());  //2020-10-31T07:49:16.502455881
    System.out.println(OffsetDateTime.now()); //2020-10-31T07:49:16.503291965Z
    System.out.println(ZonedDateTime.now());  //020-10-31T07:49:16.504062367Z[Etc/UTC]
    System.out.println(LocalDate.now());      //2020-10-31
    System.out.println(LocalTime.now());      //07:49:16.504349859
  }
}
```

#特定の日時・時刻を取得
* **LocalDateTime.ofメソッド**

```java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;


public class Main {
  //特定の日時・時刻を取得
  public static void main(String[] args) {
    //年月日・時分秒ナノ秒から日時を生成
    var ldt1 = LocalDateTime.of(2019, 1, 10, 10, 20, 30, 513);
    var ldt2 = LocalDateTime.of(2019, Month.JANUARY, 10, 10, 20, 30);
    //var ldt3 = LocalDateTime.of(2019, 1, 40, 10, 20, 30); //範囲外なのでエラー 
    System.out.println(ldt1); //2019-01-10T10:20:30.000000513
    System.out.println(ldt2); //2019-01-10T10:20:30.000000513

    //日付・時間のみ生成
    var ld = LocalDate.of(2019, 1, 10);
    System.out.println(ld);   //2019-01-10
    var lt = LocalTime.of(10, 20, 30);
    System.out.println(lt);  //10:20:30
    var ldt4 = LocalDateTime.of(ld, lt);
    System.out.println(ldt4); //2019-01-10T10:20:30

    //タイムゾーンオフセット値(ZoneOffsetオブジェクト)
    var odt = OffsetDateTime.of(2019, 1, 10, 10, 20, 30, 999, ZoneOffset.ofHours(9));
    System.out.println(odt); //2019-01-10T10:20:30.000000999+09:00

    var ot = OffsetTime.of(10, 20, 30, 999, ZoneOffset.ofHours(9));
    System.out.println(ot); //10:20:30.000000999+09:00

    var zdt = ZonedDateTime.of(2019, 1, 10, 10, 20, 30, 999, ZoneId.of("Asia/Tokyo"));
    System.out.println(zdt); //2019-01-10T10:20:30.000000999+09:00[Asia/Tokyo]
  }
}
```

#日付時刻文字列を変換
* **parseメソッド**
    * formatterを指定すると、指定形式で解析された値を取得できる

```java
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;


public class Main {
  //日付・時刻文字列から変換
  public static void main(String[] args) {
    //ISO日付
    System.out.println(LocalDate.parse(
        "2019-01-01", DateTimeFormatter.ISO_DATE)); //2019-01-01
    //年と日数
    System.out.println(LocalDate.parse(
        "2019-123", DateTimeFormatter.ISO_ORDINAL_DATE)); //2019-05-03
    //年と週数
    System.out.println(LocalDate.parse(
        "2019-W40-5", DateTimeFormatter.ISO_WEEK_DATE)); //2019-10-04
    //タイムゾーンID付き日時
    System.out.println(ZonedDateTime.parse(
      "2019-01-10T10:20:30.000000999+09:00[Asia/Tokyo]",
      DateTimeFormatter.ISO_DATE_TIME)); //2019-01-10T10:20:30.000000999+09:00[Asia/Tokyo]
  }
}
```

#日付時刻を比較
* **equals** / **isBefore** / **isAfter**

```java
import java.time.LocalDateTime;
public class Main {
  public static void main(String[] args) {
    var dt1 = LocalDateTime.of(2019, 12, 31, 10, 20, 30);
    var dt2 = LocalDateTime.of(2020, 1, 1, 10, 20, 30);
    System.out.println(dt1.equals(dt2));   //false
    System.out.println(dt1.isBefore(dt2)); //true
    System.out.println(dt1.isAfter(dt2));  //false
  }
}
```

#時刻要素取得

```java
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;

public class Main {
  public static void main(String[] args) {
    var dt = LocalDateTime.of(2019, 1, 10, 10, 20, 30, 123);
    var week = new String[] {"日曜日", "月曜日", "火曜日", "水曜日","木曜日", "金曜日", "土曜日"};
    System.out.println(dt.getYear() + "年" +dt.getMonthValue() + "月" +dt.getDayOfMonth() + "日 " +dt.getDayOfWeek() + " " +dt.getHour() + "時" +dt.getMinute() + "分"+ dt.getSecond() + "秒"+ dt.getNano() + "ナノ秒");
    System.out.println("月名は" + dt.getMonth() +" 今年" + dt.getDayOfYear() + "日目"); //月名はJANUARY 今年10日目
    //ChronoFieldで時間取得
    System.out.println(dt.get(ChronoField.YEAR) + "年" +dt.get(ChronoField.MONTH_OF_YEAR) + "月" +dt.get(ChronoField.DAY_OF_MONTH) + "日" +week[dt.get(ChronoField.DAY_OF_WEEK) -1] + " "+dt.get(ChronoField.HOUR_OF_DAY) + "時" +dt.get(ChronoField.MINUTE_OF_HOUR) + "分" +dt.get(ChronoField.SECOND_OF_MINUTE) + "秒" +dt.get(ChronoField.NANO_OF_SECOND) + "ナノ秒");
  }
} //2019年1月10日水曜日 10時20分30秒123ナノ秒
```

#日付時間を整形
* **DateTimeFormatterメソッド**の引数に応じて整形

```java
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

public class Main {
  public static void main(String[] args) {
    var dt1 = LocalDateTime.of(2020, 11, 1, 10, 20, 30);
    var dt2 = ZonedDateTime.of(2019, 11, 1, 10, 20, 30, 0, ZoneId.of("Asia/Tokyo"));

    System.out.println(dt1.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));      //Sunday, November 1, 2020
    System.out.println(dt2.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)));  //November 1, 2019 at 10:20:30 AM JST
    System.out.println(dt1.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));    //Nov 1, 2020
    System.out.println(dt2.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT))); //11/1/19, 10:20 AM
  }
}
```
* **DateTimeFormatter.ofPatternメソッド**で好きな形に整形
    * 標準フォーマットの方が、ロケールに合わせて適切な書式(英語とか)にしてくれるのでおすすめ

```Java
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
  public static void main(String[] args) {
    var dt1 = LocalDateTime.of(2020, 11, 1, 10, 20, 30);
    var dt2 = ZonedDateTime.of(2020, 11, 1, 10, 20, 30, 0, ZoneId.of("Asia/Tokyo"));
    System.out.println(dt1.format(DateTimeFormatter.ofPattern("y.MM.dd H:m:s"))); //2020.11.01 10:20:30
    System.out.println(dt2.format(DateTimeFormatter.ofPattern("Y年L月d日（E）a K時m分s秒 (z)"))); //2020年11月1日（Sun）AM 10時20分30秒 (JST)
  }
}
```

* **JapaneseDate.ofメソッド**で元号から日付生成
    * `var d = JapaneseDate.of(JapaneseEra.REIWA, 1, 1, 1);`はError

```java
//和暦
import java.time.chrono.JapaneseDate;
import java.time.chrono.JapaneseEra;
import java.time.format.DateTimeFormatter;

public class Main {
  public static void main(String[] args) {
    var d = JapaneseDate.of(JapaneseEra.REIWA, 2, 1, 1);
    System.out.println(d); //Japanese Reiwa 2-01-01
    //Gが元号
    var df = DateTimeFormatter.ofPattern("Gy年MM月dd日");
    System.out.println(d.format(df)); //Reiwa2年01月01日
  }
}
```

#日付時刻の差分差分
* Period(日付) / Duration(時間) クラスの**betweenメソッド**
    * Periodの引数は一旦toLocalDateでLocalDateに変換する必要
    * Durationの引数はTemporal型:LocalDateやZonedDate系のインスタンスを全部受け取れる

```java
//日数経過カウント
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;

public class Main {
  public static void main(String[] args) {
    var dt1 = LocalDateTime.of(2019, 12, 31, 0, 0, 0);
    var dt2 = LocalDateTime.of(2020, 11, 1, 10, 20, 30);

    var period = Period.between(dt1.toLocalDate(), dt2.toLocalDate());
    System.out.println("日付の差：" +
      period.getYears() + "年" + period.getMonths() + "ヶ月" +
      period.getDays() + "日間"); //日付の差：0年10ヶ月1日間

    var duration = Duration.between(dt1, dt2);
    System.out.println("時間の差：" + duration.toHours() + "時間"); //時間の差：7354時間
  }
}
```

#日付加算減算
* **plus / minus メソッド**
    * **plusYears / plusWeeks** のように時間計算に特化したメソッドもある

```java
//x年後,y日前の日付
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Main {
  public static void main(String[] args) {
    var d = LocalDate.of(2020, 11, 1);
    System.out.println(d); //2020-11-01
    //3年後
    System.out.println(d.plus(3, ChronoUnit.YEARS)); //2023-11-01
    //22日前
    System.out.println(d.minus(22, ChronoUnit.DAYS)); //2020-10-10
  }
}
```
* Period/Durationを渡して日付間隔で加算減算
    * Period:**P<日付>**
        * `P2Y2M`=2年2ヶ月
    * Duration:**P<日付>T<時間>**
        * `-P1DT5M`=1日5分前
   
```java
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
public class Main {
  public static void main(String[] args) {
    var d = LocalDateTime.of(2020, 11, 1, 10, 20, 30);
    var period = Period.ofYears(3);
    //P<日付>T<時間>
    var duration = Duration.parse("P22DT1H1M1S"); //22日1時間1分1秒
    System.out.println(d);  //2020-11-01T10:20:30
    System.out.println(d.plus(period)); //2023-11-01T10:20:30
    System.out.println(d.minus(duration)); //2020-10-10T09:19:29
  }
}
```

#Calenderクラス(Java7以前)
* **toInstantメソッド**でCalendeからDate-TimeAPIに変換
    * toInstantメソッド:1970/1/1からの経過時間を保持
* **ofInstantメソッド**でLocalDateTimeなどのオブジェクト生成

```java
//CalenderからDate-TimeAPIに変換
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;

public class Main {
  public static void main(String[] args) {
    var cal = Calendar.getInstance();
    var dt1 = LocalDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault());
    var dt2 = OffsetDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault());
    var dt3 = ZonedDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault());
    System.out.println(dt1); //2020-11-01T03:30:12.156
    System.out.println(dt2); //2020-11-01T03:30:12.156Z
    System.out.println(dt3); //2020-11-01T03:30:12.156Z[Etc/UTC]
  }
}
```

* **Date-TimeAPIからCalenderに変換**
* Calendarの精度はミリ秒なのでミリ秒以下切り捨て
    * 生成したInstantオブジェクトをDate.fromメソッドで受け取り**Dateオブジェクト**生成
    * **setTimeメソッド**でDateからCalendarに値を入れる

```java
//Date-TimeAPIからCalenderに変換
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.Date;

public class Main {
  public static void main(String[] args) {
    var dt = LocalDateTime.of(2020, 11, 1, 10, 20, 30, 123456789);
    var d = Date.from(dt.toInstant(ZoneOffset.of("+09:00")));
    //setTimeメソッドでDateからCalendarに値を入れる
    var cal = Calendar.getInstance();
    cal.setTime(d);
    System.out.println(cal); //java.util.GregorianCalendar[time=1604193630123,,(略),,AM_PM=0,HOUR=1,HOUR_OF_DAY=1,MINUTE=20,SECOND=30,MILLISECOND=123,ZONE_OFFSET=0,DST_OFFSET=0]
    }
}
```