S-JIS[2014-07-19/2022-08-14] 変更履歴

Java日付時刻API

Javaの日付時刻APIについて。


概要

JDK1.8で新しい日付時刻API(Date and Time API)(java.timeパッケージ)が導入された。

これまで日付時刻に使っていたjava.util.Datejava.util.Calendarjava.text.SimpleDateFormatとは全くの別物。
新APIでは従来のDateやCalendarは一切出てこない。

新旧APIの対比
内容 説明
値の保持 java.util.Date
java.util.Calendar
java.util.Dateは日付の演算(日数の加算とか)を行うことが出来ない。
Calendarは不変オブジェクトではないので、演算を行うと自分自身が変更された。
java.time.ZonedDateTime
java.time.LocalDate
ZonedDateTimeやLocalDate等は不変オブジェクトである。
それぞれのクラスは演算用のメソッドを持っており、演算すると新しい不変オブジェクトを返す。
また、これらのクラスはスレッドセーフである。
整形 java.text.SimpleDateFormat SimpleDateFormatはスレッドセーフではない上に、コンストラクターの実行が遅め。
java.time.format.DateTimeFormatter DateTimeFormatterはスレッドセーフであり、SimpleDateFormatより動作が軽い。
タイムゾーン java.util.TimeZone 「Asia/Tokyo」や「+09:00」といった情報を持つ。
java.time.ZoneId
java.time.ZoneOffset
ZoneIdが「Asia/Tokyo」という情報を持つ。
ZoneOffsetが「+09:00」という情報を持つ。
要素名 java.util.Calendar Calendar.YEAR
Calendar.MONTH
Calendar.DATE(あるいはCalendar.DAY_OF_MONTH)

これらは整数値(int)である。
java.time.temporal.ChronoField
(java.time.temporal.TemporalField)
ChronoField.YEAR
ChronoField.MONTH_OF_YEAR
ChronoField.DAY_OF_MONTH

これは列挙型である。
java.time.temporal.ChronoUnit
(java.time.temporal.TemporalUnit)
ChronoUnit.YEARS (年数)
ChronoUnit.MONTHS (月数)
ChronoUnit.DAYS (日数)

これは列挙型である。
TimeUnitとChronoUnitは、TimeUnit.XXX.toChronoUnit()やTimeUnit.of(ChronoUnit.XXX)で変換できる(Java9以降)[2022-08-14]

日付・時刻クラス

日付や時刻等の「ある時点」を表すクラスは、java.time.temporal.Temporal(TemporalAccessor)インターフェースのサブクラスになっている。

日付(年月日)を表すクラスだけでなく、年・年月・月・月日を表すクラスまで別々に用意されている。
また、日付(年月日)と日時(年月日時分秒)の他に、時刻(時分秒)を表すクラスも個別に存在する。


日時を表すクラスには、Local・Offset・Zonedの3種類が存在する。

Local
タイムゾーンを持たない日時。例えば「1時」と言った場合、日本の1時なのかアメリカの1時なのかは不明。
Offset
UTC(グリニッジ)からのオフセット(差分)を持つ日時。日本の場合は「+09:00」。
Zoned
タイムゾーン(「日本時間」とかの情報)を持つ日時。日本の場合は「Asia/Tokyo」。

Zonedには夏時間の考慮が入っているようだ。
したがって、日本時間を扱う場合はOffsetでもZonedでも大差ないと思うが、日時を保持したい場合は基本的にはZonedを使えばよいと思う。
※とりあえず何を使えばよいか→dmikurubeさんのタイムゾーン呪いの書 [2018-02-06]


import java.time.*;
日付・時刻クラス
要素 クラス名 親インターフェース 説明 保持する値の例
日時
(年月日時分秒)
LocalDateTime   Temporal タイムゾーンを持たない日時。
内部ではLocalDateとLocalTimeで保持している。
2014-07-19T11:20:18.357
OffsetDateTime   Temporal “UTCからのオフセット”を持つ日時。
内部ではLocalDateTimeとZoneOffsetで保持している。
2014-07-19T11:20:18.357+09:00
ZonedDateTime   Temporal タイムゾーンを持つ日時。
内部ではLocalDateTimeとZoneOffsetZoneIdで保持している。
2014-07-19T11:20:18.358+09:00[Asia/Tokyo]
日付(年月日) LocalDate   Temporal 日付。 2014-07-19
時刻(時分秒) LocalTime   Temporal “UTCからのオフセット”を持たない時刻。(ナノ秒まで保持可能) 11:20:18.357
OffsetTime   Temporal “UTCからのオフセット”を持つ時刻。
内部ではLocalTimeとZoneOffsetで保持している。
11:20:18.358+09:00
Year   Temporal 年。 2014
年月 YearMonth   Temporal 年月。 2014-07
月日 MonthDay   TemporalAccessor 月日。 --07-19
Month enum TemporalAccessor 月。 JULY
曜日 DayOfWeek enum TemporalAccessor 曜日。
数値にすると、月曜が1で土曜が6、日曜が7。
Calendarは日曜が1で月曜が2、土曜が7)
SATURDAY
日時
(エポック秒)
Instant   Temporal 1970-01-01を基準(エポック)とするエポック秒で保持する日時。秒以下(ナノ秒)も保持可能。
java.util.Dateと相互変換できる。
2014-07-19T02:20:18.358Z

