- PR -

日付の比較

投稿者投稿内容
roushi
常連さん
会議室デビュー日: 2003/07/18
投稿数: 28
投稿日時: 2003-10-01 08:32
おはようございます。
日付の比較についてどうしてもわからないことがあります。

変数に日時分を格納しておき
システム時間の年と月を取得し
変数の日時分を足した時に
あきらかに成り立たない(2002年2月31日など)場合は
2002年1月31日という風に
一ヶ月ひいた値を設定したのですが
なかなかうまくいきません。

--------------ソース例--------------
//日時分を格納(31日22時23分)
String hizuke = "312223";

Calendar cal = Calendar.getInstance();
//システム時刻の年を取得
int int_year = cal.get(Calendar.YEAR);
//システム時刻の月を取得
int int_month = cal.get(Calendar.MONTH) + 1;
//変数に設定されている日を取得
int int_date = Integer.parseInt( hizuke.substring( 0,2 ) );
//変数に設定されている時を取得
int int_hour = Integer.parseInt(
hizuke.substring(2,4) );
//変数に設定されている分を取得
int int_minute = Integer.parseInt(
hizuke.substring(4,6) );
//セット
cal.set(int_year,int_month,int_date, int_hour,int_minute,0);
しかしこれでは
システム日付が2002年2月5日と設定している場合だと
上記の用にセットすると
2002年3月3日と解釈されてしまいます。
どうやら,仕様で2002年2月31日を2002年3月3日と解釈するように
なっているようです(28日から3日多いので,繰り越して3月+3日としている)


パターン1
Calendar cal = new GregorianCalendar();
cal.setLenient( false );
cal.set(2002, 4, ??); //??(※1)

try {
java.util.Date ud = cal.getTime();
System.out.println("正しい日付です。");
} catch (IllegalArgumentException iae) {
System.out.println("Error: 指定の年月日は存在しません");
}
※1.??=30なら正しい
しかし??=31でも正しいと表示してしまう。//これはあきらかに間違い
??=32ならExceptionを拾う
/* つまり
* getTimeを呼んだ時にExceptionを発生させる
* が・・・
* 2,4,6,9,11月に関わらず
* 29,30,31日が日付にセットされててもOKとしてしまう
*/


パターン2
Date dd = null;
try {
SimpleDateFormat fmt = new SimpleDateFormat();
fmt.setLenient( false );
fmt.applyPattern( "yyyy/MM/dd" );
dd = fmt.parse( "2002/2/??" ); //??(※2)
System.out.println("正しい");
}
catch ( java.text.ParseException e ) {
System.out.println( "エラー:" + e.getMessage() );
}

これだと
??が29の場合は「正しい」と表示し
??が30の場合は「エラー」を表示します。

しかし
catchの中で
2002年2月30日の日付がおかしいので
2002年1月30日の日付に変える処理を書くというのは
どうもおかしい気がします。
(Exceptionは例外をキャッチするものだと考えているので)


長くなって申し訳ありません。
catch以外で処理する方法などを
ご存知の方はどうがご教授下さい。
よろしくお願いします。

いたち
常連さん
会議室デビュー日: 2003/04/25
投稿数: 27
投稿日時: 2003-10-01 08:57
おはようございます。

引用:


パターン2
Date dd = null;
try {
SimpleDateFormat fmt = new SimpleDateFormat();
fmt.setLenient( false );
fmt.applyPattern( "yyyy/MM/dd" );
dd = fmt.parse( "2002/2/??" ); //??(※2)
System.out.println("正しい");
}
catch ( java.text.ParseException e ) {
System.out.println( "エラー:" + e.getMessage() );
}

これだと
??が29の場合は「正しい」と表示し
??が30の場合は「エラー」を表示します。

しかし
catchの中で
2002年2月30日の日付がおかしいので
2002年1月30日の日付に変える処理を書くというのは
どうもおかしい気がします。
(Exceptionは例外をキャッチするものだと考えているので)



catchの中で日付を変えるのがおかしいと考えた理由は何でしょうか?
例えば単純に、「日付入力フィールドに入力された値がおかしい場合は、メッセージを出力して再度入力を促す」処理を考えた場合、パターン2のようにコーディングして例外をキャッチしたところでメッセージ出力処理に移ると思います。
roushiさんのやりたいことは、メッセージを出力するのではなく、日付を修正することですが、ロジックは変わらないと思いますがいかがでしょうか?

