2012-06-21 43 views
1

我在將Timestamp對象轉換爲joda的LocalTime時遇到問題。從java.sql.Timestamp意外轉換爲joda的LocalDate

見下面的例子:

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' 
} 

我不能確定爲什麼 '新LOCALDATE的(T)',結果 '1111年11月17日'。任何人都可以幫助我嗎?

我注意到這個「問題」,同時使用joda-time-hibernate來填充我的bean的LocalDate屬性。

+1

難道這可能與陽曆和朱利安日曆的區別?你有與1999-11-11 00:00:00相同的問題嗎? –

+0

@RoddyoftheFrozenPeas不,我不知道。 'System.out.println(new LocalDate(Timestamp.valueOf(「1999-11-11 00:00:00」)))'打印'1999-11-11'。那是什麼意思? –

+1

我敢打賭,因爲你使用的是本地日期,所以你會遇到從Julian日曆到Gregorian日曆(我們現在使用的)的切換。 [Wikipedia](http://en.wikipedia.org/wiki/Gregorian_calendar#Adoption)對該主題進行了體面的討論。基本上,一些國家在切換時「失去」了 - 例如。 1582年12月9日之後的那天是1582年12月20日。我敢打賭,你的差異與此有關。 –

回答

4

這確實必須處理不同類型的日曆。 java.sql.Timestampjava.util.Date一樣,沒有任何日曆信息,因爲它們僅存儲爲一個數字,即自1970年以來的毫秒數,暗示假設在1582年10月4日到10月15日之間存在跳躍,當公曆正式通過,取代舊的儒略曆。所以,你不可能有一個Date(或Timestamp)對象代表1582年10月7日。如果你試圖創建這樣一個日期,你會在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 

換句話說,日期對象具有一個隱含的儒略+公曆年表,這兩個之間自動切換。

JodaTime有點智能,它支持幾種年代學,包括Julian繼承,Gregorian的proleptic,混合的Julian + Gregorian,以及與Gregorian幾乎相同的標準ISO年表。如果你讀the JavaDoc of the LocalDate.fromCalendarFields method,你會看到,它提到:

此工廠方法忽略了日曆的類型,並始終將創建ISO年表LOCALDATE的。

Julian + Gregorian混合年表的行爲就像隱含的Java日期,在兩個不同的日曆之間自動切換。純粹的時間表假設他們的日曆系統是永久使用的,所以例如它假定公曆從一開始就被使用。

讓我們看看各年表如何對待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 

正如你可以看到,這兩個現代年表(ISO和陽曆)報告正確的日期,如果他們從一開始就使用,而使用Julian日曆的兩個報告的日期爲,因爲它是當時已知的,儘管在事後看來我們與真正的春分日期相比,我們知道它要晚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年做了開關......

一毫秒,日期是:

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日之前沒有公曆日曆,但這個日期在擴展的,盛大的公曆中有效。就像找到一個刻在BCE紀念碑上的BCE日期一樣......在基督誕生之前,沒有人知道他們在基督之前是

因此,問題的根源在於日期字符串不明確,因爲您不知道要測量哪個日曆。今年是5772年,還是現在的希伯來語年? Java假定混合的Julian + Gregorian日曆。 JodaTime爲不同的日曆提供了廣泛的支持,默認情況下它採用ISO8601年表。您的日期會自動從1111年的Julian日曆轉換爲我們目前使用的ISO年表。如果您希望JodaTime增強的時間戳使用與java.sql.Timestamp類相同的年表,請在構建JodaTime對象時明確選擇GJChronology

0

過去幾個世紀裏發生這種事情的原因,與Julian日曆中的(全球)切換到公曆有關。基本上,由於各國以零碎的方式實施新的日曆系統,他們將在這個過程中失去幾天或幾周(有時甚至幾個月)。例如,在西班牙,1582年10月4日在朱利安日曆中,緊隨其後的是公曆1582年10月15日。 Wikipedia有一個體面的話題討論。

只要您的應用程序在過去不處理日期太遠,就不必處理與此相關的差異。正如你在你的評論中提到的,System.out.println(new LocalDate(Timestamp.valueOf("1999-11-11 00:00:00")))正確輸出1999-11-11。如果您需要使用這些較舊的日期,我建議您查看Julian日曆的替代日期。

(順便說一句,我很好奇,看看你問當地的日期,在西班牙的1582年10月5日...一天,從來沒有發生過會發生什麼事。)

+2

new DateTime(「1582-10-05」,GJChronology.getInstance(DateTimeZone.forID(「Europe/Madrid」))); - > IllegalFieldValueException:無法解析「1582-10-05」:不支持dayOfMonth的值5 – boneill