ほとんどの日付時刻クラス同士は、変換用のメソッドを用いて変換できる。

変換元のクラス 変換先のクラス
ZonedDateTime OffsetDateTime LocalDateTime LocalDate OffsetTime LocalTime Instant long
ZonedDateTime zdt.withZoneSameInstant(zondId)
zdt.withZoneSameLocal(zondId)
zdt.toOffsetDateTime() zdt.toLocalDateTime() zdt.toLocalDate()   zdt.toLocalTime() zdt.toInstant()  
OffsetDateTime odt.atZoneSameInstant(zoneId)
odt.atZoneSimilarLocal(zoneId)
  odt.toLocalDateTime() odt.toLocalDate() odt.toOffsetTime() odt.toLocalTime() odt.toInstant() odt.toEpochSecond()
[2017-09-27]
LocalDateTime ldt.atZone(zoneId) ldt.atOffset(offset)   ldt.toLocalDate()   ldt.toLocalTime() ldt.toInstant(offset)  
ZonedDateTime.of(ldt, zoneId)
ZonedDateTime.ofInstant(ldt, offset, zoneId)
OffsetDateTime.of(ldt, offset)
OffsetDateTime.ofInstant(ins, zoneId)
LocalDate ldate.atStartOfDay(zoneId) ldate.atTime(otime) ldate.atStartOfDay()
ldate.atTime(ltime)
ldate.atTime(h, m, s)
        ldate.toEpochSecond(ltime, offset)
Java9[2017-09-27]
ZonedDateTime.of(ldate, ltime, zoneId) OffsetDateTime.of(ldate, ltime, offset) LocalDateTime.of(ldate, ltime)
OffsetTime   otime.atDate(ldate)       otime.toLocalTime()   otime.toEpochSecond(ldate)
Java9[2017-09-27]
LocalTime     ltime.atDate(ldate)   ltime.atOffset(offset)     ltime.toEpochSecond(ldate, offset)
Java9[2017-09-27]
ZonedDateTime.of(ldate, ltime, zoneId) OffsetDateTime.of(ldate, ltime, offset)     OffsetTime.of(ltime, offset)
Instant ins.atZone(zoneId) ins.atOffset(offset)            
ZonedDateTime.ofInstant(ins, zoneId) OffsetDateTime.ofInstant(ins, zoneId) LocalDateTime.ofInstant(ins, zoneId) LocalDate.ofInstant(ins, zoneId)
Java9[2017-09-27]
OffsetTime.ofInstant(ins, zoneId) LocalTime.ofInstant(ins, zoneId)
Java9[2017-09-27]

直接の変換メソッドが無い場合でも、fromメソッドを使えば(必要な情報が存在している場合は)変換できる。
fromメソッドはどんな日付時刻クラスも引数に指定できる(引数の型がTemporalAccessorインターフェースになっている)が、対応していない変換の場合は例外が発生するので 、コンパイル時にチェックされるto系やat系のメソッドを使う方がいいだろう。

		OffsetTime otime = OffsetTime.from(zdt);
変換元のクラス 変換先のクラス
Year YearMonth Month MonthDay DayOfWeek
ZonedDateTime Year.from(zdt) YearMonth.from(zdt) zdt.getMonth() MonthDay.from(zdt) zdt.getDayOfWeek()
OffsetDateTime Year.from(odt) YearMonth.from(odt) odt.getMonth() MonthDay.from(odt) odt.getDayOfWeek()
LocalDateTime Year.from(ldt) YearMonth.from(ldt) ldt.getMonth() MonthDay.from(ldt) ldt.getDayOfWeek()
LocalDate Year.from(ldate) YearMonth.from(ldate) ldate.getMonth() MonthDay.from(ldate) ldate.getDayOfWeek()
OffsetTime          
LocalTime          
Instant          

日付・時刻の使用例