あ、修正も出来ないような出鱈目な文字列には別途対応する必要があるのか。。。
koe
大ベテラン
会議室デビュー日: 2003/07/13
投稿数: 198
投稿日時: 2003-10-01 09:02
Calendar.getActualMaximum()を使って指定月の最大の日を取得し、
それが指定日より小さければ月を1つ減らす、という方法でどうでしょうか。

"320000"なんて与えられると無限ループに陥りますが。
ゆう
ベテラン
会議室デビュー日: 2003/06/20
投稿数: 56
投稿日時: 2003-10-01 09:30
こんにちわ。お世話になっております。
回答ではないので申し訳ないのですが……

手元に環境がないので試してはいないのですが、
パターン1の4月の日時チェックで31日が正しい日付と
認識されると書かれていますが、ちょっと調べたところ
Javaでは月は0〜11で保持しているようです。
#つまり8月であれば7と保持。

roushiさんの例示しているコードですと
5月のチェックをしているのではないでしょうか??
そうであれば31日まで存在するので例外は発生しないと思います。
しかし、2,4,6,11月にかかわらずと書かれているということは
12ヵ月すべての月で31日を「正しい日」として
認識されてしまうということなのでしょうか……

引用:

roushiさんの書き込み (2003-10-01 08:32) より:

パターン1
Calendar cal = new GregorianCalendar();
cal.setLenient( false );
cal.set(2002, 4, ??); //??(※1)

try {
java.util.Date ud = cal.getTime();
System.out.println("正しい日付です。");
} catch (IllegalArgumentException iae) {
System.out.println("Error: 指定の年月日は存在しません");
}
※1.??=30なら正しい
しかし??=31でも正しいと表示してしまう。//これはあきらかに間違い
??=32ならExceptionを拾う
/* つまり
* getTimeを呼んだ時にExceptionを発生させる
* が・・・
* 2,4,6,9,11月に関わらず
* 29,30,31日が日付にセットされててもOKとしてしまう
*/



[ メッセージ編集済み 編集者: ゆう 編集日時 2003-10-01 09:31 ]
roushi
常連さん
会議室デビュー日: 2003/07/18
投稿数: 28
投稿日時: 2003-10-01 10:22

koeさん,いたちさんへ-----------------------------
・今現在どちらのパターンでやるか検討中です
作成してみたら、こちらに返信しておきますので
申し訳ありませんが、しばしお待ち下さい。

・ゆうさんへ-------------------------------------
Javaドキュメントを確認してみたところ
ゆうさんのおっしゃる通りでした。
誤認しておりました。
roushi
常連さん
会議室デビュー日: 2003/07/18
投稿数: 28
投稿日時: 2003-10-02 22:27
返信が遅くなり申し訳ありませんでした。
実際に採用したのは,catchして
不正な日付がくれば,正しい日付になるように編集する処理を行う
という方法です。
採用した理由は,これなら230000などがきても
正しく日付を解釈してくれるからです。

public Timestamp dateComparison( String dateTimeMinutes ) {
// 返値格納用
Timestamp time = null;
Calendar cal = Calendar.getInstance();
//システム時刻用
Calendar calendar = Calendar.getInstance();
//システム時刻の年を取得
int int_year = cal.get(Calendar.YEAR);
//システム時刻の月を取得
int int_month = cal.get(Calendar.MONTH);
//引数に設定されている日を取得
int int_date = Integer.parseInt(
dateTimeMinutes.substring( 0,2 ) );
//引数に設定されている時を取得
int int_hour = Integer.parseInt(
dateTimeMinutes.substring( 2,4 ) );
//引数に設定されている分を取得
int int_minute = Integer.parseInt(
dateTimeMinutes.substring( 4,6 ) );

try{
//日付の厳密な判定を行う
cal.setLenient( false );
cal.set(int_year,int_month,int_date,
int_hour,int_minute,0);
//不正な日ならIllegalArgumentExceptionが発生
Date date = cal.getTime();
//比較用の日付
Timestamp ComparisonTimestamp = new Timestamp(date.getTime());
//システム時刻を取得(Timestamp型)
Timestamp sysTimestamp = new Timestamp( (calendar.getTime()).getTime() );
//比較
if ( ComparisonTimestamp.after( sysTimestamp ) ) {
cal.add(Calendar.MONTH,-1);
time = new Timestamp((cal.getTime()).getTime());
}
}catch(IllegalArgumentException iae){
System.out.println("引数に指定した日付が不正でした");
//一ヶ月マイナスしたものを設定
cal.set(int_year,int_month-1,int_date,
int_hour,int_minute,0);
time = new Timestamp((cal.getTime()).getTime());
}
return time;
}

今回考えさせられたことはたくさんありました。
まだ駆け出し中ですが、もっとJavaの知識をものにできるようにがんばります!

