Javaについて徹底解説!

JavaのDateについて、考え方から使い方、便利な応用まで徹底解説!

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

Javaのjava.util.Dateは、特定タイミングの日時を表現しているクラスです。ここで言う特定タイミングの日時とは、何年何月何日・何時何分何秒・何ミリ秒という特定の一瞬、その日時のことです。

コンピュータは全てを数字で扱いますが、日時も例外ではありません。コンピュータが日時を表す時は、特定日時からの差分、つまり経過時間で表します。この考え方の正しい理解が、Dateを使いこなすカギなのです。

実はDateは、Javaで日時を扱うにはもう古い方法です。Java 12となった今では、Java 8で追加されたDate and Time APIのクラスが標準です。でも、Dateはこれからもあり続けますし、使い方を知ることは大切です。

この記事では、Javaで日時を表すDateについて、初心者向けに説明します。Dateを扱う上で必要な考え方や使い方、実務でDateを使うならぜひ押さえておきたいプログラミング上のノウハウも満載しています。

※この記事のサンプルは、Java 12の環境で動作確認しています

目次


1.Dateとはそもそもナニモノか

では、まずはDateの考え方を学びましょう。Dateの使い方をすぐにでもお伝えしたいのですが、Dateの考え方が理解できていないと、なぜそのような使い方になるのか、いざ使う時に混乱してしまうかもしれません。

ですから、少々退屈かもしれませんが、Dateとはどういうものなのかの説明にしばらくお付き合いください。でも、他のプログラミング言語での日時の扱いでも似た考え方をするので、決して無駄にはなりませんよ!!

1-1.Dateは特定タイミングの日時を表すもの

Dateは、特定タイミングの日時を表すクラスです。この文章を書いているのは、西暦201966234028秒くらい(ミリ秒の単位は分かりません…)で、この「瞬間」の日時をミリ秒までで表現するのがDateです。

そして、Dateはミリ秒までの具体的な日時を必ず持っているモノです。人間の感覚での201966234028秒「くらい」ではなく、201966234028123ミリ秒というように、ミリ秒まではっきりと、です。

1-2.【重要】Dateが持つのは1970/01/01 00:00:00(GMT)からの経過ミリ秒

コンピュータで日時を表現する方法には、大きく分けると以下の方法があります。Dateは前者で日時を表現するものです。

  1. 特定日時からの経過時間(差分)の整数で表す
  2. 年月日時分秒などのフィールドごとの数値を持つ一つのデータ構造で表す
  3. 決まった形式の文字列や数字で持っておいて、必要な時に解析して使う

Dateでは、経過時間の単位はミリ秒(以下、ms)です。経過時間の基準日時は、西暦197011000(GMT)です。この経過ミリ秒をエポック(epoch)ミリ秒と呼ぶことがあり、秒の場合はUNIX時間とも呼ばれます。

例として、1,00019701100101,559,832,028,123201966234028123です。ちなみに、1分は60,000ms1時間は3,600,000ms1日は86,400,000ms365日は31,536,000,000msです。

Dateでの経過時間はlongで表します。例のように、大きな桁の整数になるからです。intでは±21億程度の数字を表現できますが、それでも桁があまりに小さすぎ、197012653123647までしか表せません。

1-2-1.なぜ経過時間で表現するのか?

コンピュータでは基準日時からの経過時間で日時を表現する方法がよく見られます。つまり、1000などの具体的な整数を、日時として内部で使うのです。これは、整数で表現されている方が、処理を高速化できるからです。

経過時間の整数にすれば、日時に関する計算はすべて整数の足し算や引き算になるので、コンピュータがとても高速に処理できます。その代わり、人間視点では直感的には分かりづらいものになってしまいますけれども。

なお、UNIXのいわゆる2038年問題も、基準日時からの経過秒の差分で日時を表現しているから起きるものです。UNIX時間は秒単位ですが、それでもいつかは経過時間の数値型での最大値まで到達することがあるのです。