import java.time.*;
import java.time.temporal.*;
内容 備考 類似
現在値の取得
(now)
ZonedDateTime dateTime = ZonedDateTime.now();
LocalDate date = LocalDate.now();
OffsetTime time = OffsetTime.now();
Year year = Year.now();
YearMonth ym = YearMonth.now();
MonthDay md = MonthDay.now();
Instant instant = Instant.now();
現在日付や現在時刻の取得にはnowメソッドを使う。 java.util.Date dateTime = new java.util.Date();
Calendar calendar = Calendar.getInstance();
ZonedDateTime dateTime = ZonedDateTime.now(clock);
LocalDate date = LocalDate.now(clock);
OffsetTime time = OffsetTime.now(clock);
Year year = Year.now(clock);
YearMonth ym = YearMonth.now(clock);
MonthDay md = MonthDay.now(clock);
Instant instant = Instant.now(clock);
Clockを使用して日付や時刻を取得することが出来る。
通常はClock.systemDefault()を使っておいて、テストのときだけ別のClockを使うといった使い方が出来る。
 
各要素を指定して生成
(of)
ZonedDateTime dateTime = ZonedDateTime.of(2014, 7, 19, 23, 45, 56, 0, ZoneId.systemDefault());
LocalDate date = LocalDate.of(2014, 7, 19);
OffsetTime time = OffsetTime.of(23, 45, 56, 0, ZoneOffset.ofHours(+9));
Year year = Year.of(2014);
Month month = Month.of(7);
YearMonth ym = YearMonth.of(2017, 7);
MonthDay md = MonthDay.of(7, 19);
DayOfWeek week = DayOfWeek.of(6); // 土曜日
ofメソッドで各要素を指定して日付時刻オブジェクトを生成する。
(月も、値をそのまま指定する。Calendarでは-1する必要があった)
曜日は、1〜7。1が月曜で6が土曜、7が日曜。
Calendar calendar = Calendar.getInstance();
calendar.set(2014, 7 - 1, 19, 23, 45, 56);
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());    
他クラスへの変換
(to)
OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime();
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
LocalDate date = zonedDateTime.toLocalDate();
LocalTime localTime = zonedDateTime.toLocalTime();
Instant instant = zonedDateTime.toInstant();
to系メソッドで他の日付時刻オブジェクトに変換する。  
OffsetTime offsetTime = offsetDateTime.toOffsetTime();
ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();
java.util.Date date = java.util.Date.from(zonedDateTime.toInstant()); java.util.Dateへの変換にはInstantを使用する。 java.util.Date date = calendar.getTime();
LocalDate date = 〜;
java.sql.Date sdate = java.sql.Date.valueOf(date);
java.sql.DateとLocalDateとの変換にはjava.sql.Dateのメソッドを使用する。[2021-02-10]  
java.sql.Date sdate = 〜;
LocalDate date = sdate.toLocalDate();
他クラスへの変換
(at)
OffsetDateTime offsetDateTime = localDateTime.atOffset(ZoneOffset.ofHours(9));
ZonedDateTime zoneDateTime = localDateTime.atZone(ZoneId.systemDefault());
at系メソッドは、足りない情報を補って他の日付時刻オブジェクトに変換する。  
// offsetDateTimeが「23:00+10:00」だとすると
ZonedDateTime zoneDateTime = offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()); // 22:00+09:00 Asia/Tokyo
ZonedDateTime zoneDateTime = offsetDateTime.atZoneSimilarLocal(ZoneId.systemDefault()); // 23:00+09:00 Asia/Tokyo
atZoneSameInstantは、Instant(エポック秒)が同じになるように変換する。
atZoneSimilarLocalは、日付と時刻が変わらない(ただ単にタイムゾーン情報を付加する)ようにして変換する。
 
YearMonth ym = YearMonth.from(localDate);
LocalDate firstDate = ym.atDay(1);
LocalDate lastDate = ym.atEndOfMonth();
YearMonthのatDayメソッドは、指定された日にする。不正な日付になる場合はDateTimeExceptionが発生する。
atEndOfMonthメソッドは月末日を返す。
小の月やうるう年にも対応している。
calendar.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
java.util.Date lastDate = calendar.getTime();
LocalDateTime localDateTime = localDate.atTime(23, 34, 56, 0);
LocalDateTime localDateTime = localDate.atTime(localTime);
OffsetDateTime offsetDateTime = localDate.atTime(offsetTime);
LocalDateTime localDateTime = localDate.atStartOfDay();
ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
LocalDateでは、時刻を補って日時オブジェクトに変換する。
atStartOfDayは、時刻(時分秒)を0とする日時オブジェクトを返す。
 
LocalDateTime localDateTime = localTime.atDate(localDate);
OffsetTime offsetTime = localTime.atOffset(ZoneOffset.ofHours(9));
LocalTimeのatDateメソッドは日付を補って日時オブジェクトに変換する。
atOffsetではオフセット補ってOffsetTimeに変換する。
 
