![](/img/trans.png)
[英]Converting from Instant to XMLGregorianCalendar and vice-versa without losing precision
[英]How to handle CST to CDT or vice versa using XMLGregorianCalendar
我在夏令時更改CST-CDT時遇到以下問題。
我正按預期從Was8.5服務器2018-03-11-05.00(UTC-5)獲取輸入,但是當涉及WAS7服務器時,以下方法將返回Sun Mar 10 00.00.00 CST 2018而不是Sun Mar 11 00.00 .00 CDT 2018
/*
* Converts XMLGregorianCalendar to java.util.Date
*/
public static Date toDate(XMLGregorianCalendar calendar){
if(calendar == null) {
return null;
}
return calendar.toGregorianCalendar().getTime();
}
我知道服務器日期/時區重置未正確進行,但是如果我想正確設置CST到CDT的時間,或者反之亦然。 如何在Java中重寫代碼以將XMLGregorianCalendar
轉換為java.util.Date
?
像這樣,如果傳入請求是CST(UTC-6),則toDate(XMLGregorianCalendar日歷)返回CDT(UTC-5)。 那么我想toDate()應該返回CST(UTC-6)。
以同樣的方式,
如果傳入請求是CDT(UTC-5),則toDate(XMLGregorianCalendar日歷)返回CST(UTC-6)。 那么我想toDate()應該返回CDT(UTC-5)。
java.util.Date
沒有時區。 它只有一個long
值,代表自unix紀元以來的毫秒數。
您看到的( Sun Mar 10 00.00.00 CST 2018
)是toString()
方法的結果,它使用JVM默認時區將long
值轉換為該時區中的日期和時間。 請參閱本文以獲取更多詳細信息: https : //codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/
無論如何,一種真正知道發生了什么的方法是檢查這個long
值:
long millis = calendar.toGregorianCalendar().getTimeInMillis();
然后您可以在UTC中打印此值:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));
或者,如果您使用Java 8:
System.out.println(Instant.ofEpochMilli(millis));
這將告訴您Date
對應的UTC時刻,因此與依賴Date::toString()
方法相比,您可以對代碼進行一些調試,這會造成混淆和誤導。
關於您的主要問題,我嘗試重現(我使用Java 8是因為它比使用Date
更容易操作)。 首先,我在UTC-05:00創建了一個對應於2018-03-11的日期/時間,並且我認為時間是午夜:
// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");
然后,我將其轉換為America/Chicago
時區,該時區使用CST / CDT :
// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));
然后我打印了這個:
// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST
請注意,結果是2018年 3月10日23:00 -06:00 CST :UTC-06:00是3月10日。
這是因為在2018年, 夏令時僅在3月11日凌晨2點開始 。 在午夜,DST尚未開始,因此偏移量仍然是UTC-06:00。
無論如何,您的轉換代碼是正確的,因為“ Date
僅表示一個時間點(自紀元以來經過的時間),並且沒有附加時區。 也許問題出在哪里,檢查millis值可能有助於您了解發生了什么(我猜XmlGregorianCalendar
將時間設置為不存在的午夜,這可以解釋Sun Mar 10 00.00.00 CST 2018的結果 ) 。
如果有幫助,則發生DST轉換的確切UTC時刻(UTC-06:00 2018年3月11日凌晨2點)對應於1520755200000
值1520755200000
。 如果您在2018年3月的日期的值小於該值,則表示它們在DST開始之前,並且將在CST中。
我的第一個建議是您不需要您要的東西。 如我所見,您有一個日期和一個UTC偏移量,但我並沒有真正看到該偏移量會添加任何有用的信息。 就拿日期吧。 我相信發生的事情是,從3月11日過渡到夏季時間后的某個時間點被取消了一天中的時間,但是出於任何原因或根本沒有理由保留了UTC偏移量。 當指定一天開始時間(00:00)時,偏移量與您的美國/芝加哥時區(或中央時區)不一致,但建議使用地區/城市格式的ID明確。
並且不要使用java.util.Date
作為您的日期。 那堂課早已過時了。 今天,我們在現代Java日期和時間API java.time
有了很多改進。 此外,它的LocalDate
類更適合沒有日期的日期,因為這正是它的本質,而Date
實際上是一個時間點,也就是說,是一個完全不同的故事。 根據XMLGregorianCalendar
口味轉換,可以通過兩種方式進行。
直接方式
return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());
使用您的XMLGregorianCalendar
為2018-03-11-05:00
的結果為LocalDate
為2018-03-11
。
通過GregorianCalendar
和ZonedDateTime
的間接方式:
return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();
結果是一樣的。 后者的優點是您無需關注年,月和月中的各個字段。 除其他外,這意味着您不必冒險將它們放置在錯誤的順序中。
如果您確實堅持保持時區或UTC偏移,請至少獲取該偏移。 周日3月11日00.00.00 CDT 2018沒有意義,因為3月11日DST尚未生效(開始時間為02:00)。 這樣一個不存在的時間只會使所有人感到困惑。 將您的日歷對象轉換為OffsetDateTime
:
return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();
結果: 2018-03-11T00:00-05:00
。 該時間點存在。:-)
由於您的calendar
來自外部系統,因此您可能要對其進行驗證,因為任何字段都可能未定義並返回DatatypeConstants.FIELD_UNDEFINED
。 使用LocalDate.of()
,您可以決定對其參數進行驗證就足夠了,因為它將反對DatatypeConstants.FIELD_UNDEFINED
作為參數傳遞。 toGregorianCalendar()
將默認使用默認值,因此在使用它時,我認為驗證是必不可少的。
您的代碼出了什么問題?
我運行了您的代碼,與iolus類似(請參見其他答案 ),我在Sat Mar 10 23:00:00 CST 2018
得到了 這是正確的時間點。 正如iolus也解釋的那樣,這是Date.toString
以這種方式呈現時間點。 Date
對象本身沒有時區或UTC偏移量。 所以我應該說您的代碼是正確的。 toString
方法使您感到困惑。 許多事情已經擺在您面前,好的解決方案是完全避免使用Date
類。 我也認為您的觀察與WAS 7和WAS 8.5之間的任何差異無關。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.