美好的一天,這不是一些錯誤。但是有人可以向我解釋這里發生了什么嗎?
我正在使用帶有 Spring 框架的免費 sql 資料庫,所以基本上我的 sql 資料庫在云上。然后我使用本地時區(印度尼西亞 (GMT 8))將日期時間值插入到我的資料庫中。但是時區與我的時區不正確,即使我已經使用過localdatetime.now()
情況就是這樣:
設定我的本地日期時區
LocalDateTime ldt = LocalDateTime.now();
使用我的系統默認時區
ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
這是輸出(我的時區顯示在 22:47) '2022-10-26 16:05:52'
使用 (GMT 8)
ZonedDateTime gmt = zdt.withZoneSameInstant(ZoneId.of("GMT 8"));
這是輸出(我的時區顯示在 23:10) ''2022-10-26 17:10:30''
使用 (GMT 12)
這是輸出(我的時區顯示在 23:15) ''2022-10-26 21:15:47''
使用 (GMT 14) 我什至不知道這個時區字面上是否正確
這是輸出(我的時區顯示在 23:20) ''2022-10-26 23:20:47'' 這是我的時區(基本上,這是正確的)
根據我自己的時鐘,使用 GMT 14 是正確的,但我的時區(印度尼西亞,GMT 8)。然后我檢查了我的資料庫位于云端的服務器,然后我在Asia Pacific找到了它。
這是我的代碼行:
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
ZonedDateTime gmt = zdt.withZoneSameInstant(ZoneId.of("GMT 14"));
Timestamp timestamp = Timestamp.valueOf(gmt.toLocalDateTime());
transaction.setDate(timestamp);
transactionServices.saveTransaction(transaction);
那么發生這種情況是因為我的資料庫所在的位置還是因為其他原因?有人可以解釋一下,這樣我就可以改進我的代碼。
我期待使用GMT 8而不是GMT 14,我擔心在生成此應用程式時這可能是一個問題。
uj5u.com熱心網友回復:
LocalDateTime.now()
我無法想象打電話LocalDateTime.now是正確的事情的場景。
ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
跳過LocalDateTime變數ldt。只需要求ZonedDateTime捕捉在您想要的時區中看到的當前時刻。
ZoneId z = ZonedId.systemDefault() ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
ZonedDateTime gmt = zdt.withZoneSameInstant(ZoneId.of("GMT 8"));
對于偏移量,請使用子類ZoneOffset而不是ZoneID.
ZoneOffset offset = ZoneOffset.of( 8 ) ;
而且,這段代碼在某些方面沒有意義。首先,如果您想通過偏移來表示一個時刻,請使用OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.now( offset ) ;
其次,您通常應該 ??更喜歡 time zone 而不是僅僅 offset。在通過時間進行加減運算時,僅使用偏移量意味著您將無法解釋特定時區人們使用的偏移量的變化。例如,您將無法考慮夏令時 (DST)轉換。DST 只是一個例子;政客們出于外交、軍事、實用、時尚等多種原因,頻繁地改變其管轄范圍內的時區偏移量。
定義提醒:
- 偏移量只是 UTC 之前或之后的小時、分鐘和秒數。
- 時區更多。時區是由政治家決定的特定地區人民使用的偏移量的過去、現在和未來變化的命名歷史。
例如,時區Australia/Perth當前使用的偏移量為 08:00。
ZoneId z = ZoneId.of( "Australia/Perth" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
順便說一句,對于日期時間值,請堅持使用標準ISO 8601格式。所以使用 08:00而不是"GMT 8". 我建議始終(a)包括小時和分鐘,以及(b)對一位數使用填充零。我見過不止一個庫只需要這樣的值。
穿越時區
你說你自己的時區在印度尼西亞。我猜你的意思是Asia/Jakarta。
捕捉那里看到的當前時刻。
ZoneId zJakarta = ZoneId.of( "Asia/Jakarta" ) ;
ZonedDateTime zdtJakarta = ZonedDateTime.now( zJakarta ) ;
調整為 UTC,這意味著與 UTC 的偏移量為零時分秒。只需提取一個Instant. 根據定義, AnInstant始終采用 UTC。
Instant instant = zdt.toInstant() ;
從 UTC 調整到另一個時區。
ZoneId zEdmonton = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdtEdmonton = instant.atZone( zEdmonton ) ;
或者逃課Instant。從印度尼西亞時間移動到加拿大時間。
ZonedDateTime zdtEdmonton = zdtJakarta.withZoneSameInstant( zEdmonton ) ;
?? 請注意,所有這些時刻物件(zdtJakarta、instant、zdtEdmonton)都指的是相同的同時時刻。這三個都代表時間線上的同一點,只是掛鐘/掛歷時間不同。
避免遺留類
你的代碼:
Timestamp timestamp = Timestamp.valueOf(gmt.toLocalDateTime());
transaction.setDate(timestamp);
??永遠不要使用有嚴重缺陷的java.sql.Timestamp. 這是現在遺留下來的血腥可怕的日期時間類之一,幾年前被現代java.time類所取代。
不要費心去研究這些類的行為。除非你想要一個關于如何不設計面向物件的時間跟蹤框架的大師班。
Sun、Oracle 和 JCP 社區都放棄了采用 JSR 310 的遺留類。我建議您也這樣做。
資料庫
要將片刻寫入資料庫,?? 使用OffsetDateTime. 標準 SQL 缺少ZonedDateTime.
要存盤時刻,時間線上的一個點,請確保您的資料庫表列的型別類似于 SQL 標準型別TIMESTAMP WITH TIME ZONE,而不是WITHOUT.
ZonedDateTime zdt = … ;
OffsetDateTime odt = zdt.toOffsetDateTime() ; // Discard time zone, keeping only the offset.
myPreparedStatement.setObject( … , odt ) ;
取回。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZonedDateTime zdt = odt.withOffsetSameInstant( myZoneId ) ;
你說:
發生這種情況是因為我的資料庫所在的位置
不要撰寫依賴于資料庫會話、服務器作業系統或 JVM 的當前默認時區的代碼。如果您像我在這里展示的那樣撰寫資料庫代碼,您就可以控制時區調整。
一刻都沒有
請注意,我沒有在任何地方使用LocalDateTime該類。那是因為您的問題是在詢問時刻,時間線上的特定點。
ALocalDateTime僅包含日期和時間。該類故意缺少時區或偏移量的概念。因此,LocalDateTime物件中的值本質上是模棱兩可的。如果你說“2023 年 1 月 23 日中午”,我不知道你是指東京的中午、圖盧茲的中午還是托萊多的中午——三個不同的時刻相隔幾個小時。
??LocalDateTime不能代表片刻。
標準 SQL 中的等效型別是TIMESTAMP WITHOUT TIME ZONE.
所有這些以及更多內容已經在 Stack Overflow 上多次介紹過。搜索以了解更多資訊。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/521357.html