他クラスからの変換
(from)
OffsetDateTime offsetDateTime = OffsetDateTime.from(zonedDateTime);
LocalDateTime localDateTime = LocalDateTime.from(zonedDateTime);
LocalDate date = LocalDate.from(zonedDateTime);
OffsetTime offsetTime = OffsetTime.from(zonedDateTime);
LocalTime localTime = LocalTime.from(zonedDateTime);
他の日付時刻オブジェクトから変換する。
情報が足りない変換を実行するとDateTimeExceptionが発生する。
(例えばZonedDateTime.from()にlocalDateTimeを渡すと、タイムゾーンの情報が無いので例外が発生する)
 
ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault()); java.util.Dateから変換するにはInstantを使用する。 Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
他クラスからの変換
(of)
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);    
文字列化
(format)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS");
String s = dateTime.format(dtf);
書式を指定して文字列化する。
DateTimeFormatter
java.util.Date dateTime = 〜;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
String s = sdf.format(dateTime);
文字列解析
(parse)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("2014/07/19 23:34:45", dtf);
文字列から日付時刻オブジェクトを生成する。
DateTimeFormatter
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
try {
  java.util.Date dt = sdf.parse("2014/07/19 23:34:45");
} catch(ParseException e) {
}
各要素の取得
(get)
int year = dateTime.getYear();
Month month = dateTime.getMonth();
int month = dateTime.getMonthValue();
int day = dateTime.getDayOfMonth();
int hour = dateTime.getHour();
int minute = dateTime.getMinute();
int second = dateTime.getSecond();
int nano = dateTime.getNano();
DayOfWeek week = dateTime.getDayOfWeek(); // 曜日
   
int year = dateTime.get(ChronoField.YEAR);
int month = dateTime.get(ChronoField.MONTH_OF_YEAR);
int day = dateTime.get(ChronoField.DAY_OF_MONTH);
int hour = dateTime.get(ChronoField.HOUR_OF_DAY);
int minute = dateTime.get(ChronoField.MINUTE_OF_HOUR);
int second = dateTime.get(ChronoField.SECOND_OF_MINUTE);
int milli = dateTime.get(ChronoField.MILLI_OF_SECOND);
int micro = dateTime.get(ChronoField.MICRO_OF_SECOND);
int nano = dateTime.get(ChronoField.NANO_OF_SECOND);
int week = dateTime.get(ChronoField.DAY_OF_WEEK); // 曜日
getメソッドは値をintで返す。
有効な値の範囲外の場合は例外が発生する。[2014-07-28]
ミリ秒・マイクロ秒・ナノ秒は、秒以下の値。
(月は、値がそのまま返る。Calendarでは+1する必要があった)
(曜日の数値はCalendarと異なるので注意)
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DATE);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
int milli = calendar.get(Calendar.MILLISECOND);
int week = ((calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7) + 1;
if (date.get(ChronoField.AMPM_OF_DAY) == 0) 午前(AM)は0、午後(PM)は1。[2017-06-10]
AMやPMを表す定数は無さそう
if (calendar.get(Calendar.AM_PM) == Calendar.AM)
long year = dateTime.getLong(ChronoField.YEAR);
long month = dateTime.getLong(ChronoField.MONTH_OF_YEAR);
long day = dateTime.getLong(ChronoField.DAY_OF_MONTH);
long hour = dateTime.getLong(ChronoField.HOUR_OF_DAY);
long minute = dateTime.getLong(ChronoField.MINUTE_OF_HOUR);
long second = dateTime.getLong(ChronoField.SECOND_OF_MINUTE);
long milli = dateTime.getLong(ChronoField.MILLI_OF_SECOND);
long micro = dateTime.getLong(ChronoField.MICRO_OF_SECOND);
long nano = dateTime.getLong(ChronoField.NANO_OF_SECOND);
long week = dateTime.getLong(ChronoField.DAY_OF_WEEK); // 曜日
getLongメソッドは値をlongで返す。
有効な値の範囲内かどうかのチェックは行わない。(→DateTimeFormatter#parseUnresolved()[2014-07-28]
 
日数の取得
(length)
int days = localDate.lengthOfMonth(); // その月の日数
int days = localDate.lengthOfYear(); // その年の日数
月の日数は、その月の最後の日としても使える。
LocalDate#atEndOfMonth
int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); // その月の日数
int days = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); // その年の日数
Year year = Year.from(localDate);
int days = year.length(); // その年の日数
YearMonth ym = YearMonth.from(localDate);
int days = ym.lengthOfMonth(); // その月の日数
int days = ym.lengthOfYear(); // その年の日数
Month month = Month.of(2);
int days = month.length(false); // その月の日数
Monthは年の情報を持っていないので、うるう年かどうかを引数で渡す。
一部要素の変更
(with)
ZonedDateTime dt = dateTime.withYear(9999);
ZonedDateTime dt = dateTime.withMonth(12);
ZonedDateTime dt = dateTime.withDayOfMonth(1);
ZonedDateTime dt = dateTime.withHour(23);
ZonedDateTime dt = dateTime.withMinute(59);
ZonedDateTime dt = dateTime.withSecond(59);
ZonedDateTime dt = dateTime.withNano(0);
with系メソッドは、指定した要素を変更した新しいオブジェクトを返す。  
ZonedDateTime dt = dateTime.with(ChronoField.YEAR, 9999);
ZonedDateTime dt = dateTime.with(ChronoField.MONTH_OF_YEAR, 12);
ZonedDateTime dt = dateTime.with(ChronoField.DAY_OF_MONTH, 1);
ZonedDateTime dt = dateTime.with(ChronoField.HOUR_OF_DAY, 23);
ZonedDateTime dt = dateTime.with(ChronoField.MINUTE_OF_HOUR, 59);
ZonedDateTime dt = dateTime.with(ChronoField.SECOND_OF_MINUTE, 59);
ZonedDateTime dt = dateTime.with(ChronoField.NANO_OF_SECOND, 0);
Calendarは自分自身を書き換えてしまう) calendar.set(Calendar.YEAR, 9999);
calendar.set(Calendar.MONTH, 12 - 1);
calendar.set(Calendar.DATE, 1);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 0);
 
