2011-9-9更新 2002-12-27作成

 ■サーブレット編4

 ■日付と時間

 ここではJavaでの日付と時間の扱いについて説明します。まず最初にJavaで、(特に)事務系業務処理においてよく行うであろう処理に関するパッケージ・クラスを挙げておきます。

日付時間処理で使用するクラス
 ・java.text.DateFormat
 ・java.text.SimpleDateFormat
 ・java.util.Date
 ・java.util.Calendar
 ・java.util.GregorianCalendar

 これらのクラスを使用して日付時間に関する処理をするのですが、これらクラス間の関係を把握したほうがいいと思うので、細かい説明の前にまず図示しておきます。

 Javaで日付時間を扱う基本的なクラスは Date クラスなんですが、実際に処理で使う際は Calendar クラスおよびその拡張である GregorianCalendar クラスを使うほうが多いと思います。

 Javaが扱う日付時間のもっとも基本の部分では、1970年1月1日0時0分0秒を基準としたミリ秒単位で管理しています。 Date クラスや Calendar クラス、 GregorianCalendar クラスも全てこの管理体系に基づきます。
 APIドキュメントで Date クラスのメソッドの説明を見ると、ほとんどが「推奨されていません」となっており、 Calendar クラスのメソッドで置き換えられています。

  Calendar クラスは、いわゆる一般的なカレンダを表すクラスで、具体的な西暦、グレゴリオ暦を表すのがこれを拡張した GregorianCalendar クラスです。こちらは Calendar クラスの拡張なのでこれのメソッドは使用できますし、さらにうるう年判定などの機能を持っています。

 現バージョン(Java2 SDK SE1.4)では拡張クラスは GregorianCalendar クラスしかありませんが、そのうち日本の和暦や旧暦を扱う拡張クラスも標準で取り込まれるかもしれません(今の段階では自分で共通ルーチンのようにこれらの拡張クラスを作成する必要がありますし、それぞれの開発チームでは実際にそうしているでしょう)。

 DateFormat クラスは Date クラスと日付を表す文字列との相互変換に使います。
 SimpleDateFormat クラスは DateFormat クラスを拡張したものです。 Date クラスと日付を表す文字列との相互変換時に書式スタイルというものを使うのですが、こちらのクラスでは任意の書式スタイルを使えます。

 ◇日付時間関連のメソッド

 日付時間に関するクラスについて、主なメソッドを説明します。

 ・GregorianCalendarCalendar) に対する主なメソッド

 メソッドの説明に先立ち、 new 演算子で GregorianCalendar インスタンスを生成するためのコンストラクタを説明しておきます。

GregorianCalendar の主なコンストラクタ
コンストラクタ 引数(パラメータ)
説明
GregorianCalendar
 デフォルトの地域(世界標準時からの時差を示す地域)の、現在時刻(ミリ秒単位)で GregorianCalendar を生成する
int, int, int
 デフォルトの地域(世界標準時からの時差を示す地域)の、指定した年月日の GregorianCalendar を生成する
int, int, int, int, int
 デフォルトの地域(世界標準時からの時差を示す地域)の、指定した年月日、時分の GregorianCalendar を生成する
int, int, int, int, int, int
 デフォルトの地域(世界標準時からの時差を示す地域)の、指定した年月日、時分秒の GregorianCalendar を生成する
TimeZone GMTからの時差
 指定したGMTからの時差に基ずく、現在時刻(ミリ秒単位)で GregorianCalendar を生成する

GregorianCalendar(Calendar) に対する主なメソッド
メソッド 戻り値 引数(パラメータ)
説明
equalsboolean Object 比較対象
 引数に指定した比較対象と等しい場合は true 、そうでないときは false を返す。
 引数は Object 型だが、実際には GregorianCalendar もしくは Calendar 型に限定すべき。
afterboolean Object 比較対象
 引数に指定した比較対象と比べ、この現在時刻が比較対象より後なら true 、そうでないときは false を返す。
 引数は Object 型だが、実際には GregorianCalendar もしくは Calendar 型に限定すべき。
beforeboolean Object 比較対象
 引数に指定した比較対象と比べ、この現在時刻が比較対象より前なら true 、そうでないときは false を返す。
 引数は Object 型だが、実際には GregorianCalendar もしくは Calendar 型に限定すべき。
