什么比日期和时区更难? SQL / JDBC中的日期和时区!
在jOOQ郵件列表上,最近有一個(gè)有趣的討論,關(guān)于jOOQ當(dāng)前缺乏對TIMESTAMP WITH TIME ZONE數(shù)據(jù)類型的現(xiàn)成支持。
沒有人說日期,時(shí)間和時(shí)區(qū)很容易! 這里有一個(gè)有趣的部分,我建議閱讀: 虛假的程序員相信時(shí)間
當(dāng)那還不夠時(shí),還請閱讀: 更多的虛假程序員相信時(shí)間
我個(gè)人喜歡程序員錯(cuò)誤地認(rèn)為“ Unix時(shí)間是自1970年1月1日以來的秒數(shù)”。 …Unix時(shí)間無法代表leap秒;)
返回JDBC
這是Jaybird開發(fā)人員(Firebird JDBC驅(qū)動(dòng)程序) Mark Rotteveel提出的一個(gè)有趣的Stack Overflow答案: java.sql.Timestamp時(shí)區(qū)是否特定?
可以按照以下方式觀察Mark的解釋(我在這里使用PostgreSQL):
Connection c = getConnection(); Calendar utc = Calendar.getInstance(TimeZone.getTimeZone("UTC"));try (PreparedStatement ps = c.prepareStatement("select"+ " ?::timestamp,"+ " ?::timestamp,"+ " ?::timestamp with time zone,"+ " ?::timestamp with time zone" )) {ps.setTimestamp(1, new Timestamp(0));ps.setTimestamp(2, new Timestamp(0), utc);ps.setTimestamp(3, new Timestamp(0));ps.setTimestamp(4, new Timestamp(0), utc);try (ResultSet rs = ps.executeQuery()) {rs.next();System.out.println(rs.getTimestamp(1) + " / " + rs.getTimestamp(1).getTime());System.out.println(rs.getTimestamp(2, utc)+ " / " + rs.getTimestamp(2, utc).getTime());System.out.println(rs.getTimestamp(3) + " / " + rs.getTimestamp(3).getTime());System.out.println(rs.getTimestamp(4, utc)+ " / " + rs.getTimestamp(4, utc).getTime());} }上面的程序使用Java和DB中使用時(shí)區(qū)而不使用時(shí)區(qū)的所有排列,并且輸出始終相同:
1970-01-01 01:00:00.0 / 0 1970-01-01 01:00:00.0 / 0 1970-01-01 01:00:00.0 / 0 1970-01-01 01:00:00.0 / 0如您所見,在每種情況下,UTC時(shí)間戳0均已正確存儲并從數(shù)據(jù)庫中檢索到。 我自己的語言環(huán)境是瑞士,即CET / CEST,在Epoch是UTC + 1,這就是在Timestamp.toString()上獲取輸出的地方。
當(dāng)您在SQL和/或Java中使用時(shí)間戳文字時(shí),事情會(huì)變得很有趣。 如果這樣替換綁定變量:
Timestamp almostEpoch = Timestamp.valueOf("1970-01-01 00:00:00");ps.setTimestamp(1, almostEpoch); ps.setTimestamp(2, almostEpoch, utc); ps.setTimestamp(3, almostEpoch); ps.setTimestamp(4, almostEpoch, utc);這又是我在CET / CEST上獲得的東西
1970-01-01 00:00:00.0 / -3600000 1970-01-01 00:00:00.0 / -3600000 1970-01-01 00:00:00.0 / -3600000 1970-01-01 00:00:00.0 / -3600000即不是Epoch,而是我首先發(fā)送到服務(wù)器的時(shí)間戳文字。 請注意,綁定/獲取的四種組合仍然始終產(chǎn)生相同的時(shí)間戳。
讓我們看看如果寫入數(shù)據(jù)庫的會(huì)話使用與從數(shù)據(jù)庫中獲取會(huì)話不同的時(shí)區(qū)(假設(shè)您在PST中)(我再次使用CET或UTC),會(huì)發(fā)生什么情況。 我正在運(yùn)行此程序:
Calendar utc = Calendar.getInstance(TimeZone.getTimeZone("UTC"));Calendar pst = Calendar.getInstance(TimeZone.getTimeZone("PST"));try (PreparedStatement ps = c.prepareStatement("select"+ " ?::timestamp,"+ " ?::timestamp,"+ " ?::timestamp with time zone,"+ " ?::timestamp with time zone" )) {ps.setTimestamp(1, new Timestamp(0), pst);ps.setTimestamp(2, new Timestamp(0), pst);ps.setTimestamp(3, new Timestamp(0), pst);ps.setTimestamp(4, new Timestamp(0), pst);try (ResultSet rs = ps.executeQuery()) {rs.next();System.out.println(rs.getTimestamp(1)+ " / " + rs.getTimestamp(1).getTime());System.out.println(rs.getTimestamp(2, utc)+ " / " + rs.getTimestamp(2, utc).getTime());System.out.println(rs.getTimestamp(3)+ " / " + rs.getTimestamp(3).getTime());System.out.println(rs.getTimestamp(4, utc)+ " / " + rs.getTimestamp(4, utc).getTime());} }它產(chǎn)生以下輸出:
1969-12-31 16:00:00.0 / -32400000 1969-12-31 17:00:00.0 / -28800000 1970-01-01 01:00:00.0 / 0 1970-01-01 01:00:00.0 / 0第一個(gè)時(shí)間戳記是將Epoch存儲為PST(16:00),然后數(shù)據(jù)庫中刪除了時(shí)區(qū)信息,這將Epoch變成了您在Epoch處的本地時(shí)間(-28800秒/ -8h),即真正存儲。
現(xiàn)在,當(dāng)我從自己的時(shí)區(qū)CET獲取該時(shí)間時(shí),我仍然想要獲取當(dāng)?shù)貢r(shí)間(16:00)。 但是在我的時(shí)區(qū)中,這不再是-28800秒,而是-32400秒(-9h)。 夠古怪嗎?
當(dāng)我獲取存儲的本地時(shí)間(16:00)時(shí),事情發(fā)生了相反的變化,但是我強(qiáng)迫獲取發(fā)生在UTC中,這將產(chǎn)生您存儲的時(shí)間戳,最初是PST(-28800)秒)。 但是,在我的時(shí)區(qū)CET中打印此時(shí)間戳(-28800秒)時(shí),現(xiàn)在是17:00。
當(dāng)我們在數(shù)據(jù)庫中使用TIMESTAMP WITH TIME ZONE數(shù)據(jù)類型時(shí),將保持時(shí)區(qū)(PST),并且當(dāng)我獲取Timestamp值時(shí),無論使用CET還是UTC,我仍然會(huì)得到Epoch,它已安全地存儲到了數(shù)據(jù)庫,在CET中打印為01:00。
ew。
TL; DR:
使用jOOQ時(shí) ,如果正確的UTC時(shí)間戳對您很重要,請使用TIMESTAMP WITH TIMEZONE,但是您必須實(shí)現(xiàn)自己的數(shù)據(jù)類型Binding ,因?yàn)閖OOQ當(dāng)前不支持該數(shù)據(jù)類型。 一旦使用了自己的數(shù)據(jù)類型Binding,就可以使用Java 8的time API,它比java.sql.Timestamp +丑陋的Calendar更好地表示了這些不同的類型。
如果當(dāng)?shù)貢r(shí)間對您很重要,或者您不在跨時(shí)區(qū)工作,則可以使用TIMESTAMP和jOOQ的Field <Timestamp>。
幸運(yùn)的是,如果您像我一樣,可以在一個(gè)只有一個(gè)時(shí)區(qū)的很小的國家/地區(qū)進(jìn)行操作,而大多數(shù)本地軟件都不會(huì)遇到此問題。
翻譯自: https://www.javacodegeeks.com/2015/07/whats-even-harder-than-dates-and-timezones-dates-and-timezones-in-sql-jdbc.html
總結(jié)
以上是生活随笔為你收集整理的什么比日期和时区更难? SQL / JDBC中的日期和时区!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAX-RS 2.x与Spring MV
- 下一篇: 带有Hibernate OGM的NoSQ