下位要素の切り捨て
(truncate)
// zonedDateTimeが「2014-07-19T23:45:56.001」だったとすると
ZonedDateTime dt = zonedDateTime.truncatedTo(ChronoUnit.DAYS);    //→ 2014-07-19T00:00:00
ZonedDateTime dt = zonedDateTime.truncatedTo(ChronoUnit.HOURS);   //→ 2014-07-19T23:00:00
ZonedDateTime dt = zonedDateTime.truncatedTo(ChronoUnit.MINUTES); //→ 2014-07-19T23:45:00
ZonedDateTime dt = zonedDateTime.truncatedTo(ChronoUnit.SECONDS); //→ 2014-07-19T23:45:56
指定された単位より小さい要素を0にした新しいオブジェクトを返す。  
演算
(plus・minus)
ZonedDateTime dt = dateTime.plusYears(1);
ZonedDateTime dt = dateTime.plusMonths(1);
ZonedDateTime dt = dateTime.plusDays(1);
ZonedDateTime dt = dateTime.plusHours(1);
ZonedDateTime dt = dateTime.plusMinutes(1);
ZonedDateTime dt = dateTime.plusSeconds(1);
ZonedDateTime dt = dateTime.plusNanos(1);
要素に加算するplus系メソッドと、減算するminus系メソッドがある。
いずれも、変更された新しいオブジェクトを返す。
 
ZonedDateTime dt = dateTime.minusYears(1);
ZonedDateTime dt = dateTime.minusMonths(1);
ZonedDateTime dt = dateTime.minusDays(1);
ZonedDateTime dt = dateTime.minusHours(1);
ZonedDateTime dt = dateTime.minusMinutes(1);
ZonedDateTime dt = dateTime.minusSeconds(1);
ZonedDateTime dt = dateTime.minusNanos(1);
 
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.YEARS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.MONTHS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.DAYS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.HOURS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.MINUTES);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.SECONDS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.MILLIS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.MICROS);
ZonedDateTime dt = dateTime.plus(1, ChronoUnit.NANOS);
Calendarは自分自身を書き換えてしまう)
TimeUnitとChronoUnitは、TimeUnit.XXX.toChronoUnit()やTimeUnit.of(ChronoUnit.XXX)で変換できる(Java9以降)[2022-08-14]
calendar.add(Calendar.YEAR, 1);
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.DATE, 1);
calendar.add(Calendar.HOUR_OF_DAY, 1);
calendar.add(Calendar.MINUTE, 1);
calendar.add(Calendar.SECOND, 1);
calendar.add(Calendar.MILLISECOND, 1);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.YEARS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.MONTHS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.DAYS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.HOURS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.MINUTES);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.SECONDS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.MILLIS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.MICROS);
ZonedDateTime dt = dateTime.minus(1, ChronoUnit.NANOS);
calendar.add(Calendar.YEAR, -1);
calendar.add(Calendar.MONTH, -1);
calendar.add(Calendar.DATE, -1);
calendar.add(Calendar.HOUR_OF_DAY, -1);
calendar.add(Calendar.MINUTE, -1);
calendar.add(Calendar.SECOND, -1);
calendar.add(Calendar.MILLISECOND, -1);
Period period = Period.of(1, 2, 3); // 年月日
ZonedDateTime dt = dateTime.plus(period);
ZonedDateTime dt = dateTime.minus(period);
期間・時間オブジェクトを加算・減算することが出来る。
これも変更された新しいオブジェクトを返す。
 