addvoid int 時間領域, int 増分または減分値
 GregorianCalendar インスタンスの指定した時間領域に対して、指定した値を加算または減算する
 値が+のときは加算し、−のときは減算となる
 時間領域に指定可能な値は後述する
clearvoid
 GregorianCalendar インスタンスの時間領域の値をクリアする
int 時間領域
 GregorianCalendar インスタンスの指定した時間領域の値をクリアする
getint int 時間領域
 GregorianCalendar インスタンスから、指定した時間領域の値を取得する
getTimeDate
 GregorianCalendar インスタンスに設定されている値を Date インスタンスとして取得する
getTimeInMillislong
 GregorianCalendar インスタンスに設定されている値をミリ秒単位で、 long 型で取得する
setvoid int 時間領域, int 設定値
 GregorianCalendar インスタンスの指定した時間領域に、指定した値を設定する
int, int, int
 GregorianCalendar インスタンスに、指定した年月日を設定する
 年月日以外の時間領域の値は元のままとなる
int, int, int, int, int
 GregorianCalendar インスタンスに、指定した年月日、時分を設定する
 秒、ミリ秒の時間領域の値は元のままとなる
int, int, int, int, int, int
 GregorianCalendar インスタンスに、指定した年月日、時分秒を設定する
 ミリ秒の時間領域の値は元のままとなる
setTimevoid Date 設定値
 GregorianCalendar インスタンスに、引数で指定した Date インスタンスの値を設定する
setTimeInMillisvoid long 設定値
 GregorianCalendar インスタンスに、引数で指定した long 型のミリ秒単位の値を設定する
isLeapYearboolean int
 引数で指定した年がうるう年なら true 、そうでなければ false を返す


 add メソッドで時間領域を指定することにより、例えば何日後、何日前、何分後、何分前、といった計算をすることが出来ます。
 しかしある日付時間と別の日付時間の差を求めるメソッドがありません。時間差の計算であれば、 getTimeInMillis メソッドで long 型のミリ秒単位の値を取得すれば可能です。 long 同士で引き算を行い、結果を setTimeInMillis メソッドでGregorianCalendar インスタンスに設定してあげると分秒マイクロ秒単位の時間差が取れます。

 上記メソッドの引数に指定する int 型の時間領域は、 Calendar クラスで定数として定義されています。つまり Calendar クラスで public final で定義されたフィールドということです。指定するときは Calendar.定数名 としください。

時間領域関連の主な定数値
種類 定数名 意味
時間領域YEAR  年を表す時間領域
MONTH  月を表す時間領域
 get(Calendar.MONTH)一月の場合、1ではなく0が返るので要注意(これも最初から0番目の月という相対指定、でJavaの悪しき慣習の一つです)
DATE  月の何日かを表す時間領域
 月の最初の日(一日)は1になる
DAY_OF_MONTH
DAY_OF_YEAR  年内の通算日を表す時間領域
 年の最初の日(一月一日)は1になる
HOUR_OF_DAY  24時間制の時間を表す時間領域
 午後一時は13になる
HOUR  12時間制の時間を表す時間領域
 午後一時は1になる
AM_PM  午前午後を表す時間領域
 午前は0になる
MINUTE  分を表す時間領域
SECOND  秒を表す時間領域
MILLISECOND  ミリ秒を表す時間領域
DAY_OF_WEEK  曜日を表す時間領域
 日曜日は1になる
月名JANUARY  一月を表す定数
FEBRUARY  二月を表す定数
MARCH  三月を表す定数
APRIL  四月を表す定数
MAY  五月を表す定数
JUNE  六月を表す定数
JULY  七月を表す定数
AUGUST  八月を表す定数
SEPTEMBER  九月を表す定数
OCTOBER  十月を表す定数
NOVEMBER  十一月を表す定数
DECEMBER  十二月を表す定数
曜日名SUNDAY  日曜日を表す定数
MONDAY  月曜日を表す定数
TUESDAY  火曜日を表す定数
WEDNESDAY  水曜日を表す定数
THURSDAY  木曜日を表す定数
FRIDAY  金曜日を表す定数
SATURDAY  土曜日を表す定数
午前午後AM  午前を表す定数
PM  午後を表す定数


 例えば月名の定数を使う場合を、例題を挙げて説明しましょう。

 ある販売管理システムで、受注日(Order Date)から対応する会計年度(Fiscal Year)を決めるとしましょう。3月決算の会社なら2003年3月の会計年度は2003年度ではなく、2002年度になります。つまり受注日の月が3月以前か、4月以降かで判断します。
  GregorianCalendar 型のw_odr_dteに受注日が入っているとするとこの判断処理は、

 if (w_odr_dte.get(Calendar.MONTH) > 2)
  {
   w_fiscal_yy = w_odr_dte.get(Calendar.YEAR);
  }
 else
  {
   w_fiscal_yy = w_odr_dte.get(Calendar.YEAR) - 1;
  }

