web-dev-qa-db-ja.com

Java.sql.DateはUnsupportedOperationExceptionを与えるため、toInstant()なしでJava.util.DateをLocalDateに切り捨てます

たまたま_Java.util.Date_である変数に対して_Java.sql.Date_のtoInstant()を使用すると、UnsupportedOperationExceptionが得られます。

_try {
    Java.util.Date input = new Java.sql.Date(System.currentTimeMillis());
    LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
    // grrr!
}
_

私が関わっている_Java.util.Date_は、レガシーAPIを介してmysql DBのDATEフィールドから取得され、実際には_Java.sql.Date_です。

現在、次の関連する質問はすべて非常に興味深いものです。

nsupportedOperationException-Java.sql.DateでtoInstant()を呼び出せないのはなぜですか?

Java.util.DateをJava.time.LocalDateに変換

LocalDateからJava.util.Dateへ、またはその逆の最も単純な変換?

ただし、時間コンポーネントを削除してJava 8 LocalDateを取得するために、_Java.util.Date_を切り捨てるエレガントな方法は提供されていません。

あるタイムゾーンの同じ瞬間が別のタイムゾーンの同じ瞬間とは異なる日付になる可能性があるという問題があることを認めます。

ソリューションにはJava.util.Calendarが含まれると思いますが、独自のソリューションを作成するのではなく、他の人が最初に行ったことを確立したいと思います。

私はこれより短いものを見つけたいと思います:

from Javaでタイムスタンプの時間部分をリセットする

_Date date = new Date();                      // timestamp now
Calendar cal = Calendar.getInstance();       // get calendar instance
cal.setTime(date);                           // set cal to date
cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
cal.set(Calendar.MINUTE, 0);                 // set minute in hour
cal.set(Calendar.SECOND, 0);                 // set second in minute
cal.set(Calendar.MILLISECOND, 0);            // set millis in second
Date zeroedDate = cal.getTime();             // actually computes the new Date
_
8
Adam

多くの場合、最も単純な解決策を見つけるのが最も困難です。

public LocalDate convertDateObject(Java.util.Date suspectDate) {

    try {
        // Don't do this if there is the smallest chance 
        // it could be a Java.sql.Date!
        return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

    } catch (UnsupportedOperationException e) {
        // BOOM!!
    }

    // do this first:
    Java.util.Date safeDate = new Date(suspectDate.getTime());

    return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

}
9
Adam

input変数が_Java.sql.Date_であることがわかっている場合は、それをキャストしてtoLocalDate()メソッドを呼び出すことができます。

_LocalDate date = ((Java.sql.Date) input).toLocalDate();
_

残念ながら、_Java.sql.Date_でtoInstant()を呼び出すことはできません。これは、 javadocによる であるため、常にUnsupportedOperationExceptionがスローされるためです。

タイプがわからない場合(_Java.util.Date_または_Java.sql.Date_のいずれか)、getTime()メソッドによって返される値を使用してInstant次に、それをタイムゾーンに変換し(以下では、JVMのデフォルトを使用しています)、最後にローカル日付を取得します。

_LocalDate date = Instant
    // get the millis value to build the Instant
    .ofEpochMilli(input.getTime())
    // convert to JVM default timezone
    .atZone(ZoneId.systemDefault())
    // convert to LocalDate
    .toLocalDate();
_

toLocalDate()メソッドは、残りを無視して日付部分(日/月/年)を取得するため、切り捨てる必要はありません。時刻が深夜、午前10時、またはその他の時間であるかどうかは関係ありません。時刻の場合、toLocalDate()はそれを無視し、日付部分のみを取得します。

ただし、本当に時間を真夜中に設定したい場合は、 withメソッド を使用して LocalTime を渡すことができます。

_LocalDate date = Instant
    // get the millis value to build the Instant
    .ofEpochMilli(input.getTime())
    // convert to JVM default timezone
    .atZone(ZoneId.systemDefault())
    // set time to midnight
    .with(LocalTime.MIDNIGHT)
    // convert to LocalDate
    .toLocalDate();
_

しかし、私が言ったように、toLocalDate()メソッドは時間部分を無視するだけなので、この場合、時間を設定する必要はありません(LocalDateは同じになります)。


次のように、日付のタイプを確認し、それに応じて適切なアクションを選択することもできます。