間隔の取得
(until)
long value = dt1.until(dt2, ChronoUnit.YEARS);
long value = dt1.until(dt2, ChronoUnit.MONTHS);
long value = dt1.until(dt2, ChronoUnit.DAYS);
long value = dt1.until(dt2, ChronoUnit.HOURS);
long value = dt1.until(dt2, ChronoUnit.MINUTES);
long value = dt1.until(dt2, ChronoUnit.SECONDS);
dt1からdt2までの間隔を、指定された単位で返す。(年数・月数・日数とか)
dt1よりdt2の方が大きい(新しい)とき正の数になる。
dt1とdt2は異なるクラスでもよい。
 
Period period = date1.until(date2); LocalDateには、期間(Period)を返すuntilメソッドがある。  
間の全日付の取得(datesUntil) LocalDate ldt  = LocalDate.of(2017,  9, 27);
LocalDate ldt2 = LocalDate.of(2017, 10,  1);
Stream<LocalDate> s = ldt.datesUntil(ldt2);
→2017-09-27, 28, 29, 30
Java9以降。[2017-09-27]
間の全日付を取得する。
 
Stream<LocalDate> s = ldt.datesUntil(ldt2, Period.of(0, 0, 2)); // 2日間隔
→2017-09-27, 29
 
比較
(compare)
ZonedDateTime dt1 = ZonedDateTime.of(2014, 7, 19, 23,  4, 56, 0, ZoneId.of("Asia/Tokyo")); //+9:00
ZonedDateTime dt2 = ZonedDateTime.of(2014, 7, 19, 23, 34, 56, 0, ZoneId.of("Australia/Darwin")); //+9:30
boolean equals = dt1.equals(dt2);   //→ false
int compare = dt1.compareTo(dt2);   //→ -1
boolean isEqual = dt1.isEqual(dt2); //→ true
compareToは、オブジェクト内に保持している全要素を使って大小比較する。
compareToの結果とequalsの結果は一致する。
isEqualはエポック秒(とナノ秒)で比較する。
左記の例(タイムゾーン違いの同一日時)だと、equalsとcompareToは不一致になるが、isEqualでは等しくなる。
 
boolean before = dt1.isBefore(dt2); 自分のオブジェクトが指定されたオブジェクトより前かどうかを判定する。
判定方法はisEqualと同様。
java.util.Date dt1 = 〜, dt2 = 〜;
boolean before = dt1.before(dt2);
boolean after = dt1.isAfter(dt2); 自分のオブジェクトが指定されたオブジェクトより後かどうかを判定する。
判定方法はisEqualと同様。
java.util.Date dt1 = 〜, dt2 = 〜;
boolean after = dt1.after(dt2);
うるう年の判定
(isLeap)
boolean leap = localDate.isLeapYear(); うるう年かどうかを返す。  
Year year = Year.from(dt);
boolean leap = year.isLeap();
YearMonth ym = YearMonth.from(dt);
boolean leap = ym.isLeapYear();
boolean leap = Year.isLeap(2012); Yearクラスにあるstaticメソッド。 GregorianCalendar gcalendar = new GregorianCalendar();
boolean leap = gcalendar.isLeapYear(2012);
問い合わせ
(query)
// 月末日かどうか判定する例
boolean b = dt.query(t -> LocalDate.from(t).equals(YearMonth.from(t).atEndOfMonth()));

boolean b = dt.query(t -> t.get(ChronoField.DAY_OF_MONTH) == YearMonth.from(t).lengthOfMonth());
関数を渡して、その実行結果を返す。
関数の型はTemporalQuery<R>、すなわちTemporalAccessorを受け取ってRを返す関数。
TemporalAccessorは日付時刻オブジェクトの親インターフェース。queryの対象である日付時刻オブジェクトが渡ってくる。
(関数の引数がジェネリクスになっていないのが不便だ。
もしかすると、このメソッドはプログラマーが使う為ではなく、他APIから利用する為に用意されているのかもしれない)
 