になります。しかしこれだと if の条件式が.get(Calendar.MONTH) > 2 となり、直感的に、3月か判定している、ということが分かり難くなります。そのため月名定数を使って

 if (w_odr_dte.get(Calendar.MONTH) > Calendar.MARCH)
  {
   w_fiscal_yy = w_odr_dte.get(Calendar.YEAR);
  }
 else
  {
   w_fiscal_yy = w_odr_dte.get(Calendar.YEAR) - 1;
  }

としたほうがいいでしょう。


 時間差の計算をするときに getTimeInMillis メソッドで long 型のミリ秒単位の値を取得し、 long 同士で引き算を行って結果を setTimeInMillis メソッドで GregorianCalendar インスタンスに設定してあげるとよい、と書きました。
 しかし若干の注意点があります。分以下なら問題ないのですが時間以上の時間差計算をすると、 GregorianCalendar インスタンスに設定したときにGMTとの時差も入ってしまうので、デフォルトの地域が Asia/Tokyo だと9時間ずれてしまいます( GregorianCalendar から getTimeget で取り出したときに、時間にGMTからの時差9時間が足されている)。

 そのため時間差用の GregorianCalendar インスタンスを生成するときは、 TimeZone を指定したコンストラクタを使ってください。
  TimeZone を使うためには次のクラスもインポートします。
 ・java.util.TimeZone
 時間差用の GregorianCalendar インスタンス生成手順は次のようになります。

 TimeZone w_tzone = null; // TimeZone型変数の定義
 GregorianCalendar w_gclndr = null; // GregorianCalendar型変数の定義

 w_tzone = TimeZone.getDefault(); // デフォルトのTimeZone取得
 w_tzone.setRawOffset(0); // TimeZoneにGMTからの時差0ミリ秒を設定
 w_gclndr = new GregorianCalendar(w_tzone); // 指定した時差を持つGregorianCalendarの生成

 ただしこの GregorianCalendar から getTimeDate インスタンスを取得して format すると、やはりデフォルトの地域に基づく時差が考慮されてしまうので、 get メソッドで個別に時間領域を取り出したほうがいいです。
 こうしておけば GregorianCalendar に設定した時間差は、1970年1月1日0時0分0秒を基準としたミリ秒単位の日付時刻と解釈されるので、31日までなら時間差を日時分秒ミリ秒に変換して表示できます(31日を過ぎると2月になってしまうので)。

 これについては実際に自分で、ある日時に対して MONTHDAY_OF_MONTHHOUR などに add でいろいろな値を加減算し、結果の日時と元の日時の差を求めて表示するプログラムを作って確認してみてください。

 必要なのが日差、日数計算なら、計算に使う日付から get(DAY_OF_YEAR) で年内通算日を取得して計算するといいでしょう。ただし年を跨るときはうるう年判定によって、計算結果が負のときに365を足すか366を足すかが変わってくるので注意してください。
 例えば12月20日から翌年1月10までの日数は、うるう年に関係なく21日です。
 2002年12月20日の DAY_OF_YEAR は354日で、2003年1月10日の DAY_OF_YEAR は10日です。日数はTO日付 - FROM日付ですが、数値が逆転するので年を跨るときはTO日付に1年分の日数、365を足してあげます。そうすると10 + 365 - 354 = 21日となります。
 しかし2000年12月20日から2001年1月10日だと2000年がうるう年なので DAY_OF_YEAR は355日、2001年1月10日の DAY_OF_YEAR は10日なので先の計算式だと日数は20日になってしまいます。うるう年のときはTO日付には365ではなく366を足さないといけない、ということです。

 ・DateFormat に対する主なメソッド

  DateFormat クラスは日付時刻に関する書式を定義しています。これを拡張したものに SimpleDateFormat クラスがあり、こちらを使うと任意の書式を設定できます(2003年1月7日 と日本語表示するなど)。
 先に標準の DateFormat クラスについて説明しておきます。

