11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Java などでの日付処理について

Last updated at Posted at 2017-12-17
1 / 22

2017年9月21日 Java 9 リリース🎉


ですが今日は Java8 で導入された Date and Time APIについて話します


歴史

Javaには java.util.Date が @Since 1.0 から存在していた。
Java 1.0 は1996年
DateはEpochからのミリ秒を保持。タイムゾーンに関する情報はもってない。
Mutableなのでよく事故が起こる
関数の引数にDateを渡したら、関数内部で書き換えられる可能性があるので
防御的コピー (Defensive copy)をした上で渡す必要があったりする。


Date

import java.text.SimpleDateFormat;
import java.util.Date;

public class Aaa {
   public static void main(String[] args) throws Exception {
       Date today = new Date();
       System.out.println("Today is: " + new SimpleDateFormat("yyyy-MM-dd").format(today));

       // 昨日の日付を表示したいな
       昨日の日付を表示(today);

       // 今日の日付を表示したいな
       System.out.println("Today is: " + new SimpleDateFormat("yyyy-MM-dd").format(today));
   }

   public static void 昨日の日付を表示(Date date) {
       // 昨日の日付を作る
       date.setTime(date.getTime() - 24L * 60 * 60 * 1000);
       System.out.println("Yesterday is: " + new SimpleDateFormat("yyyy-MM-dd").format(date));
   }
}

結果

Today is: 2017-12-19
Yesterday is: 2017-12-18
Today is: 2017-12-18

Calendarの時代

Java 1.1 で java.util.Calendar が追加された。
Java 1.1 は1997年
実質的に Calendar == GregorianCalendar
JDK 1.1 における国際化対応の一環として導入された
CalendarはEpochからのミリ秒(Date)とタイムゾーンに関する情報を保持
がAPIが酷い
これまたjava.util.Dateと同じくMutableなのでよく事故が起こる


日本時間の2001年12月25日の14時ちょうどのCalendarをつくりたいんだけど・・

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class Bbb {
   public static void main(String[] args) throws Exception {
       Calendar cal = Calendar.getInstance();
       cal.set(Calendar.YEAR, 2001);
       cal.set(Calendar.MONTH, 11); // 0 はじまり
       cal.set(Calendar.DAY_OF_MONTH, 25);
       cal.set(Calendar.HOUR_OF_DAY, 14); // Calendar.HOUR を使うとAM/PMがおかしくなる
       cal.set(Calendar.MINUTE, 0);
       cal.set(Calendar.SECOND, 0);
       cal.set(Calendar.MILLISECOND, 0);

       System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(cal.getTime()));
   }
}

長い冬の時代

Apache (Jakarta) commons-langのDateUtilsでがんばる日々・・
joda-time という選択肢もあった
2003年頃出てきた .Net Framework1.1 のDateTimeはしっかりImmutableになってた。

余談、Java 6.0 (2006年) で和暦を扱うJapaneseImperialCalendarが追加


新しい夜明け

Java 8.0 (2014年) で Date and Time API が追加された。
Joda-Time (初出は2002年ごろ?)をベースに JSR-310として策定された
Immutable
java.time パッケージで提供


Date and Time API

中心となるクラス

LocalDate

年月日を保持するクラス。ISO8601がベース
先発グレゴリオ暦で扱う
java.util.Dateはユリウス暦とグレゴリオ暦の切り替わりがある。

LocalTime

時分秒(精度はナノ秒)を保持するクラス。タイムゾーンを持たない

LocalDateTime

LocalDate + LocalTime のクラス。

ZoneId

Asia/Tokyo などの地理的なタイムゾーン情報を保持
java.util.TimeZoneがあったが、新たに作成された
単なるOffset情報だけでなく、夏時間の情報も保持
単なるOffsetを扱うためにZoneOffsetというサブクラスがある。

Instant

意味:瞬時、瞬間、(特定の)時点

ZonedDateTime

LocalDate + LocalTime + ZoneId


先発グレゴリオ暦とは・・・

「1582年10月から施行されたグレゴリオ暦の暦法を、1582年以前にも適用したもの」
https://ja.wikipedia.org/wiki/%E5%85%88%E7%99%BA%E3%82%B0%E3%83%AC%E3%82%B4%E3%83%AA%E3%82%AA%E6%9A%A6
ISO8601は先発グレゴリオ暦

1582年以前は処理系によって扱いは様々

ISO8601は西暦1年より前の扱いは規定していない
JavaのLocalDateは西暦1年の前年は0年で扱っている
LocalDate.of(1,1,1).minusDays(1) -> 0000-12-31
Postgresは1年の前年は-1年


データベースでの日付の扱い

DATE型といってもDBMSごとに扱いは様々

標準SQLでは、日付時間関連のデータ型は

  • DATE型(日付)
  • TIME型(時刻)
  • TIMESTAMP型(日付と時刻を足したもの)
    TIMEとTIMESTAMPはタイムゾーンは保持しない

Oracleでは・・

日付時間関連のデータ型は四種類

DATEは内部的にユリウス日+秒を保持している。7byte。範囲は-4712/01/01 ~ 9999/12/31
ユリウス暦とグレゴリオ暦を使っている。
西暦1年の前年は0年、0年の前年を「西暦前1年」と言っている。


Postgresでは・・

  • DATE型(日付)
  • TIME型(時刻)
  • TIMESTAMP型(日付と時刻を足したもの)
    TIMEとTIMESTAMPはタイムゾーンの有り無しを指定できる

Oracleと同じくユリウス日 紀元前4713/01/01 ~ 294276/12/31
先発グレゴリオ暦を使用
西暦1年の前年は1 BC、1 BCの前年は2 BC
0001-01-01の前日は '0001-12-31 BC'


MySQLでは

  • DATE型(日付)0年月日
  • TIME型(時刻) '00:00:00' マイクロ秒まで。タイムゾーンない
  • DATETIME型(日付と時刻を足したもの) タイムゾーンない

DATEは3byteで格納。'1000-01-01' から '9999-12-31'
先発グレゴリオ暦を使用


その他


Git

Epochからの秒数とタイムゾーンを保持


Apache Spark

日付時間関連は2種類


Parquet

日付時間関連は3種類


まとめ

日時の処理をするときは処理系ごとの動きを把握しよう
型のタイムゾーンの有無を考慮しよう
夏時間があってもちゃんと動きますか?
できれば処理するマシンのタイムゾーンを変更しても問題ないような作りにする


実行時点での日本のローカル日時を取得する
LocalDate.now(ZoneId.of("Asia/Tokyo"))
ZoneId.getAvailableZoneIds.asScala.foreach(println)
JapaneseDate.of(1900,1,1).getEra

Oracleでは・・
日付時間関連のデータ型は四種類

内部的にユリウス日を保持している。-4712/01/01 ~ 9999/12/31
ユリウス暦とグレゴリオ暦を使っている。
TO_CHAR(DATE'2001-01-01','J')
2451911

TO_CHAR (DATE '0001-01-01', 'J')
1721424

TO_CHAR(DATE'1582-10-04','J')
2299160
TO_CHAR(DATE'1582-10-15','J')
2299161

Postgresでは・・
Oracleと同じくユリウス日 紀元前4713/01/01 ~ 294276/12/31
先発グレゴリオ暦を使用
postgres=# SELECT TO_CHAR (DATE '2001-01-01', 'J');
to_char

2451911
(1 row)

postgres=# SELECT TO_CHAR (DATE '0001-01-01', 'J');
to_char

1721426
(1 row)

postgres=# SELECT TO_CHAR (DATE '1582-10-04', 'J');
to_char

2299150

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?