サポートチェック
(isSupported)
boolean s = dt.isSupported(ChronoField.YEAR);
boolean s = dt.isSupported(ChronoField.MONTH_OF_YEAR);
boolean s = dt.isSupported(ChronoField.DAY_OF_MONTH);
boolean s = dt.isSupported(ChronoField.HOUR_OF_DAY);
boolean s = dt.isSupported(ChronoField.MINUTE_OF_HOUR);
boolean s = dt.isSupported(ChronoField.SECOND_OF_MINUTE);
指定されたフィールドが(getやwithメソッド等で)使用できるかどうかを返す。  
boolean s = dt.isSupported(ChronoUnit.YEARS);
boolean s = dt.isSupported(ChronoUnit.MONTHS);
boolean s = dt.isSupported(ChronoUnit.DAYS);
boolean s = dt.isSupported(ChronoUnit.HOURS);
boolean s = dt.isSupported(ChronoUnit.MINUTES);
boolean s = dt.isSupported(ChronoUnit.SECONDS);
指定された単位が(plusやminusメソッド等で)使用できるかどうかを返す。  
フィールド範囲の取得
(range)
ValueRange range = dt.range(ChronoField.YEAR); //→ -999999999 - 999999999
ValueRange range = dt.range(ChronoField.MONTH_OF_YEAR); //→ 1 - 12
ValueRange range = dt.range(ChronoField.DAY_OF_MONTH); //→ 1 - 31
ValueRange range = dt.range(ChronoField.HOUR_OF_DAY); //→ 0 - 23
ValueRange range = dt.range(ChronoField.MINUTE_OF_HOUR); //→ 0 - 59
ValueRange range = dt.range(ChronoField.SECOND_OF_MINUTE); //→ 0 - 59
指定されたフィールドが取りうる範囲を返す。
ある年や月の固有の日数を取得したい場合はlengthを使う
 

タイムゾーン

タイムゾーンはjava.time.ZoneIdで扱う。
(従来はjava.util.TimeZoneを使っていた)

他に、UTCからのオフセット(「+09:00」みたいなやつ)を扱うZoneOffsetがある。
ZoneOffsetはZoneIdのサブクラスになっている。
ZoneIdもZoneOffsetも不変オブジェクトであり、スレッドセーフである。

OffsetDateTimeOffsetTimeは内部でZoneOffsetを保持している。
ZonedDateTimeは内部でZoneOffsetとZoneIdの2つを保持している。

import java.time.*;
ZoneId・ZoneOffsetの例
クラス 内容 備考 類似(従来の方法)
ZoneId デフォルトZoneId ZoneId zoneId = ZoneId.systemDefault(); 日本の場合は「Asia/Tokyo」になる。

内部ではTimeZone.getDefault()を呼び出している。
つまり、デフォルトタイムゾーンの決定方法は従来と同じ。

TimeZone tz = TimeZone.getDefault();
ID文字列から生成 ZoneId zoneId = ZoneId.of("Asia/Tokyo");   TimeZone tz = TimeZone.getTimeZone("Asia/Tokyo");
SHORT_ID文字列から生成 ZoneId zoneId = ZoneId.of("JST", ZoneId.SHORT_IDS); 「Asia/Tokyo」の指定も可。 TimeZone tz = TimeZone.getTimeZone("JST");
ZonedDateTimeから取得 ZoneId zoneId = zonedDateTime.getZone();    
ZoneId zoneId = ZoneId.from(zonedDateTime);
ZoneId一覧の取得 Set<String> ids = ZoneId.getAvailableZoneIds(); TimeZoneでは略称(JSTとか)も取れるが、ZoneIdでは取れない。 String[] ids = TimeZone.getAvailableIDs();
ZoneOffset ID文字列から生成 ZoneOffset offset = ZoneOffset.of("+09:00");    
時間から生成 ZoneOffset offset = ZoneOffset.ofHours(9);
ZoneOffset offset = ZoneOffset.ofHoursMinutes(9, 0);
ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(9, 0, 0);
ZoneOffset offset = ZoneOffset.ofTotalSeconds(9 * 60 * 60);
   
ZoneIdから取得 ZoneOffset offset = zoneId.getRules().getOffset(Instant.EPOCH);    
OffsetDateTime等から取得 ZoneOffset offset = zonedDateTime.getOffset();
ZoneOffset offset = offsetDateTime.getOffset();
ZoneOffset offset = offsetTime.getOffset();
   
ZoneOffset offset = ZoneOffset.from(zonedDateTime);
ZoneOffset offset = ZoneOffset.from(offsetDateTime);
ZoneOffset offset = ZoneOffset.from(offsetTime);

タイムゾーンの変更方法

