1

Timestamp オブジェクトを joda の LocalTime に変換する際に問題が発生しました。

以下の例を参照してください。

public static void main(String[] args) {

    Timestamp t = Timestamp.valueOf("1111-11-11 00:00:00");
    System.out.println(t); //-- prints '1111-11-11 00:00:00.0'
    System.out.println(new LocalDate(t)); //-- prints '1111-11-17'

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(t);
    System.out.println(LocalDate.fromCalendarFields(calendar)); //-- prints '1111-11-11'
}

「new LocalDate(t)」が「1111-11-17」になる理由を特定できませんでした。誰でもそれについて私を助けることができますか?

joda-time-hibernate を使用して LocalDate 型の Bean のプロパティを設定しているときに、この「問題」に気付きました。

4

2 に答える 2

5

これは確かに、さまざまな種類のカレンダーに関係しています。java.sql.Timestamp、 のようjava.util.Dateに、カレンダー情報はありません。1970 年からのミリ秒数としてのみ格納されているため、グレゴリオ暦が公式に制定された 1582 年 10 月 4 日から 10 月 15 日までの間にジャンプがあったという暗黙の仮定があります。古いユリウス暦に代わって採用されました。したがって、1582 年 10 月 7 日を表す Date (または Timestamp) オブジェクトを持つことはできません。そのような日付を作成しようとすると、自動的に 10 日後になります。例えば:

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    c.set(1582, Calendar.OCTOBER, 7, 0, 0, 0);
    c.set(Calendar.MILLISECOND, 0);
    d = c.getTime();
    System.out.println("Date: " + d);
    // Prints:
    // Date: Sun Oct 17 00:00:00 GMT 1582

言い換えれば、Date オブジェクトには暗黙のユリウス暦とグレゴリオ暦の年表があり、これら 2 つの間で自動的に切り替わります。

JodaTime は少し賢く、継続ユリウス暦、先発グレゴリオ暦、ユリウス暦とグレゴリオ暦の混合、グレゴリオ暦とほぼ同じ標準 ISO 暦など、いくつかの暦をサポートしていますmethodの JavaDocLocalDate.fromCalendarFieldsを読むと、次のことが言及されていることがわかります。

このファクトリ メソッドは、カレンダーのタイプを無視し、常に ISO 暦で LocalDate を作成します。

ユリウス暦とグレゴリオ暦の混合暦は、Java の暗黙的な日付のように動作し、2 つの異なる暦が自動的に切り替わります。純粋な年表は、その暦体系が永遠に使用されていることを前提としているため、たとえば、グレゴリオ暦が最初から使用されていると想定しています。

各 Chronology が 1111-11-11 の日付をどのように扱うかを見てみましょう。

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    c.set(1111, Calendar.NOVEMBER, 11, 0, 0, 0);
    c.set(Calendar.MILLISECOND, 0);
    Date d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

次のようになります。

Date: Sat Nov 11 00:00:00 GMT 1111 (-27079747200000 milliseconds)
ISO: 1111-11-18T00:00:00.000Z
Julian+Gregorian: 1111-11-11T00:00:00.000Z
Julian: 1111-11-11T00:00:00.000Z
Gregorian: 1111-11-18T00:00:00.000Z

ご覧のとおり、2 つの現代の年表 (ISO とグレゴリオ暦) は、それらが最初から使用されていた場合に正しい日付を報告しますが、ユリウス暦を使用する 2 つの暦は、当時知られていた日付を報告します。後から考えると、実際の春分の日と比較して 7 日ずれていることがわかっています。

スイッチの周りで何が起こったのか見てみましょう:

    c.set(1582, Calendar.OCTOBER, 15, 0, 0, 0);
    d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

最終的には次のようになります。

Date: Fri Oct 15 00:00:00 GMT 1582 (-12219292800000 milliseconds)
ISO: 1582-10-15T00:00:00.000Z
Julian+Gregorian: 1582-10-15T00:00:00.000Z
Julian: 1582-10-05T00:00:00.000Z
Gregorian: 1582-10-15T00:00:00.000Z

後に残るのはユリウス暦だけです。これは、グレゴリオ暦をまだ受け入れていないすべての国で有効な日付でした。当時は多くの国でした。ギリシャは 1923 年に切り替えを行いました...

その 1 ミリ秒前の日付は次のとおりです。

    c.add(Calendar.MILLISECOND, -1);
    d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

意味:

Date: Thu Oct 04 23:59:59 GMT 1582 (-12219292800001 milliseconds)
ISO: 1582-10-14T23:59:59.999Z
Julian+Gregorian: 1582-10-04T23:59:59.999Z
Julian: 1582-10-04T23:59:59.999Z
Gregorian: 1582-10-14T23:59:59.999Z

ISO とグレゴリオ暦は、10 月 15 日より前にグレゴリオ暦がなかったため、グレゴリオ暦には実際には存在しなかった日付を報告しますが、この日付は拡張された先発的なグレゴリオ暦で有効です。紀元前の記念碑に記された紀元前の日付を見つけるようなものです...キリストが生まれる前に、彼らがキリストの前にいたことを誰も知りませんでした。

したがって、問題の根本は、測定しているカレンダーがわからないため、日付文字列があいまいであることにあります。5772年は未来の年ですか、それとも現在のヘブライ年ですか? Java は、ユリウス暦とグレゴリオ暦が混在する暦を想定しています。JodaTime はさまざまなカレンダーを幅広くサポートしており、デフォルトでは ISO8601 暦を想定しています。日付は、1111 年に使用されていたユリウス暦から現在使用されている ISO 暦に自動的に変換されます。JodaTime で強化されたタイムスタンプでクラスと同じ年表を使用する場合は、JodaTime オブジェクトを構築java.sql.Timestampするときに明示的に を選択します。GJChronology

于 2012-08-24T22:43:30.783 に答える
0

これが過去数世紀の日付で発生している理由は、ユリウス暦からグレゴリオ暦への(世界的な)切り替えに関係しています。基本的に、各国が新しいカレンダーシステムを段階的に実装すると、その過程で数日または数週間(場合によっては数か月)が失われます。たとえばスペインでは、ユリウス暦の1582年10月4日の直後に、グレゴリオ暦の1582年10月15日が続きました。ウィキペディアには、このトピックに関するまともな議論があります。

アプリケーションが過去の日付をあまり処理しない限り、これに関連する不一致に対処する必要はありません。コメントでおっしゃったように、System.out.println(new LocalDate(Timestamp.valueOf("1999-11-11 00:00:00")))1999-11-11を正しく出力しました。これらの古い日付で作業する必要がある場合は、ユリウス暦の別の日付を調べることをお勧めします。

(余談ですが、1582年10月5日のスペインでの現地の日付を尋ねたらどうなるか知りたいです...決して起こらなかった日です。)

于 2012-06-22T18:54:54.987 に答える