2014-04-01 49 views
17

我在Java 8中使用了新的java.time包。我有一箇舊數據庫,它給了我java.util.Date,我將其轉換爲InstantJava 8 java.time:在Instant中添加TemporalUnit與LocalDateTime

我想要做的是添加一段基於另一個數據庫標誌的時間。我可以增加幾天,幾周,幾個月或幾年。我不想關心我所添加的內容,我希望能夠在未來添加更多選項。

我的第一個想法是Instant.plus(),但這給了我一個UnsupportedTemporalTypeException值大於一天。即時顯然不支持大單位時間的操作。好吧,無論如何,LocalDateTime呢。

所以這給了我這個代碼:

private Date adjustDate(Date myDate, TemporalUnit unit){ 
    Instant instant = myDate.toInstant(); 
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    dateTime = dateTime.plus(1, unit); 
    Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant(); 
    return new Date(dueInstant.toEpochMilli()); 
} 

現在,這是使用新的API時我的第一次,所以我會在這裏錯過了一些東西。但似乎笨重,我認爲我必須去:

Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date. 

即使我沒有使用日期部分,我仍然認爲這是一個有點尷尬。所以我的問題是,我是否完全錯誤,做這件事的最好方法是什麼?


編輯:擴大對註釋的討論。

我想我現在有更好的想法瞭解LocalDateTime和Instant如何與java.util.Date和java.sql.Timestamp一起玩。感謝大家。

現在,更實際的考慮。假設用戶向我發送一個來自世界任何地方的任意時間的日期。他們發給我2014-04-16T13:00:00,我可以解析成LocalDateTime。然後我直接將其轉換爲java.sql.Timestamp並保存在我的數據庫中。

現在,沒有做任何事情,我從我的數據庫中取出java.sql.timestamp,使用timestamp.toLocalDateTime()轉換爲LocalDateTime。都好。然後我使用ISO_DATE_TIME格式將此值返回給我的用戶。結果是2014-04-16T09:00:00

我認爲這種差異是由於某種類型的隱式轉換爲/從UTC。我認爲我的默認時區可能會被應用到數值(EDT,UTC-4),這可以解釋爲什麼數字關閉了4個小時。

新的問題。本地時間到UTC的隱式轉換在哪裏發生?什麼是更好的方式來保存時區。我不應該直接從當地時間作爲一個字符串(2014-04-16T13:00:00)到LocalDateTime?我是否應該期待用戶輸入的時區?

+3

你打算在這裏代表什麼價值? 「即時」在邏輯上不知道日曆系統 - 這只是一個時間點 - 所以增加一個月沒有意義。您還應該仔細考慮您是否真的想要使用系統時區 - 您是否希望針對相同的值獲得不同的結果,具體取決於您正在運行的位置? –

+0

@JonSkeet那麼隨意選擇ZoneId會更合適嗎?總是格林威治時間什麼的看起來這可能會帶來一系列的問題。也許問題是我不知道如何表示我的java.util.date。我有一個時間點。如果符合某些條件,我想在未來將這一點改爲一個月(或日,年,無論)。 – jacobhyphenated

+0

使用UTC可能是最佳選擇,但您確實需要考慮您的要求。它甚至意味着將一個時間點(沒有時區或日曆)改變一個月? –

回答

16

我會繼續前進,並根據我最終的解決方案發表一個答案,以及對非常長的評論鏈的總結。

要啓動,整個轉換鏈:

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date 

是必要的,以保存時區信息,仍然做喜歡的對象一個日期,是意識到日曆和所有在其中的上下文的操作。否則,我們冒着隱式轉換到當地時區的風險,如果我們試圖將其轉換爲人類可讀的日期格式,則時間可能因此而改變。

例如,java.sql.Timestamp類中的toLocalDateTime()方法隱式轉換爲默認時區。這對我的目的是不理想的,但不一定是不好的行爲。但是,重要的是要意識到這一點。這是直接從遺留java日期對象轉換爲LocalDateTime對象的問題。由於傳統對象通常被認爲是UTC,因此轉換使用本地時區偏移量。

現在,讓我們的程序將輸入2014-04-16T13:00:00並保存到數據庫作爲java.sql.Timestamp

//Parse string into local date. LocalDateTime has no timezone component 
LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00"); 

//Convert to Instant with no time zone offset 
Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

//Easy conversion from Instant to the java.sql.Timestamp object 
Timestamp timestamp = Timestamp.from(instant); 

現在我們採取了時間戳,並添加天的一定數量它:

Timestamp timestamp = ... 

//Convert to LocalDateTime. Use no offset for timezone 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 

//Add time. In this case, add one day. 
time = time.plus(1, ChronoUnit.DAYS); 

//Convert back to instant, again, no time zone offset. 
Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

Timestamp savedTimestamp = Timestamp.from(output); 

現在,我們只需要輸出在​​格式人類可讀的字符串。

Timestamp timestamp = .... 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 
String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time); 
+6

ZoneOffset.ofHours(0)可以更好地表示爲ZoneOffset.UTC。您也可能發現使用ZonedDateTime更容易,因爲該類支持LocalDateTime的所有正/負行爲,但保留了時區信息。 – JodaStephen

+0

對於日期的具體使用情況,您不需要(本地/已分區)日期時間,因爲持續時間可以直接添加到即時:'Timestamp.from(timestamp.toInstant()。plus(Duration。 ofDays(1)))' –

+1

將'Duration.ofDays'添加到'Instant'將不考慮日光節約等,因此不推薦。改爲轉換爲「ZonedDateTime」。 –

相關問題