DateFormat に対する主なメソッド
メソッド 戻り値 引数(パラメータ)
説明
getDateInstanceDateFormat
 デフォルトの地域で標準の、日付書式スタイルを持つフォーマッタを取得する
getDateTimeInstanceDateFormat
 デフォルトの地域で標準の、日付時刻書式スタイルを持つフォーマッタを取得する
getTimeInstanceDateFormat
 デフォルトの地域で標準の、時刻書式スタイルを持つフォーマッタを取得する
formatString Date 日付時刻
 引数に指定した日付時刻を、フォーマッタのスタイルに合わせてフォーマットする
parseDate String 日付時刻文字列
 引数に指定した日付時刻を表す文字列をフォーマッタのスタイルに合わせて解析し、結果の Date を返す
 日付時刻文字列が DateFormat インスタンスに設定されている書式に合わない場合、 ParseException が発生する

 ・DateFormat インスタンス化の違い

  DateFormat クラスをインスタンス化する際のメソッドの、getDateInstancegetDateTimeInstance の違いについて説明します。
 この二つを例に説明すると、 format メソッドの結果文字列や parse メソッドの元文字列に時刻まで含むか否か、ということになります。
 地域が Asia/Tokyo (日本になります)だと標準の書式スタイルは DateFormat.MEDIUM になり、
 日付は yyyy/MM/dd
 時刻は H:mm:ss
 日付時刻は yyyy/MM/dd H:mm:ss
となります。 parse メソッドで入力文字列を解析して Date 型のデータを取得するとき、入力文字列がこの形式になっていないといけません。

  getDateInstance メソッドで インスタンス化した場合は日付の部分のみ解析されます。例えば、"2003/1/7"はもちろん問題ありませんし、"2003/1/7 12:34:56"と時刻まで指定しても日付部分の"2003/1/7"のみ解析されるので大丈夫です。

 それに対して getDateTimeInstance メソッドで インスタンス化した場合は、入力文字列に時刻まで含まれていないといけません。今度は"2003/1/7"と日付のみ指定すると ParseException が発生します。

 逆に format メソッドで Date 型を文字列に変換するときは getDateInstancegetDateTimeInstance かによって、上記書式スタイルに合わせて変換する際に日付のみか時刻まで変換するかが変わります。

 ・SimpleDateFormat の場合

 SimpleDateFormat の場合はメソッドではなく、 new 演算子でコンストラクタを使用してインスタンスを生成してください。コンストラクタの引数に書式パターン文字列を指定します。

SimpleDateFormat の主なコンストラクタ
コンストラクタ 引数(パラメータ)
説明
SimpleDateFormat
 デフォルトの地域(世界標準時からの時差を示す地域)の、標準書式スタイルで SimpleDateFormat を生成する
String 書式パターン
 デフォルトの地域(世界標準時からの時差を示す地域)の、指定した書式パターンスタイルの SimpleDateFormat を生成する


日付時刻の主な書式パターン文字
書式パターン文字意味
y 年を表す
M 月を表す
d 日を表す
E 曜日を表す
例えば月曜日のとき、"EEE"と3文字以下で指定のときは "Mon" 、
"EEEE" と4文字以上で指定のときは "Monday" となる
H 24時間制の時を表す
a 午前・午後を表す。午前は"AM"、午後は"PM"となる。
K 12時間制の時を表す。正午は0となる。
h 12時間制の時を表す。正午は12となる。
m 分を表す
s 秒を表す
S マイクロ秒を表す

 年月日、時分秒などの数値のとき、書式パターン文字を1文字で指定したときは頭の0は省略されます。そうでない場合は書式パターン文字数に合うように頭に0が付加されます。
 例えば "H" とすると午前2時は 2 、午後4時は 16 になりますが、 "HH" とすると午前2時は 02 、午後4時は 16 になります。

 書式パターン文字以外の文字は、そのまま表示されます。 "yyyy-MM-dd" という書式パターンを指定して2003年1月10日を format すると、 "2003-01-10" と変換されます。
 書式パターン文字に該当する文字をそのまま使いたい場合はその文字列部分を ' で囲ってください。囲まれた部分は書式パターン文字とは解釈されずにそのまま表示になります。あまり意味が無いんですが例えば "Mamy" と表示したい場合、 "'Mamy' yy/MM a K:m" と指定して 2003年2月20日午前10時42分 を format すると、 "Mamy 03/02 AM 10:42" と変換されます。

 例えば日本語で時間まで表示したいなら、"yyyy年MM月dd日 HH時mm分" や "yyyy年M月d日 H時m分" という書式パターン文字列を指定することができます。

 最後に SimpleDateFormat クラスのメソッドを一つ説明しておきます。日付や時刻の表示形式よって個別にインスタンス化するのではなく、一つのインスタンスを使い回して日付表示のとき、時刻表示のときなどで書式パターンを変更するときに使えるメソッドです。

SimpleDateFormat に対する主なメソッド
メソッド 戻り値 引数(パラメータ)
説明
applyPatternvoid String 書式パターン
 SimpleDateFormat インスタンスの書式パターンに、指定した書式パターンを設定する

 ・コーディング例

 入力画面に日付を入力するテキストボックスがあり、その内容をチェックする処理のサンプルコーディングはこうなります。サンプルは DateFormat を使い、標準の書式パターンで日付のみ入力、という場合の処理です。

 String          w_in_dat; // 画面からのパラメータ
 DateFormat      w_dfmt;
 GregorianCalendar w_gclndr; // 変換後の日付データ

 if (request.getParameter("Date1") == null)
  {}
 else
  {

   w_in_dat = request.getParameter("Date1");
   w_dfmt = DateFormat.getDateInstance();
   w_gclndr = new GregorianCalendar();
   try
    {

     w_gclndr.setTime(w_dfmt.parse(w_in_dat);
     // 日付取得後の処理
    }
   catch(ParseException
 wl_excp)
    {

     // 日付エラーの処理
    }
  }


 また GregorianCalendar の日付情報を、画面表示用に String に変換するサンプルは次のようになります。

 String          w_date; // 画面表示用の日付文字列
 SimpleDateFormat w_sdfmt;
 GregorianCalendar w_gclndr; // 日付データ

 w_sdfmt = new SimpleDateFormat("yyyy年MM月dd日");
 w_date = w_sdfmt.format(w_gclndr.getTime());

 ◇練習問題

 こういった日付に関する処理の説明があると、必ず出てくる練習問題が「カレンダー表示」です。ここでもそれをやりましょう。

 実現する機能はこんなものでいいでしょう。
 ・初期表示ではその月のカレンダーを表示する
 ・「前月」ボタンを押すと現在表示の前月のカレンダーを表示する(ボタンでなくリンクでも可)
 ・「翌月」ボタンを押すと現在表示の翌月のカレンダーを表示する(ボタンでなくリンクでも可)

 例えばこんな風に表示できればいいと思います。

カレンダー表示1

カレンダー表示1

  2003年 1月  
1234
567891011
12131415161718
19202122232425
262728293031


 あるいは前処理と後処理の部分を変えて、月初の余りに前月の最後、月末の余りに翌月の最初を表示してもいいと思います。

カレンダー表示1

カレンダー表示1

  2003年 1月  
2930311234
567891011
12131415161718
19202122232425
2627282930311

 ・考え方

 この練習問題を作成するにあたっては次のことを検討しましょう。

 初期表示の判定は今までの課題と同じで、鍵になるパラメータの有無で判定すればよいと思います。

 前月や翌月はあくまでも現在表示中の年月に対してです。ですから現在表示中の年月をパラメータとして受け取る必要があります。
 一つの方法は画面中に type="hidden" を指定した隠しフィールドを設け、ここに表示中の年月を持つ方法です。そして押されたボタンによって表示するのが前月か翌月かを判定します。

 押されたボタンの判定ですが、getParameter()メソッドでパラメータに画面定義でボタンに付けた名前を指定します( getParameter("ボタン名") )。そうすると押されていないボタンの場合はパラメータ自体が存在しないため戻り値がnullになります。これでnull以外のボタン名を判定します。

 もう一つはリンクを使い、飛び先のURLにパラメータとして表示する年月を持たせてしまうものです。この方法の場合は form method= には get しか使えません。

 サンプルはこれこれです。それぞれでカレンダーの表示形式と、パラメータの処理方法を分けてあります。なお後の方のサンプルでは年間通算日も表示するようにしています。

 毎年年末になるといろいろなところから卓上カレンダーが配れたりします。汎用機上のシステムに携わっている人の間ではIBMの卓上カレンダーが人気です。理由はIBMのものは通常の日付だけでなくジュリアンデート(年間通算日)も出ているからです。汎用機だとOSメッセージやログメッセージ、ある種のデータセット名の一部などで年間通算日を目にすることがままあります。ですから卓上カレンダーに出ていると何かと便利なんです。
 一時期IBMは卓上カレンダーへのジュリアンデート印刷を止めたことがありました。ですがあまりに不評だったらしく、現在は無事に復活してます。


 ■10進演算

 特に事務系業務システムで必要な、誤差の無い小数点演算をするための仕組みであるJavaの10進演算について説明します。
 厳密には誤差が無いということはないのですが、小数点を使った際の2進変換誤差が生じないということです。例えば0.2など2進数にすると無限小数になってしまい、厳密に表すことができません(この辺は情報処理試験等でおなじみですね)。こういった倍精度浮動小数点を使おうが厳密に表現できない数値を使って計算を行った場合、人間が手で計算するのと異なる誤差が出ることがあります。このような説明できない誤差が許されない計算をJavaでするときに、この10進演算の仕組みを使うわけです。

 さてJavaで表現できる精度ですが、ほとんど無限に近いんじゃないでしょうか(試してません)。Javaで10進演算を行うときは BigDecimal クラスの参照データ型として定義します。これを使うときに小数点以下の桁数(JavaAPI仕様ではスケールという言葉を使っていますが、ここでは精度を使います)を指定しますが、この桁数指定は int 型の数値で行います。ですからその気になれば int 型で表現できる範囲の小数点以下桁数が指定可能だと思います(これも試してません)。

 また BigDecimal は参照データ型ですが、文字列の String 型と同様に変更不可です。文字列に対して変更を加えると元の文字列を変更するのでなく、変更後の新しい文字列が生成されました。 BigDecimal 型もこれと同じです。
 つまりある BigDecimal 型変数を別の BigDecimal 型変数に代入し、元の変数に変更を加えても、同じ実体を参照しているので両方の変数が同じ値になってしまう、ということはありません。変更を加えたほうは新しい BigDecimal が生成され、そこへの参照に変わります。

 Javaの10進演算は、特にCOBOL経験者にとっては馴染み深いことがいろいろ出てくると思います。まず BigDecimal 自体はCOBOLの「仮想小数点を使用した外部10進形式」と同じようなもの、と考えてください。もちろんCOBOLの場合は有効桁数は全体で18桁までという制限がありますが、Javaの場合はこの制限がほぼ無いものと捉えていいと思います。

 また少し先走って説明してしまうと BigDecimal を使った演算は、COBOLでCOMPUTE文を使わないで計算処理をするのと似たようなコーディングになる、と思っていればいいです。

 ◇10進演算の主なメソッド

 まず10進演算を行うには BigDecimal を使いますが、これを使う場合は java.math.BigDecimal をインポートしてください。
 変数を定義する場合は
 BigDecimal 変数名;
となります。
 例えばCOBOLであればデータの定義のときに精度も決まりますが、 BigDecimal の場合は実際にデータ値を持ったインスタンスを生成するときに精度を指定します。

 まず new 演算子でインスタンス化するときのコンストラクタについて主なものを説明します。

BigDecimal の主なコンストラクタ
コンストラクタ 引数(パラメータ)
説明
BigDecimal String 数値を表す文字列
 引数で指定した文字列を解析し、BigDecimal を生成する
 精度は文字列中の小数点の位置により、自動的に決まる
 なお文字列に含めることが出来る文字は、先頭(際左端)の符号を表す '-''+' 、数字( '0' から '9' )、小数点を表す '.' だけである

 このコンストラクタで文字列から BigDecimal を生成する場合、文字列の先頭や最後に空白があってもいけません。必ず trim メソッドで空白を削ってください。

BigDecimal の主なメソッド
メソッド 戻り値 引数(パラメータ)
説明
valueOfBigDecimal long 元データ
 引数に指定した long 型の元データを BigDecimal に変換する
 変換後の BigDecimal の精度は0となる(小数点以下無し)
 なお元データが int 型でも自動的に long に型変換(キャスト)される
valueOfBigDecimal long 元データ, int 精度
 引数に指定した long 型の元データを BigDecimal に変換する
 変換後の BigDecimal は、精度は引数で指定した桁数、値は元データ / 10精度 となる
 例えば BigDecimal.valueOf(1234, 3) とすると、変換後は BigDecimal 型の1.234になる
 なお元データが int 型でも自動的に long に型変換(キャスト)される
intValueint
 この BigDecimal を2進に変換した下位32bitを int 型として返す
 この際結果の int 型の符号bitは無視されて返されるので、 int 型に収まらない値を変換すると正負が逆転した意図しない値になることがある
 実際に次のようなソースをコーディングして試してみて欲しい
int        w_int;
BigDecimal w_num;
w_int = Integer.MAX_VALUE;
w_num = BigDecimal.valueOf(w_int).add(BigDecimal.valueOf(1));
w_int = w_num.intValue();

longValuelong
 この BigDecimal を2進に変換した下位64bitを long 型として返す
 この際結果の long 型の符号bitは無視されて返されるので、 long 型に収まらない値を変換すると正負が逆転した意図しない値になることがある
toStringString
 この BigDecimal の文字列表現を返す
addBigDecimal BigDecimal 加算値
 引数に指定した BigDecimal 型の値を加算する
 計算後の精度は、両方の精度の大きいほうに合わされる
subtractBigDecimal BigDecimal 減算値
 引数に指定した BigDecimal 型の値を減算する
 計算後の精度は、両方の精度の大きいほうに合わされる
multiplyBigDecimal BigDecimal 乗算値
 引数に指定した BigDecimal 型の値を乗算する
 計算後の精度は、両方の精度を足した数になる
divideBigDecimal BigDecimal 除算値, int 丸め方式
 引数に指定した BigDecimal 型の値で除算する
 計算後の精度は元の BigDecimal の精度になり、引数で指定した方式で丸められる
divideBigDecimal BigDecimal 除算値, int 精度, int 丸め方式
 引数に指定した BigDecimal 型の値で除算する
 計算後の精度は引数で指定した精度になり、引数で指定した方式で丸められる
equalsboolean Object 比較値
 引数に指定した比較値と値、精度とも等しい場合は true 、そうでないときは false を返す
 例えば1と1.0は精度が異なるので false になる
 引数は Object 型なので BigDecimal 型以外を指定すると false になるが、実際にこれを使うときは BigDecimal 型に限定したほうが良いと思う
compareToint BigDecimal 比較値
 引数に指定した比較値と値が等しい場合は0 、比較値より大きいときは1、比較値より小さいときは-1が返る
 基本データ型の数値に対する比較演算子(<<===>=>!=)と同様の比較を記述したいときは、 (A.compareTo(B) 比較演算子 0) と記述するとよい
A = B のとき
A < Bfalse A.compareTo(B)0 A.compareTo(B) < 0false
A <= Btrue A.compareTo(B) <= 0true
A == Btrue A.compareTo(B) == 0true
A >= Btrue A.compareTo(B) >= 0true
A > Bfalse A.compareTo(B) > 0false
A != Bfalse A.compareTo(B) != 0false
A > B のとき
A < Bfalse A.compareTo(B)1 A.compareTo(B) < 0false
A <= Bfalse A.compareTo(B) <= 0false
A == Bfalse A.compareTo(B) == 0false
A >= Btrue A.compareTo(B) >= 0true
A > Btrue A.compareTo(B) > 0true
A != Btrue A.compareTo(B) != 0true
A < B のとき
A < Btrue A.compareTo(B)-1 A.compareTo(B) < 0true
A <= Btrue A.compareTo(B) <= 0true
A == Bfalse A.compareTo(B) == 0false
A >= Bfalse A.compareTo(B) >= 0false
A > Bfalse A.compareTo(B) > 0false
A != Btrue A.compareTo(B) != 0true
movePointLeftBigDecimal int 移動桁数
 引数に指定した桁数分、 BigDecimal の小数点位置を左にずらす
 精度には引数で指定した桁数分加算される
 12.345に対して movePointLeft(2) とすると、0.12345になる
movePointRightBigDecimal int 移動桁数
 引数に指定した桁数分、 BigDecimal の小数点位置を右にずらす
 精度には引数で指定した桁数分減算される(ただし減算結果が負になるときは精度は0になる)
 12.345に対して movePointRight(2) とすると、1234.5になる
scaleint
 この BigDecimal 型の現在の精度を返す
setScaleBigDecimal int 精度, int 丸め方式
  BigDecimal 型の現在の精度を、引数で指定した精度に変換する
 引数で指定した精度が現在の精度より小さい場合、引数で指定した方式で丸められる


 Javaの10進演算用の丸め方式は細かく分かれており、全部で8種類あるのですが、ここでは日常よく使うと思うものだけをあげておきます。これ以外についてはAPIドキュメントを参照してください。

主な丸め方式の定数値
丸め方式定数意味
ROUND_DOWN 切り下げによる丸めを行う
ROUND_UP 切り下げによる丸めを行う
ROUND_HALF_UP 四捨五入による丸めを行う

 ◇10進への変換例

 数値情報を BigDecimal 型に変換する方法について、ありがちないくつかのパターンについて具体的に例示します。例として挙げたものは、汎用機でCOBOLで組まれた基幹系システムが動いていて、連動ファイル経由で受けたデータの変換方法に重きを置いています。それから例中で数値を表す文字列中の'_'は、アンダーバーという文字ではなく1桁の空白と捉えてください。

 以下の例全体に対し、次のようにデータ定義がされているものとして話を進めます。

 String      w_src_dat = null;  // 文字列表記の数値データ
 int         w_src_num = 0;  // 2進整数の数値データ
 int         w_scl = 0;      // 数値データの精度(小数点以下桁数)
 BigDecimal w_num = null;      // 変換後の BigDecimal 数値データ

 ・2進からの変換

 精度の情報もすでにあり、厳密な書き方で変換すると
 w_num = BigDecimal.valueOf((long)w_src_num, w_scl);
となります。実際には
 w_num = BigDecimal.valueOf(w_src_num, w_scl);
と書いても自動的に型変換されているので大丈夫でした。

 精度についてはあとから適用するなら
 w_num = BigDecimal.valueOf((long)w_src_num);
 w_num = w_num.movePointLeft(w_src_scl);
となります。

  BigDecimal は参照データ型なので最初はインスタンス化する必要があります。w_numがすでにインスタンス化されていて、これを再利用するのなら最初の変換の部分は
 w_num = w_num.valueOf(w_src_num, w_scl);
と書けます。

 ・文字列からの変換

 文字列中に小数点まで含めた数値表現が入っている場合は、次のようにインスタンス化します。必ず trim メソッドで前後の空白を除いてください。
 w_num = new BigDecimal(w_src_dat.trim());

 例えばデータを作成する基幹側のCOBOLで
 05 W-SRC-DAT  PIC -(12).999.

 05 W-SRC-DAT  PIC -9(11).999.
と定義されているとき、こうなります。こうするとCOBOL側からの出力時、先頭の0は空白になり、小数点直前は値が負なら'-'、正なら空白になります。
 例えば上の定義だと-12.345なら"_________-12.345"、0.123なら"____________.123"と編集されます('_'は空白の意味です)。
 下の定義だと-12.345なら"-00000000012.345"、0.123なら" 00000000000.123" と編集されます。
 編集文字に'-'ではなく'+'を使って
 05 W-SRC-DAT  PIC +9(11).999.
とすれば0.123でも"+00000000000.123"となり、先頭の空白は含まれないようになるため trim は無くてもいいんですが、やはり必ず入れる癖を付けたほうがいいでしょう。

 このように数値データ中に小数点を含むのではなく、COBOL側で仮想小数点を使っている場合、今と同じ例なら
 05 W-SRC-DAT  PIC -(12)V999.

 05 W-SRC-DAT  PIC -9(11)V999.
となっている場合は変換方法が変わります。この'V'というのは計算する場合はそこに小数点があるものとして扱うが、データ中には実際の小数点は含まれない、ということを意味します。
 この場合は精度については連動元と連動先の間で事前に取り決めておく必要があります。
 下の定義で12345を表現すると" 00000012345000"、-12.345だと"-00000000012345"と編集されます。

 このときの変換方法は
 w_num = new BigDecimal(w_src_dat.trim());
 w_num = w_num.movePointLeft(3);

 w_num = BigDecimal.valueOf(Integer.parseInt(w_src_dat);
 w_num = w_num.movePointLeft(3);
あるいは
 w_num = BigDecimal.valueOf(Integer.parseInt(w_src_dat, 3);
としてください(もちろん取り決めに従いw_sclに精度を設定しておき、命令中に'3'という定数を書かないようにしても同じです)。




このページの先頭  Java編先頭  サーバーJava入門先頭

Copyright © 2002-2003 YOK. All Rights Reserved.