本連載は、Java言語やその文法は一通り理解しているが、「プログラマー」としては初心者、という方を対象とします。Javaコアパッケージを掘り下げることにより「プログラマーの常識」を身に付けられるように話を進めていきます。今回は、「時間」について。OSの時間はどこから取得されるか、グレゴリオ暦とユリウス暦の境目などの常識と時間に関するクラスについて解説します。
今回の記事では、「時間」に関する常識を Javaで身に付けていきます。時間の扱い方は、前回説明した「数」の扱い方と同様で、プログラミング言語によって特徴が出やすいものです。Java言語を通じて、時間の取り扱い方を見ていくことにより、プログラマーとしての常識を身に付けていきましょう。
プログラミングの話題に入る前に、まず私たちの身近なところに現れる日付や時刻を見ていきましょう。あなたがお使いのOSがWindows XPでしたら、デフォルトの設定では、タスクバーの通知領域に時計が表示されていると思います。
時計の部分をダブルクリックすると、 「日付と時刻のプロパティ」画面を開くことができます。
この画面には、日付と時刻が表示されます。ここで表示されている日付および時刻は、OSが内部的に保持している日時となります。OSが刻んでいる時計は システムクロック(システム時計)と呼ばれます。
一方で、OSが起動していない間にも時刻を刻んでいる必要があります。これを実現するために、コンピュータにはリアルタイムクロック(ハードウェアクロック)と呼ばれる時計が内蔵されています。
Windows XPなどのOSは、起動時にリアルタイムクロックから時刻を取得して、その後、OSとしてのシステム時計の時刻を刻むようになっています。私たちが普段何げなく利用しているOSの時計ですが、二重構造の時計として実装されているのは、意外なことです。
原稿執筆時点において、私たちの身近なパソコンのOS上で表示される時計は、意外なほどに不正確です。プログラムから日時を扱うプログラミングをする場合にも、この点に注意を払う必要があります。
先ほどの「日付と時刻のプロパティ」画面の「タイムゾーン」タブを選択すると、タイムゾーンを選択する画面が表示されます。
最近のOSの多くはGMT(グリニッジ標準時)によって定義された標準にのっとっています。そして、java.util.CalendarなどJava言語の日時についても、GMTを基準とした仕組みになっています。この画面に表示があるように、グリニッジと日本では、9時間の時差があります。
それでは、Java言語における日付・時刻を見ていきましょう。Java言語には、図4のように主に3つの日付・時刻を表す型や数値があります。そして、それらは日時を表すようになっています(※注釈:これ以外にもいくつかの型があります)。歴史的な経緯もあり、これら型や数値の間には、いくぶん癖のある関係があります。
それぞれについて見ていきましょう。
コンピュータ関連技術の多くでは、日時を整数で表す手法が採用されています。整数はコンピュータ処理しやすいからでしょう。Java言語も日時を整数で表す方法を採用しています。
具体的には、1970年1月1日 00:00:00 GMTからのミリ秒数を表すlong値として表現されています。この原稿では、これを便宜的に「エポックからのミリ秒」と呼ぶことにします。なお、1970年1月1日 00:00:00 GMTについてJava言語APIでは、「エポック」と呼んでいます。
Java言語には、日時を表すクラスとしてjava.util.Dateクラスが提供されています。クラス名も、それが日付を表す重要なクラスであることを示唆しています。しかし、java.util.Dateクラスの大半のメソッドは推奨されない(deprecated)メソッドとしてマークされています。
java.util.DateのAPIドキュメントに記載があるように、歴史的な経緯から「日付と時間フィールドの間の変換には、Calendarクラスを、日付文字列のフォーマットと構文解析には、DateFormatクラスを使用する」(ここはAPIドキュメントから引用しています)ことになっているのです。とはいえ、日時を表現するクラスとして、java.util.Dateはよく利用されます。
Java言語APIには、java.util.Dateクラスに似た名前のjava.sql.Dateクラスがあります。パッケージ名を除くと、まったく同じ名称なので混同しないよう注意が必要です。例えば、Eclipseなどの統合開発環境において、入力補完やインポートの編成などの機能を利用すると、以下のような選択肢が表示されます。ここでどちらのクラスを利用するのか、適切に選択する必要があります。
Java言語では、日付や時間の各種変換にはjava.util.Calendarクラスが利用されます。特にget/setメソッドがよく利用されるようです。なお、Calendarクラスのget/setメソッドの呼び出しには、Calendarクラスの定数の利用が必要になるので、主立ったものを以下に紹介します。
定数 | 説明 | 例 |
---|---|---|
YEAR | 年を示す | 2007 |
MONTH | 月を示す(0オリジンになっている点に注意が必要。例えば、1月は1ではなく、0として表現される) | 0 |
DATE | 月の日を示す | 31 |
HOUR_OF_DAY | 時刻を示す(HOUR_OF_DAYは24時間制) | 15 |
MINUTE | 分を示す | 46 |
SECOND | 秒を示す | 52 |
MILLISECOND | ミリ秒を示す | 251 |
表1 java.util.Calendarクラスの主な定数 |
特に、月が0オリジンである点に注意する必要があります。
java.util.Calendarクラスは抽象クラスです。Calendarクラスのインスタンスとして実際にはjava.util.GregorianCalendarクラスが利用されます。下記のソースコードを実行してください。
CalendarSample.java | |
|
java.util.Calendarクラスの実行時の具象クラス: class java.util.GregorianCalendar
getInstanceしてgetClass→toStringすると、GregorianCalendarクラスであることが確認できます。
また、java.util.GregorianCalendarのAPIドキュメントには、以下のような記載があります。
GregorianCalendarは、Calendarの具象サブクラスであり、世界のほとんどの地域で使用される標準的なカレンダシステムを提供します。GregorianCalendar は、グレゴリオ暦とユリウス暦をサポートするハイブリッドカレンダシステムで、単一の変わり目を処理します。
…中略…
歴史的に、グレゴリオ暦を最初に採用した国々では、1582年10月4日(ユリウス歴)の後に 1582年10月15日(グレゴリオ歴)が続きました。
本当にそうなっているのか、実際に試してみましょう。下記のソースコードを実行してください。SimpleDateFormatクラスについては、後述します。
GregorianCalendarSample.java | |
|
1582/10/17
1582/10/16
1582/10/15
1582/10/04
1582/10/03
1582/10/02
本当に日付が飛ぶところが確認できました。紫色の日付と赤色の日付の間に、グレゴリオ暦とユリウス暦の境界があります。
ほかのクラスで日時を表す際には、いままで紹介した日時を表す型が利用されることが多いです。
例えば、java.io.Fileクラスにおいて、ファイル最終更新日時はFile.lastModified()メソッドで取得できます。個々で得られるlong値は、「エポックからのミリ秒」なのです。
Copyright © ITmedia, Inc. All Rights Reserved.