2017-10-19 157 views
6

-509 VS 510

我看到了某種改變或錯誤的數據,與使用JDBC。所以我觀察在Java 8 Update 151上使用H2 Database版本1.4.196。年由負-509改變到JDBC積極的510 H2數據庫

這是一個完整的示例。

注意我們如何檢索日期值三次,第一次作爲一個LocalDate對象,其次爲文本,第三從鑄造LocalDate對象提取的int年數。在文本版中,我們可以看到這一年確實是負面的。神祕的LocalDate有不同的年份數字,它是積極的,而不是負面的。看起來像一個錯誤。

private void doIt () 
{ 
    System.out.println("BASIL - Running doIt."); 
    try 
    { 
     Class.forName("org.h2.Driver"); 
    } catch (ClassNotFoundException e) 
    { 
     e.printStackTrace(); 
    } 

    try (
      Connection conn = DriverManager.getConnection("jdbc:h2:mem:") ; // Unnamed throw-away in-memory database. 
    ) 
    { 
     conn.setAutoCommit(true); 
     String sqlCreate = "CREATE TABLE history (id IDENTITY , when DATE); "; 
     String sqlInsert = "INSERT INTO history (when) VALUES (?) ; "; 
     String sqlQueryAll = "SELECT * FROM history ; "; 

     PreparedStatement psCreate = conn.prepareStatement(sqlCreate); 

     psCreate.executeUpdate(); 

     PreparedStatement psInsert = conn.prepareStatement(sqlInsert); 

     psInsert.setObject(1 , LocalDate.of(2017 , 1 , 23)); 
     psInsert.executeUpdate(); 

     psInsert.setObject(1 , LocalDate.of(-509 , 1 , 1)); 
     psInsert.executeUpdate(); 

     PreparedStatement psQueryAll = conn.prepareStatement(sqlQueryAll); 
     ResultSet rs = psQueryAll.executeQuery(); 
     while (rs.next()) 
     { 
      long l = rs.getLong(1); // Identity column. 
      // Retrieve the same date value in three different ways. 
      LocalDate ld = rs.getObject(2 , LocalDate.class); // Extract a `LocalDate`, and implicitly call its `toString` method that uses standard ISO 8601 formatting. 
      String s = rs.getString(2); // Extract the date value as text from the database using the database-engine’s own formatting. 
      int y = ((LocalDate) rs.getObject(2 , LocalDate.class)).getYear(); // Extract the year number as an integer from a `LocalDate` object. 
      String output = "ROW: " + l+ " | " + ld + " | when as String: " + s+ " | year: " + y ; 
      System.out.println(output); 
     } 

     conn.close(); 
    } catch (SQLException e) 
    { 
     e.printStackTrace(); 
    } 
} 

運行時。

ROW:1 | 2017-01-23 |當as字符串:2017-01-23 |年份:2017

ROW:2 | 0510-01-01 |當作爲字符串時:-509-01-01 |年:510

因此,似乎有涉及JDBC的事情發生。請注意,今年如何呈現積極的510而不是消極的509.我不理解這種行爲。

我可以推斷出這是JDBC之內的問題,而不是LocalDate之內的問題。看到這個example code run live in IdeOne.com顯示一個LocalDate對象的確帶有並報告了一個負的年份。

LocalDate ld = LocalDate.of(-509 , 1 , 1) ; 
System.out.println("ld.toString(): " + ld) ; 
System.out.println("ld.getYear(): " + ld.getYear()) ; 

注意我們是怎麼做只有一個LocalDate打交道時得到從-509轉換爲510,沒有JDBC。

LD:-0509-01-01

ld.getYear():-509

我對H2的項目開了Issue ticket

+0

有趣的問題。我幾乎不瞭解JDBC,但預感會發生某種形式的下溢。 –

+0

受啓發[爲什麼查詢日期BC更改爲Java中的AD?](https://stackoverflow.com/questions/46835047/why-quering-a-date-bc-is-changed-to-ad-in- JAVA)?並不是說這個連接對這個問題很重要。 –

+0

對我來說聽起來更像是一個忘記了忘記通過這個時代的錯誤。但這是猜測。 –

回答

1

Bug,fixed

此問題出自bug in H2

現已修復,截至2018-01。

3

該問題是由java.sql.Date轉換爲LocalDate 所致。 因爲它是一個負年,Calendar實例保存所取得的結果將改變1 - 年,但轉換爲LocalDate Java時,不考慮附加信息(時代== BC),指示year < 0以下是返回結果前執行的最後一個方法。 enter image description here

試試這個:

public class Test { 
public static void main(String[] args) { 

      Calendar instance = Calendar.getInstance(); 
      instance.set(-509,1,1); 

      java.sql.Date d = new Date(instance.getTime().getTime()); 

      System.out.println(d.toLocalDate().getYear());// 510 


     } 
} 

感謝奧萊V.V.爲您的評論!

+0

是的,但在這種情況下,它也將時代設置到BC,所以所有的信息仍然應該在那裏以產生正確的'LocalDate'。必須有更多的事情發生,不能在那裏?不過謝謝你的部分解釋。 –

3

TL; DR:如果JDBC驅動程序在內部使用java.sql.Date和使用java.sql.Date.toLocalDate(),一個可疑的錯誤在棄用Date.getYear()將(至少暫時)使你觀察到的行爲,將其轉換。

這是猜測,但我發現它足夠有趣,可以分享。

我從SEY_91’s answer收集,司機確實使用一個或多個遺留日期和時間類,至少CalendarGregorianCalendar。從CalendarLocalDate的轉換路徑更多,因此通過java.sql.Date的轉換路徑只是其中之一。不過,其他轉換路徑可能會遇到同樣的錯誤。

事實:toLocalDate方法依賴於不推薦使用的getYear方法。來源:

@SuppressWarnings("deprecation") 
public LocalDate toLocalDate() { 
    return LocalDate.of(getYear() + 1900, getMonth() + 1, getDate()); 
} 

要查看getYear的行爲與上年共同的時代之前怎麼樣,我想:

OffsetDateTime dateTimeBce 
      = OffsetDateTime.of(-509, 1, 1, 0, 0, 0, 0, ZoneOffset.ofHours(1)); 
    Date d = Date.from(dateTimeBce.toInstant()); 
    System.out.println("d.toInstant() " + d.toInstant()); 
    System.out.println("d.getYear() (deprecated): " + d.getYear() 
         + ", means " + (d.getYear() + 1900)); 

由於從getYear年是「1900型」,預期今年將是 - 2409。如果我們爲此添加了1900,我們就會得到-509,這是我們開始的一年。然而,片斷輸出:

d.toInstant() -0510-12-31T23:00:00Z 
d.getYear() (deprecated): -1390, means 510 

第一行顯示了Date確實包含一個負年,因爲它應該(偏移轉換爲UTC改變從-509到-510的一年,我選擇的是標準時間偏移我的電腦的時區設置)。該片段使用java.util.Date,但java.sql.Date繼承了getYear方法,並且我也重現了與java.sql.Date類似的行爲。

我做了一個簡短的互聯網搜索任何提及的可疑錯誤沒有找到任何東西。我們可能想努力嘗試。

+0

但爲什麼字符串值('rs.getString(2)')顯示正確的負值年? –

+0

有趣的問題,@MickMnemonic。簡單的答案是,它不會經歷相同的(越野車)轉換。我的猜測是字符串是在數據庫端生成的,並以字符串形式從那裏傳遞,但我當然不知道。 –