でも、先ほどお伝えしたとおり、JavaDate64bitlongで差分を表現しています。longの最大値では、292,278,994年くらいまで表現できますので、まあ、人類が生き残っていそうな間は安心して使えそうですね!!

1-3.基準日時より以前はマイナスの経過ミリ秒で表現する

先ほどの例では、基準日時より未来の日時としましたが、もちろん基準日時より前の日時も表せます。基準日時以前は、マイナスの経過時間で表現します。つまり、-86,400,000196912310000となります。

1-4.Dateそのものには年月日やタイムゾーンの概念はない

ここまで説明してきたことからお分かりいただけたかもしれませんが、Dateそのものには年月日やタイムゾーンの考え方はありません。Dateが持っているのは、基準日時からの経過ミリ秒数だけです。

ですので、Dateだけでは、Dateが表現する日時が何年何月何日なのか、何曜日なのか、あるタイムゾーンでは何時になるか、などの処理は行えません。経過ミリ秒からの計算は一応できますが、その計算はかなり面倒です。

そんな日時関連の処理は、Dateとは別のクラスが担当します。例えば、java.util.Calendarや、java.text.SimpleDateFormatなどです。ですから、Javaでの日時処理は、いくつかのクラスを組み合わせて行うものなのです。

1-5.Dateが持つ経過ミリ秒は変更できない

Dateのインスタンスが持つ経過ミリ秒は、後から変更ができません。つまり、一回作ったDateの日時は固定されています。これをDateはイミュータブル(Immutable、変更できない)である、とも言うことがあります。

ですので、特定の日時を表すDateに対して、例えばその1日後を表すDateが必要になったとすれば、新しいDateをその都度作り直すことになります。

これも、Dateが持つ経過ミリ秒へ足し算引き算をすれば計算はできますが、少々分かりづらいですよね。先ほど少し名前の出たクラスCalendarは、そんなよくある日時の計算に使うメソッドを持っていたりもします。


2.Dateの基本的な使い方

ここまで、Dateの前提知識のご説明にお付き合いいただき、ありがとうございました。ここからは、さっそくDateの使い方を紹介していきます。

2-1.Dateのインスタンスの作り方

Dateのインスタンスの作り方にはいくつかの方法があります。ここでは、それぞれを説明していきます。

2-1-1.Dateのデフォルトコンストラクタで現在日時のDateを作る

Dateのデフォルトコンストラクタを使ってインスタンスを作ると、現在日時を持ったDateが得られます。この現在日時は、Javaを動かしたパソコンやサーバに設定されている現在日時です。

2-1-2.Dateの引数ありコンストラクタで指定した日時のDateを作る

Dateのlongを引数に取るコンストラクタを使ってインスタンスを作ると、1970/1/1 00:00:00から指定しただけの経過時間を持つDateを作れます。このlongは、ずっとお話してきた通り、経過時間のミリ秒表現です。

2-1-3.DateFormat.parseで、日時を表す文字列からDateを作る

Dateは文字列からも作れます。その際には、java.text.DateFormatのサブクラスである、java.text.SimpleDateFormatのメソッドparseを使うのが普通です。parseは、解析するといった意味の言葉です。

DateFormat (Java SE 12 & JDK 12)

https://docs.oracle.com/javase/jp/12/docs/api/java.base/java/text/DateFormat.html

 

SimpleDateFormat (Java SE 12 & JDK 12)

https://docs.oracle.com/javase/jp/12/docs/api/java.base/java/text/SimpleDateFormat.html

日時の文字列の書式は、SimpleDateFormatのコンストラクタの引数で指定します。日本でプログラムを作るなら、以下のどれかか、組み合わせればOKでしょう。時分秒やミリ秒が不要なら、その部分を削除します。

書式文字列 解釈できる日時の例
yyyyMMddHHmmssSSS 20190102123456123
yyyy/MM/dd HH:mm:ss.SSS 2019/01/02 12:34:56.123
yyyy-MM-dd HH:mm:ss.SSS 2019-01-02 12:34:56.123
yyyy/MM/dd HH:mm:ssXXX 2019/01/02 12:34:56+09:00
yyyy-MM-dd’T’HH:mm:ssXXX 2019-01-02T12:34:56+09:00