今回作成してみて疑問に思ったことを書き出してみます。

1.このようにcatchした中で適切な処理を行い
  リターンで呼び出し元のメソッドに返すというような
  処理は一般的なのでしょうか(使いかたとして正しい??)
2.オブジェクトを作成した場合は,ガーベージコレクションを
  助けるためにも,できる限り作成したオブジェクトに対して
(returnで返す直前などに)nullを代入した方がよいのでしょうか?

Javaに触れてまだ間もないので要領をつかめないでいます。
ご存知の方がいらっしゃれば,参考にお聞かせ願えませんでしょうか?
よろしくお願いします。
Kiriko
常連さん
会議室デビュー日: 2003/09/25
投稿数: 25
投稿日時: 2003-10-03 01:34
こんばんは。

引用:

今回作成してみて疑問に思ったことを書き出してみます。
1.このようにcatchした中で適切な処理を行い
  リターンで呼び出し元のメソッドに返すというような
  処理は一般的なのでしょうか(使いかたとして正しい??)


何ら問題はないと思います。
その例外が発生した時に実行したいことをcatch節に記述すれば
いいのではないでしょうか。
逆に何故そのような疑問をお持ちになったのかお聞きしたいです。
おそらく、書籍などでは次のようなコードが多く、
それが影響しているのかとは想像しています。

try {

} catch (Exception e) {
// StackTraceを出力しているだけで大した処理は記述されていない。
e.printStackTrace();
throw e; // or return;
}

引用:

2.オブジェクトを作成した場合は,ガーベージコレクションを
  助けるためにも,できる限り作成したオブジェクトに対して
(returnで返す直前などに)nullを代入した方がよいのでしょうか?


私はガーベージコレクションを意識して変数にnullを
代入したりしません。
メモリ管理をしなくていいというのがJavaの売りの1つであると
思っているからです。
また、メソッド内でのみ有効なローカル変数にnullを設定したと
してもnonsenseのように感じます。
GCを意識してコーディングされている方は多くはないと思うのですが、
どのようなケースに意識するのかは興味ありますね。
koe
大ベテラン
会議室デビュー日: 2003/07/13
投稿数: 198
投稿日時: 2003-10-03 02:26
引用:

roushiさんの書き込み (2003-10-02 22:27) より:
実際に採用したのは,catchして
不正な日付がくれば,正しい日付になるように編集する処理を行う
という方法です。
採用した理由は,これなら230000などがきても
正しく日付を解釈してくれるからです。


とおっしゃっていますが、正しく解釈できることを確認しましたか?
私の手元で、引数に"320000"(恐らく230000はこれのtypoだと思います)
を指定して実行すると以下のcatch節の中で
java.lang.IllegalArgumentExceptionが発生します。
(java.lang.IllegalArgumentExceptionの発生を正しい解釈と定義するなら
私の勘違いです。この書き込みは無視してください。)
引用:

cal.set(int_year,int_month-1,int_date,
int_hour,int_minute,0);


「32日」というのは、9月でも8月でも不正な日には変わりないわけで、
その場合1月戻すというアプローチを取っている限り正しい日付を得る
ことは出来ません。このときに、だめだったのでもう一月戻してみるか…
という処理を繰り返せば無限ループに陥りますね、というのが
前の私の書き込みの意図でした。

こういう誤解を招いたあたり、今更ですが、前の私の書き込みで無限ループの
問題があると示したのは適切ではなかったのだと思います。

解決策ですが、ユリウス暦は知りませんがグレゴリオ暦なら30日以下の月の後には
必ず31日の月があるので、1つ前の月に戻しても日が不正なら、
「さらなる訂正処理」をする、という方法でどうでしょうか。
どういう「さらなる訂正処理」が望ましいかは仕様によるので何ともいませんが。

ここからは全然関係ない話です。
変数にint_のような型名を示すプレフィックスをつけるのは、
コードの可読性の低下(変数名が長くなる、似たような名前だが意味が異なる変数が増える)、
コーディング効率の低下(同じ文字から始まる変数が増えることで
エディタやIDEの入力補完機能が有効に機能しなくなる)などの弊害があります。
変数の型チェックや型変換の妥当性チェックをコンパイラやIDEに任せられる
環境があるのでしたら、プレフィックスをつけないコーディング規則をお薦めします。

この話はスレッドと関係の少ない話ですので、別スレッドに書いた方がいいかもしれません。
場合によっては別スレッドを立てて、この部分ごと引っ越すことも考えておきます。

スキルアップ/キャリアアップ(JOB@IT)