_if (input instanceof Java.sql.Date) {
    date = ((Java.sql.Date) input).toLocalDate();
} else {
    date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
_

JVMのデフォルトのタイムゾーン(ZoneId.systemDefault())を使用する代わりに、ZoneId.of("zoneName")を呼び出すことで、必要に応じて他のタイムゾーンを使用できます。ゾーン名は有効な-のいずれかです。 IANAタイムゾーン名 (常に_Region/City_の形式で、_America/New_York_または_Europe/London_のように)。 3文字の略語(CETPSTなど)は あいまいで標準ではない であるため、使用しないでください。

ZoneId.getAvailableZoneIds()を呼び出すことにより、使用可能なタイムゾーンのリストを取得できます(そしてシステムに最適なタイムゾーンを選択できます)。必要に応じて、JVMのデフォルトのタイムゾーンを引き続き使用することもできますが、それを忘れないでください 実行時でも予告なしに変更できます ので、使用しているタイムゾーンを常に明示することをお勧めします。

4
user7605325

tl; dr

myResultSet.getObject( … , LocalDate.class ) 

Java.time

Java.sql.DateJava.util.DateCalendarなどの従来の日時クラスを使用すると問題が発生しました。これらのクラスは完全に避けてください。それらは現在レガシーであり、Java.timeクラスに取って代わられています。

値は、タイプDATEのMySQL列に格納された値として始まったとおっしゃいました。このタイプは日付のみで、時刻はありません。したがって、不必要に時刻値を導入しました間違ったクラスを使用します。

JDBC 4.2以降に準拠したJDBCドライバーを使用して、Java.timeクラスを使用してデータベースと値を交換します。

LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;

そして、PreparedStatementに渡します。

myPstmt.setObject( … , myLocalDate ) ;
2
Basil Bourque

これを試して。

Java.util.Date input = new Java.sql.Date(System.currentTimeMillis());
Instant instant = Instant.ofEpochMilli(input.getTime());
LocalDate date = instant .atZone(ZoneId.systemDefault()).toLocalDate();
1
Suhas Saheer

tl; dr

Java.sql.DateJava.util.Dateの間の乗換えの前提が正しくありません。

たまたまJava.sql.Dateである変数に対してJava.util.Dateを使用する場合

クラスのドキュメントに記載されている契約に違反しました。 Java.sql.DateJava.util.Dateのサブクラスであるという事実を無視するように言われました。

しかし、解決策は非常に単純かもしれません。

ZonedDateTime zdt = myJavaSqlDate.toLocalDate().atStartOfDay( ZoneId.of( "America/Montreal" ) ) ;

詳細

どうやらあなたはJava.sql.Dateを手にしています。そのクラスは、時刻やタイムゾーンのない日付のみの値を表します。少なくともそれがそのクラスの意図です。 Java.util.Dateからサブクラス化するために、悲劇的に貧弱な設計上の選択が行われました。これは、名前にもかかわらず、UTCの日付および時刻を表します。この継承関係は、内部の時刻を「00:00:00」に設定するハック、悪いハックです。ドキュメントでは、この継承関係を無視し、時刻コンポーネントがあるという事実を無視し、2つのDateクラスが無関係であると偽ることを明確に指示しています。しかし、著者などの多くの人々は、そのドキュメントを注意深く読んでおらず、クラス名に基づいて大まかな仮定をしています。

両方のDateクラスは、Javaの初期バージョンに付属している日時フレームワークであるひどい混乱の一部です。これらは、Java 8以降に組み込まれているJava.timeクラスに取って代わられました。

従来の日時オブジェクトに遭遇したときの最初のステップ:古いクラスに追加された新しいメソッドを使用してJava.timeに変換します。

偽の日付のみのオブジェクトJava.sql.DateをタイプJava.util.LocalDateの真の日付のみのオブジェクトに変換します。 「ローカル」ワードは、タイムゾーンまたはUTCからのオフセットがないことを示します。

LocalDate ld = myJavaSqlDate.toLocalDate() ;

どうやら、その日付のみの値を時刻のある日付に変換したいようです。おそらくあなたはその日の最初の瞬間が欲しいでしょう。最初の瞬間を決定するには、タイムゾーンが必要です。たとえば、新しい日はEurope/ParisよりもAmerica/Montrealの方が早く、Asia/Kolkataの方がまだ早く始まります。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ; 
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
0
Basil Bourque

理由はわかりませんが、このコードは私のために機能します:)

LocalDate ld = new Java.util.Date(personEntity.getBirthday()。getTime())。toInstant()。atZone(ZoneId.systemDefault())。toLocalDate();

そしてこれは機能しません:

LocalDate ld = personEntity.getBirthday()。getTime()。toInstant()。atZone(ZoneId.systemDefault())。toLocalDate();

0
Omid Mohebbi

SimpleDateFormatを使用して支援しようとしましたか?

import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.time.LocalDate;
import Java.time.ZoneId;
import Java.util.Date;

public class Utils {

    private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );

    public static void main( String[] args ) {
        LocalDate ld = sqlDateToLocalDate( new Java.sql.Date( System.currentTimeMillis() ) );
        System.out.println( ld );
    }

    public static LocalDate sqlDateToLocalDate( Java.sql.Date sqlDate ) {

        try {
            Date d = sdf.parse( sdf.format( sqlDate ) );
            return d.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate();
        } catch ( ParseException exc ) {
        }

        return null;

    }

}
0
davidbuzatto