なお、時分秒やミリ秒の値が未指定なら、その部分の値は0になります。つまり、値“2019/01/01”を、書式“yyyy/MM/dd”parseして作ったDateは、2019/01/01 00:00:00.000を持つDateになる、ということです。

SimpleDateFormatで指定できる書式の詳細は、後の章でもう少しだけ細かくお伝えします。完全な書式の仕様については、SimpleDateFormatJavadocを参照してください。

なお、SimpleDateFormatが日時を解析する際のタイムゾーンを意識すべき場合があります。それについては後述しますが、解析結果が予期せぬ日時になる時は、書式の間違いか、タイムゾーンを疑いましょう。。

2-1-4.Calendar.getTimeで、Calendarが持つ日時からDateを作る

java.util.Calendarも、Javaで日時を扱うためのクラスの一つです。Calendar.getTimeで、Calendarが持つ日時をDateとして取り出せます。

Calendar (Java SE 12 & JDK 12)

https://docs.oracle.com/javase/jp/12/docs/api/java.base/java/util/Calendar.html

Calendarのインスタンスに日時を設定した後にgetTimeすれば、必要な日時を持ったDateを自由に作れます。

ただし、後述しますが、SimpleDateFormatと同様に、タイムゾーンについては要注意です。どんなプログラミング言語で日時を扱う時でも、タイムゾーンあるいは時差の考え方は、少々分かりづらいものです。

2-1-5.Date.fromで、Instantが持つ日時からDateを作る

Java 8以降で使える新しい日時の標準APIには、ある特定の瞬間を表すjava.time.Instantというクラスがあります。このInstantからも、Date.fromを使ってDateのインスタンスを作れます。

あるいは、Instant.toEpochMilliを使って経過ミリ秒のlongに変換し、そのlongDateのコンストラクタを呼び出してもOKです。

2-1-6.Dateのdeprecatedなコンストラクタは使わない

DateのJavadocには、ここで紹介したデフォルトコンストラクタ、longを引数に取るコンストラクタの他にも、年月日、時分秒などを指定できるコンストラクタがいくつかオーバーロードされています。

これらのコンストラクタは、使うことが推奨されていませんので(deprecated)、使わないことをお勧めします。過去のプログラムとの互換性のためにまだ残されてはいますが、いつ削除されてもおかしくはないものです。

Dateは基準日時からの経過時間を持つだけのものなので、年月日時分秒などの視点での生成・操作が必要な場合は、JavaではCalendarを使います。

なお、Javaは、deprecatedなもの(クラス、メソッドなど)を、過去からの互換性を維持するために残す方針を続けてきました。でも、最近ではそういうレガシーなものが削除される例が出てきています。気を付けましょう。

2-2.経過ミリ秒の取得

Dateのインスタンスから経過時間のミリ秒を取得するには、Date.getTimeを呼び出します。すると、その時間を持ったlongが戻ってきます。基準日時より過去の日時を表すDateなら、マイナスのlongが戻ります。

経過時間のミリ秒を直接取り出して何か意味があるのか?と思われたかもしれませんが、日時の数値を基にして計算したい時などに、結構使ったりするものです。

2-3.Date同士の比較

Date同士の比較は、日時を扱うプログラムでは頻繁に行います。Dateは比較のためのインターフェイスComparableを実装しているので、そのcompareToを使ってもいいですし、専用の比較用メソッドも用意されています。

2-3-1.Dateの大小関係とはどういうものか

日時の大小と、未来と過去を紐付けるときは、少し混乱しがちです。Dateでは、以下のルールとなっています。

  • Date1 < Date2 → Date1はDate2よりも過去である
  • Date1 == Date2 → Date1とDate2は同じ日時である
  • Date1 > Date2 → Date1はDate2よりも未来である

つまり、基準日時からの経過時間が大きい方が未来、小さい方が過去になります。これはDateが実際に持つ値が、経過時間のlongであると知っていると、理解・納得しやすいでしょう。

要注意なのは、Dateが持っているミリ秒単位の日時で比較されることです。比較するDate同士で1ミリ秒違っただけでも結果の大小に反映されますので、おかしな結果になるように見えるなら疑ってもいいポイントです。

なお、実際のところ、Dateのソースコードでも以下のようにlong同士の単純な大小比較をしているだけだったりします。実に簡単なロジックで高速に動くでしょうが、前述した注意をしなければならない理由でもあります。

2-3-2.Dateの大小関係をcompareToで確認する

では、Date.compareToを使って大小判断をしてみます。Comparable.compareToのルールに従って、小さい(-1以下)、等しい(0)、大きい(1以上)intが戻ってきます。

ここで、==><などで比較してはいけません。==はインスタンスが同じか確認する演算子なので、インスタンスが持つ日時で比較しません。そして、><は数値用の比較演算子なので、Dateへは使えません。

なお、Dateが持つ日時を、年月日だけなどで大小比較したいこともあります。その場合はDateだけでは簡単にはできませんので、CalendarSimpleDateFormatを組み合わせるといいでしょう。やり方は後述します。

2-3-3.Dateの大小関係をDate.before/after/equalsで確認する

compareToの結果は数値なので、人間向けにはちょっと不親切です。数字の読み違いでバグを作ってしまうかもしれません。過去、未来、同じという判断をメソッドで表現できれば、もう少し読みやすくなりますよね。

そのためのメソッドが、Date.before/after/equalsです。結果はすべてbooleanで戻ります。ですので、例えばif文で使えば、compareToの結果を数値で判断するよりも、プログラムが直感的になるでしょう。

  • before → 自身が引数のDateよりも過去ならtrue、そうでなければfalse
  • equals → 自身が引数のDateと同じ日時ならtrue、そうでなければfalse
  • after → 自身が引数のDateよりも未来ならtrue、そうでなければfalse

2-3-4.Dateそのものを、あるいはDateをからめてソートする

DateはComparableを実装していますので、java.util.Collections.sortやjava.util.Arrays.sortなどで、そのままソートできます。

フィールドとしてDateを持つクラスのソート条件に、Dateのフィールドを加えたいなら、以下のようにcompareToで指定するか、java.util.Comparatorを使えばいいでしょう。


3.Dateのいろいろな使い方

ここでは、実際のプログラムでDateを使う場合によく出てくるものを紹介します。Dateはこういう感じで使えますよ、と言うサンプルとしてもご覧ください。

3-1.ログへの日時の追加

ごく身近な使い方として、ログへの日時を追加するのに使います。何かの処理の途中経過を示すログ出力をする時に、ログを出力した日時があることは、デバッグ上とても意味があることです。

といってもやり方はごく簡単で、ログに出したい文字列へDateを結合すればいいだけです。ごく単純にやるなら以下のとおりです。例ではSystem.outを使っていますが、普通はファイルなどの出力ストリームになります。

ただ、これだとちょっと読みづらいですよね。あと、プログラムを実行すると、1秒以内で処理が終わってしまうこともごく普通なので、その場合は実行状況を把握するログとしては、少々物足りない内容です。

ですから、SimpleDateFormat.formatを組み合わせて、人が読みやすい形式・必要な日時の精度を持つよう、日時を文字列に変換するのが普通です。SimpleDateFormat.formatの使い方は、後でご紹介します。

3-2.処理時間の計測

Dateは、プログラムの処理時間を計測するのにもよく使います。Dateはミリ秒まで持っていますので、普通のプログラムの処理時間計測には十分な精度です。

やり方は簡単で、処理の前後でDateのインスタンスを生成し、経過時間のlong値の引き算をするだけです。

もし、マイクロ秒やナノ秒単位の測定をしたいなら、残念ながらミリ秒までしか持たないDateでは実現できません。その場合は、java.time.Instantなどの、Java 8以降で使える新しいクラスを使いましょう。

3-3.特定期間の加算・減算

繰り返しですが、Dateは基準日時からの経過時間しか持っていません。その機能しかないクラスです。ですから、特定期間つまり分・時間・日・年などの加減算ができる直接的なメソッドは持っていません。

Dateだけでそのようなことをやるなら、経過時間への加算・減算を行って、その経過時間から新しいDateを作る必要があります。計算をする時は、経過時間はミリ秒だということを意識しましょう。

ただし、これは単に機械的に計算した結果でしかありません。例えば何かのDateを翌月1日にする、というような処理はなかなかに大変です。なぜなら、実際には月ごとの日数や閏年を考慮する必要があるからです。

そんなことをやるために、Dateとは別にjava.util.Calendarがあります。Calendarの簡単な使い方と、Dateとの連携のさせ方は、後の章でもご説明します。

3-4.【参考】DateからInstantを取得する

Date.fromではInstantからDateを作れますが、逆にDate.toInstantではDateからInstantを作れます。Dateはミリ秒までですので、当然作ったInstantのマイクロ秒・ナノ秒は0になります。


4.Calendar/SimpleDateFormatとの連携

Dateを使う時は、java.util.Calendarとjava.text.SimpleDateFormatと組み合わせることが大変多いです。これらのクラスを組み合わせてよくやることを、いくつか紹介します。

4-1.【重要】DateをCalendarにする/CalendarをDateにする

まず、CalendarとDateを連携させるために、Date→Calendar、Calendar→Dateの変換の仕方を覚えましょう。といっても、今までの例でも出てきていますね。以下のようにCalendar.getTime/setTimeで行います。

Date→Calendar:

Calendar→Date:

なお、Calendarのインスタンスはnewでは作れません。Calendar.getInstanceで現在日時のCalendarを取得し、そのインスタンスにいろいろと設定していく形になります。

4-2.【重要】SimpleDateFormatで、Dateを文字列とし、解析をする

Dateの作り方のところで、SimpleDateFormat.parseを使って、文字列からDateを作りました。その逆、つまりDateから文字列を作るには、SimpleDateFormat.formatを使います。

このように、SimpleDateFormatは、文字列の解析とフォーマットの両方に使える大変便利なクラスなのです。

4-2-1.SimpleDateFormatでよく使う書式文字列

SimpleDateFormatでよく使う書式文字列は以下のものです。書式文字列では、特定のアルファベットおよびその数が書式上で意味を持ちます。書式文字列の詳細は、SimpleDateFormatのJavadocで確認してください。

  • yyyy → 年(year)の4桁0埋め表現(0000~9999)、例:2019
  • MM → 月(Month)の2桁0埋め表現(01~12)、例:01、12
  • dd → 日(day)の2桁0埋め表現(00~31)、例:09、31
  • HH → 時(Hour)の24時間表記の2桁0埋め表現(00~23)、例: 03、12、23
  • mm → 分(minute)の2桁0埋め表現(00~59)、例: 00、09、30、55
  • ss → 秒(second)の2桁0埋め表現(00~59)、例: 01、10、45、59
  • SSS → ミリ秒の30埋め表現(000999)、例: 008012123
  • XXX → 時差の表現、+HH:mm-HH:mm形式の文字列など、例: +09:00-09:30

これ以外のアルファベットも意味を持ちますが、書式としては使わず、単に文字列として出力に混ぜ込みたいならでくくります。アルファベット以外の文字(/、空白など)はくくる必要はなく、そのまま書けます。

なお、解析する文字列が書式文字列どおりであれば解析できるので、欧米などでよく見られるMM/dd/yyyy形式などに対応したいなら、そのとおりに書式文字列を作ればOKです。

4-3.Dateから年月日・時刻・曜日などを抽出する

基準日時からの経過時間しか持っていないDateだけでは、年月日・時分秒・曜日の抽出をするのは難しいです。もちろん、計算すればできないことはありませんが、結構な量の計算が必要です。

この場合は、CalendarあるいはSimpleDateFormatなどを使って、一応は以下のように必要な情報を抜き出すことはできます。

4-4.Dateからの月末月初を計算する

月末月初の計算とは、例えば 2019/06/10 12:34:56.789という日時から、月末と月初の日付がいつかを調べることです。例では、月末は2019/6/30で、月初は2019/6/1です。

普通の月なら、月末は月を見れば30/31日を判断できますが、2月はうるう年で28/29日が違いますよね。そのプログラミングは少々面倒ですし、実はうるう年の正しい計算方法を知らない人もいて、危なっかしいです。

このような処理は、DateからCalendarを作り、Calendar.getActualMaximum​/getActualMinimumを使うことで簡単に行えます。これらは、Calendarが持つフィールドで、あり得る値の最大・最小値を戻すメソッドです

当然ながら、うるう年を意識した処理もきちんとしてくれますよ。

4-5.ジャスト0分などに日時を切り捨て・切り上げしたDateを作る

Dateはミリ秒までの日時を持っていますが、ミリ秒は不要だったり、日付はそのままに000秒にしたいこともあります。その他にも、次の秒や分に進めたい時もあるでしょう。その場合も、Calendarなどでできます。

4-5-1.切り捨て

Dateを一旦Calendarに変換し、切り捨てしたいフィールド以下へ、Calendar.set0を設定します。

例えば、00分ジャストにしたいなら、Calendarの時・分・秒・ミリ秒のフィールドを0にします。分以下、秒以下なら、そのフィールドからにすればOKです。

あるいは、一旦文字列に変換して解析し直す、という手も使えないことはありません。書式文字列に含まれていないフィールドは0になるからです。

なお、ミリ秒だけいらないなら、経過時間のミリ秒を1000で割って、また1000を掛けるという、お手軽な計算での手段も一応できます。使う機会はないと思いますが

4-5-2.切り上げ

いろいろと方法はありますが、せっかく切り捨てを紹介しましたので、それを活用してみましょう。つまり、切り上げしたいフィールド未満を切り捨てした後に、対象のフィールドをCalendar.add+1します。

以下の例では、秒を切り上げして、次の分にしています。もし59分だったとすれば、分を+1すれば、時間も当然自動的に+1されます。文字列操作だと、こうは簡単にはいきませんね。

なお、本来の切り上げ処理なら、この例だと秒かミリ秒が1以上かをチェックする必要があるでしょう。そのチェック処理の追加は、皆さんへお任せします。

また、Calendar.addでマイナスの数値を指定すれば、その分だけ対象のフィールドが前になります。そして、年月日へも当然使えますので、便利に使ってください。

4-6.【重要】タイムゾーンを意識した処理をする

ここまでずっとお話してきた通り、Dateは基準日時からの経過時間を持っているクラスです。Dateの基準日時はGMT、いわゆる協定世界時のUTC+0で、どこか別の地域のタイムゾーンではありません。

Date自身が持っている日時はGMTです。今までの例で、Dateprintした時に“GMT+09:00”が一緒に出ていますが、これは私のパソコンの環境が日本時間(UTC+9)で、単にそのタイムゾーンを出力しているだけです。

ですので、画面上にprintする時や文字列を解析してDateを作る時など、タイムゾーンを意識しなければならないことがあります。

4-6-1.Calendarでタイムゾーンを設定する

CalendarをgetInstanceすると、システムのタイムゾーンを持ったCalendarが作られます。前述のとおり、私のパソコンは日本時間なので、Calendarも現地時間はUTCから9時間進んでいるとみなして処理をします。

私の環境でDateCalendarに渡して日時を取得すると、UTC+0から9時間進んだ日時が得られます。繰り返しですが、DateそのものはGMTなので、GMTから現地時間への解釈・変換をCalendarが行っているのです。

ですから、Calendarのタイムゾーンを切り替えれば、同じ日時を表すDateであっても、別のタイムゾーンでの日時を取得できます。

4-6-2.SimpleDateFormatでタイムゾーンを設定する

SimpleDateFormatもCalendarと同じで、それ自身がタイムゾーンを持っています。こちらも、何もしなければシステムのタイムゾーンです。ですので、出力や解析はシステムのタイムゾーンに準拠したものになるのです。

SimpleDateFormatが持つタイムゾーンを変えるには、Calendarと同じようにタイムゾーンを指定します。

4-6-3.日時文字列を解析する時は、タイムゾーンを意識しよう

この節で見てきたように、Javaでの日時の根本的な表現方法はGMTです。その日時がプログラムの外からどう見えるかは、システムのタイムゾーンや、CalendarDateFormatterのタイムゾーンに依存します。

ただ、プログラム外部から取り込んだ情報、特に文字列として取り込んだ日時が、どのタイムゾーンで表現されているものかは意識する必要があります。そうしないと、Dateへ変換した時に時刻がずれるからです。

特に文字列を日時として解釈する時、タイムゾーンを表すものが付いていれば、それに準じて解釈すればいいだけです。タイムゾーンがついていないなら、どのタイムゾーンかをきちんと確認して指定しましょう。


5.【参考】Date and Time APIの簡単な紹介

Java 8より、Date and Time APIという、日時を取り扱う新しいAPIが追加されました。パッケージとしてjava.timeが新設されています。

java.time (Java SE 12 & JDK 12)

https://docs.oracle.com/javase/jp/12/docs/api/java.base/java/time/package-summary.html

これにより、今までのjava.util.Datejava.util.Calendarjava.text.DateFormatは古いAPIということになりました。もちろんこれからも使えますが、機能追加・強化は、新APIの方が優先されていくでしょう。

ここでは、この新しいAPIに接するに当たり、今までのDateとどういう違いがあるのかを簡単にお伝えします。

5-1.Date/Calendarしかないことの問題点

今までのAPIDateCalendarがその中心にありました。ですので、Date/Calendarで保持している日時をどう扱うか、というAPIになっています。ですが、いくつかの問題が昔から指摘されていました。

その一つに、Dateは持っている日時の単位が常にミリ秒なので、用途によってはメソッドやメソッドの戻り値の意味があいまいになってしまう、ということがあります。これはCalendarでも同じことです。

例えば、メソッドの引数がDateだとして、そのDateは年月日だけ必要なのか、それともミリ秒まで必要なのかはわかりません。Javadocがあればまだマシですが、なければお手上げなので、ソースを読む必要があります。

Dateでは問題なのでStringintを使ったとしても、メソッド宣言上はどんなString/intでもコンパイルエラーにはならないので、“YYYY/MM/DD”を想定したメソッドだとしても、メソッド内でのチェックが必要です。

5-2.日時の種類がクラスになって明確に!!

結局、問題は日時の役割をDateCalendarだけでカバーしようとしたことです。ですので、Date and Time APIでは、用途別のクラスで役割分担をすることになりました。それが従来のものから大きく変わったところです。

また、クラスが多くなったのですが、それぞれのクラスの操作をするメソッド名には統一されたルールがあり、意味が分かりやすくなっています。これは、同じくJava 7NIO.2で追加されたクラスにも共通した特徴です。

なお、ここで紹介するクラスは全て不変(Immutable)、かつスレッドセーフです。

5-2-1.日時のクラス

日時を直接表すクラスとしては、以下のものが用意されています。

  • java.time.Instant:タイムスタンプのようなもの。意味的にはこれが従来のDateに一番近い?
  • java.time.LocalTime:時刻だけを持つクラス。年月日の情報は持たない。
  • java.time.LocalDate:日付(年月日)だけを持つクラス。時分秒以下の情報はもたない。
  • java.time.LocalDateTime:日時を持つクラス。ただし、タイムゾーンや時差の情報は持たない。
  • java.time.ZonedDateTime:日時と紐付くタイムゾーンを同時に持つクラス。
  • java.time.OffsetDateTime:日時と紐付く時差を同時に持つクラス。

ここでZonedDateTimeOffsetDateTimeは同じものに見えますが別物です。タイムゾーンと時差は、実は少々違う考え方だからです。ここでは詳細には踏み込みませんので、興味がある方は調べてみてもいいでしょう。

5-2-2.期間のクラス

新しい種類のデータ型として、期間を表現できるクラスが二つできました。

  • java.time.Duration:期間をナノ秒単位で表現できるクラス。
  • java.time.Period:期間を、何年・何ヶ月・何日など、人間が分かりやすい形で表現したクラス。

5-2-3.年月日・曜日・時差のクラス

年・月・日・曜日・時差なども、それぞれクラスとして扱えるようになりました。これでメソッドの引数や戻り値の意味を、より明確にできるようになりました。

  • java.time.Year:年
  • java.time.Month:月
  • java.time.YearMonth:年月
  • java.time.MonthDay:月日
  • java.time.DayOfWeek:曜日
  • java.time.OffsetTime:時差のオフセット時間(+01:00など)

5-3.日時用のフォーマット・解析用クラスが新しくできた

今までのDateFormat(SimpleDateFormat)に代わり、java.timeにあるクラス用のフォーマット・解析用クラスjava.time.format.DateTimeFormatterが新しく出来ました。従来のものより機能が強化されています。

そして、例えばISO 8601などの各種標準に則った書式用のフォーマッタが、あらかじめいくつか用意されています。ですから、SimpleDateFormatのように、自分で書式文字列を考えなくてもすむことが多いでしょう。

また、DateTimeFormatterはスレッドセーフです。そのため、今までのSimpleDateFormatのように、不具合回避のために無駄にnewする必要もありません。最初に一つ作っておくか、もうあるものを使えばいいのです。

5-4.日時の精度がナノ秒単位になった

地味に機能強化されたポイントは、各種日時の精度の仕様が、今までのミリ秒(1/1000)からナノ秒(1/1000000000)になったことです。

実際のところ、ナノ秒までの精度があったとしても、普通のプログラムではまず使いません。ですが、プログラミング言語の仕様上できるということと、できないということの間には、天と地ほどの差があります。

それに、そういう時間の精度が必要となるプログラムの領域もあるのです。これにより、Javaを適用できる領域が、また大きく広がることになるでしょう。


6.まとめ

この記事では、Javaで日時を扱うためのクラスjava.util.Dateをご紹介しました。Dateは基準日時からの経過時間のミリ秒を持つだけの単純なクラスですが、このクラスがJavaの日時処理を長らく支えてきました。

そして、Dateだけでは少々使いづらいため、Dateに欠けている機能を補完するためのCalendarDateFormat(SimpleDateFormat)があります。Javaの日時処理は、これらのクラスを組み合わせて行うのです。

Java 12の今では、公式にはDateの役割は終わりました。Java 8からはjava.timeにあるDate and Time APIが公式のAPIになりましたので、これからはそれらを使ったAPIやプログラムが増えていくでしょう。

しかし、Dateがなくなることはないでしょうし、各種フレームワークやAPIDateしか扱えないものがまだあります。ですから、Dateの使い方を身に着けることは、今のJavaプログラマにはまだ必要なことなのです。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

8/28(水)開催決定!
【テックジム×ENGINEER.CLUB】ゼロからはじめるPythonプログラミング入門講座

プログラミングは初めてだけどPythonから始めてみたいという方のために、無料のハンズオン開発講座をテックジム×ENGINEER.CLUBで共同開催することになりました。開催日時は以下の通りです。

  • 8月28日(水)19時〜21時 東京開催

本講座で学んでいただく「TechGYM方式」とは、基礎知識なしでも座学なしでプログラミングに専念できるように設計されたプログラミングのカリキュラムメソッドです。「まるで魔法にかかったようにプログラミンスキルが習得できる」と評判の本講座をぜひ一度体験してみてください。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。