ZonedDateTimeでUTC(GMT・標準時)からJST(日本時間)へ変換するには以下のようにする。[2017-01-19]

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
	static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
		String s = "2017-01-19 23:45:01.234";
		LocalDateTime localDate = LocalDateTime.parse(s, DATETIME_FORMATTER);
		System.out.println("ldt=" + localDate);

		ZonedDateTime utcDate = ZonedDateTime.of(localDate, ZoneId.of("UTC"));
		System.out.println("utc=" + utcDate);

		// 日付時刻の値を変えずにタイムゾーンIDだけ変更する
		ZonedDateTime jstDate1 = utcDate.withZoneSameLocal(ZoneId.of("Asia/Tokyo"));
		System.out.println("jst=" + jstDate1);

		// 同じ日時を表す別タイムゾーン
		ZonedDateTime jstDate2 = utcDate.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
		System.out.println("jst=" + jstDate2);

		String d = jstDate2.format(DATETIME_FORMATTER);
		System.out.println("str=" + d);

↓実行結果

ldt=2017-01-19T23:45:01.234
utc=2017-01-19T23:45:01.234Z[UTC]
jst=2017-01-19T23:45:01.234+09:00[Asia/Tokyo]
jst=2017-01-20T08:45:01.234+09:00[Asia/Tokyo]
str=2017-01-20 08:45:01.234

Clock

java.time.Clockは、現在日時へアクセスする為のクラス。[2014-07-21]

日付時刻クラスで現在日付・現在時刻を取得するnowメソッドに、Clockを渡すことが出来る。
普段はデフォルトのClockを使っておき、テスト用に特定日時のClockを渡す、といった使い方が出来る。


テスト対象クラス:

import java.time.Clock;
import java.time.YearMonth;
public class ClockExample {

	private static Clock DEFAULT_CLOCK = Clock.systemDefaultZone();

	public String getNowMonthName() {
		return getNowMonthName(DEFAULT_CLOCK);
	}

	String getNowMonthName(Clock clock) {
		YearMonth ym = YearMonth.now(clock);

		int month = ym.getMonthValue();
		if (month % 3 == 0) {
			return "Fizz";
		} else if (month % 5 == 0) {
			return "Buzz";
		} else {
			return ym.getMonth().name();
		}
	}
}

テストクラス:

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

import org.junit.Test;
public class ClockExampleTest {

	@Test
	public void testGetNowMonthName() {
		ClockExample example = new ClockExample();

		Clock clock1 = Clock.fixed(Instant.parse("2014-01-01T00:00:00Z"), ZoneId.systemDefault());
		assertThat(example.getNowMonthName(clock1), is("JANUARY"));

		Clock clock3 = Clock.fixed(Instant.parse("2014-03-01T00:00:00Z"), ZoneId.systemDefault());
		assertThat(example.getNowMonthName(clock3), is("Fizz"));

		Clock clock5 = Clock.fixed(Instant.parse("2014-05-01T00:00:00Z"), ZoneId.systemDefault());
		assertThat(example.getNowMonthName(clock5), is("Buzz"));
	}
}

Clockの生成

import java.time.*;
内容 備考
デフォルト Clock clock = Clock.systemDefaultZone(); デフォルトタイムゾーンを持つ。
UTC Clock clock = Clock.systemUTC();  
タイムゾーンを指定 Clock clock = Clock.system(ZoneId.of("Asia/Tokyo"));  
固定 Clock clock = Clock.fixed(Instant.parse("2014-07-21T22:33:44Z"), ZoneId.of("Asia/Tokyo")); このクロックは進まない。
1分ずつ進む Clock clock = Clock.tickMinutes(ZoneId.systemDefault()); 時間経過の速さは通常通りだが、値が変わるのは1分毎。
分より小さい部分は0になる。
1秒ずつ進む Clock clock = Clock.tickSeconds(ZoneId.systemDefault()); 時間経過の速さは通常通りだが、値が変わるのは1秒毎。
秒より小さい部分は0になる。
1ミリ秒ずつ進む Clock clock = Clock.tickMillis(ZoneId.systemDefault()); Java9以降。[2017-09-27]
指定間隔で進む Clock clock = Clock.tick(baseClock, Duration.of(2, ChronoUnit.SECONDS)); 基準となるクロックを開始時刻とし、指定時間ずつ進む。
時間経過の速さは通常通りだが、値が変わるのは指定時間毎。
ただしbaseClockがfixedで作られたクロックの場合、時間は進まない。

Clockを指定したnowメソッドを呼び出す度(ZonedDateTime.now(clock)とか)に、Clockから“呼び出し時点の現在日時”を取得して新しい日付時刻オブジェクトが作られる。
ただし、fixedメソッドによって作られたClockの場合は、時間は進まない。何度nowメソッドを呼び出しても同じ日時が返る。


リンク


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま