日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java时区处理初学者指南

發(fā)布時(shí)間:2023/12/3 java 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java时区处理初学者指南 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

基本時(shí)間觀念

大多數(shù)Web應(yīng)用程序必須支持不同的時(shí)區(qū),而正確處理時(shí)區(qū)絕非易事。 更糟糕的是,您必須確保各種編程語言(例如,前端JavaScript,中間件中的Java和作為數(shù)據(jù)存儲庫的MongoDB)之間的時(shí)間戳是一致的。 這篇文章旨在解釋絕對時(shí)間和相對時(shí)間的基本概念。

時(shí)代

紀(jì)元是絕對時(shí)間基準(zhǔn)。 大多數(shù)編程語言(例如Java,JavaScript,Python)使用Unix紀(jì)元(1970年1月1日午夜)來表示給定的時(shí)間戳,即自固定時(shí)間點(diǎn)引用以來經(jīng)過的毫秒數(shù)。

相對數(shù)字時(shí)間戳

相對數(shù)字時(shí)間戳表示為從紀(jì)元以來經(jīng)過的毫秒數(shù)。

時(shí)區(qū)

協(xié)調(diào)世界時(shí)(UTC)是最常見的時(shí)間標(biāo)準(zhǔn)。 UTC時(shí)區(qū)(相當(dāng)于GMT )表示所有其他時(shí)區(qū)涉及的時(shí)間參考(通過正/負(fù)偏移量)。

UTC時(shí)區(qū)通常稱為Zulu時(shí)間(Z)或UTC + 0。 日本時(shí)區(qū)為UTC + 9,而檀香山時(shí)區(qū)為UTC-10。 在Unix時(shí)代(1970年1月1日UTC時(shí)區(qū)),東京為1970年1月1日,檀香山為1969年12月31日14:00。

ISO 8601

ISO 8601是最廣泛的日期/時(shí)間表示標(biāo)準(zhǔn),它使用以下日期/時(shí)間格式:

時(shí)區(qū) 符號
世界標(biāo)準(zhǔn)時(shí)間 1970-01-01T00:00:00.000 + 00:00
UTC祖魯時(shí)間 1970-01-01T00:00:00.000 + Z
時(shí)雄 1970-01-01T00:00:00.000 + 09:00
火奴魯魯 1969-12-31T14:00:00.000-10:00

Java時(shí)間基礎(chǔ)

java.util.Date

java.util.Date絕對是最常見的時(shí)間相關(guān)類。 它表示一個固定的時(shí)間點(diǎn),表示為自歷元以來經(jīng)過的相對毫秒數(shù)。 java.util.Date是與時(shí)區(qū)無關(guān)的 ,除了toString方法使用本地時(shí)區(qū)生成String表示形式。

java.util.Calendar

java.util.Calendar既是日期/時(shí)間工廠,也是時(shí)區(qū)感知定時(shí)實(shí)例。 它是最不友好的Java API類之一,我們可以在以下示例中進(jìn)行演示:

@Test public void testTimeZonesWithCalendar() throws ParseException {assertEquals(0L, newCalendarInstanceMillis("GMT").getTimeInMillis());assertEquals(TimeUnit.HOURS.toMillis(-9), newCalendarInstanceMillis("Japan").getTimeInMillis());assertEquals(TimeUnit.HOURS.toMillis(10), newCalendarInstanceMillis("Pacific/Honolulu").getTimeInMillis());Calendar epoch = newCalendarInstanceMillis("GMT");epoch.setTimeZone(TimeZone.getTimeZone("Japan"));assertEquals(TimeUnit.HOURS.toMillis(-9), epoch.getTimeInMillis()); }private Calendar newCalendarInstance(String timeZoneId) {Calendar calendar = new GregorianCalendar();calendar.set(Calendar.YEAR, 1970);calendar.set(Calendar.MONTH, 0);calendar.set(Calendar.DAY_OF_MONTH, 1);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);calendar.setTimeZone(TimeZone.getTimeZone(timeZoneId));return calendar; }

在Unix時(shí)代(UTC時(shí)區(qū)),東京時(shí)間提前了9個小時(shí),而檀香山卻落后了10個小時(shí)。

更改日歷時(shí)區(qū)會在偏移時(shí)區(qū)偏移時(shí)保留實(shí)際時(shí)間。 相對時(shí)間戳隨日歷時(shí)區(qū)偏移量而變化。

Joda-Time和Java 8 Date Time API只是使java.util.Calandar過時(shí),因此您不必再使用此古怪的API。

org.joda.time.DateTime

Joda-Time旨在通過提供以下服務(wù)來修復(fù)舊版Date / Time API:

  • 不變和可變的日期結(jié)構(gòu)
  • 流利的API
  • 更好地支持ISO 8601標(biāo)準(zhǔn)

使用Joda-Time,這就是我們之前的測試用例的樣子:

@Test public void testTimeZonesWithDateTime() throws ParseException {assertEquals(0L, newDateTimeMillis("GMT").toDate().getTime());assertEquals(TimeUnit.HOURS.toMillis(-9), newDateTimeMillis("Japan").toDate().getTime());assertEquals(TimeUnit.HOURS.toMillis(10), newDateTimeMillis("Pacific/Honolulu").toDate().getTime());DateTime epoch = newDateTimeMillis("GMT");assertEquals("1970-01-01T00:00:00.000Z", epoch.toString());epoch = epoch.toDateTime(DateTimeZone.forID("Japan"));assertEquals(0, epoch.toDate().getTime());assertEquals("1970-01-01T09:00:00.000+09:00", epoch.toString());MutableDateTime mutableDateTime = epoch.toMutableDateTime();mutableDateTime.setChronology(ISOChronology.getInstance().withZone(DateTimeZone.forID("Japan")));assertEquals("1970-01-01T09:00:00.000+09:00", epoch.toString()); }private DateTime newDateTimeMillis(String timeZoneId) {return new DateTime(DateTimeZone.forID(timeZoneId)).withYear(1970).withMonthOfYear(1).withDayOfMonth(1).withTimeAtStartOfDay(); }

DateTime流利的API比java.util.Calendar#set易于使用。 DateTime是不可變的,但如果適合當(dāng)前的用例,我們可以輕松地切換到MutableDateTime 。

與我們的Calendar測試用例相比,當(dāng)更改時(shí)區(qū)時(shí),相對時(shí)間戳不會改變,因此保留了相同的原始時(shí)間點(diǎn)。

只是人類的時(shí)間感知發(fā)生了變化( 1970-01-01T00:00:00.000Z和1970-01-01T09:00:00.000 + 09:00指向相同的絕對時(shí)間)。

相對時(shí)間與絕對時(shí)間實(shí)例

當(dāng)支持時(shí)區(qū)時(shí),基本上有兩個主要選擇:相對時(shí)間戳和絕對時(shí)間信息。

相對時(shí)間戳

時(shí)間戳的數(shù)字表示形式(自紀(jì)元以來的毫秒數(shù))是相對信息。 該值是針對UTC時(shí)代給出的,但是您仍然需要一個時(shí)區(qū)來正確表示特定區(qū)域上的實(shí)際時(shí)間。

作為一個長值,它是最緊湊的時(shí)間表示形式,是交換大量數(shù)據(jù)時(shí)的理想選擇。

如果您不知道原始事件的時(shí)區(qū),則可能會顯示與當(dāng)前本地時(shí)區(qū)相對的時(shí)間戳,這并不總是可取的。

絕對時(shí)間戳

絕對時(shí)間戳包含相對時(shí)間以及時(shí)區(qū)信息。 在其ISO 8601字符串表示中表示時(shí)間戳是很常見的。

與數(shù)字形式(64位長)相比,字符串表示的緊湊性較低,它最多可包含25個字符(UTF-8編碼為200位)。

ISO 8601在XML文件中非常常見,因?yàn)閄ML模式使用的是受ISO 8601標(biāo)準(zhǔn)啟發(fā)的詞匯格式 。

當(dāng)我們想針對原始時(shí)區(qū)重構(gòu)時(shí)間實(shí)例時(shí),絕對時(shí)間表示會更加方便。 電子郵件客戶端可能希望使用發(fā)件人的時(shí)區(qū)顯示電子郵件創(chuàng)建日期,而這只能使用絕對時(shí)間戳來實(shí)現(xiàn)。

謎題

以下練習(xí)旨在說明使用古老的java.text.DateFormat實(shí)用程序正確處理符合ISO 8601的日期/時(shí)間結(jié)構(gòu)有多么困難。

java.text.SimpleDateFormat

首先,我們將使用以下測試邏輯來測試java.text.SimpleDateFormat解析功能:

/*** DateFormat parsing utility* @param pattern date/time pattern* @param dateTimeString date/time string value* @param expectedNumericTimestamp expected millis since epoch */ private void dateFormatParse(String pattern, String dateTimeString, long expectedNumericTimestamp) {try {Date utcDate = new SimpleDateFormat(pattern).parse(dateTimeString);if(expectedNumericTimestamp != utcDate.getTime()) {LOGGER.warn("Pattern: {}, date: {} actual epoch {} while expected epoch: {}", new Object[]{pattern, dateTimeString, utcDate.getTime(), expectedNumericTimestamp});}} catch (ParseException e) {LOGGER.warn("Pattern: {}, date: {} threw {}", new Object[]{pattern, dateTimeString, e.getClass().getSimpleName()});} }

用例1

讓我們看看各種ISO 8601模式如何針對第一個解析器表現(xiàn):

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "1970-01-01T00:00:00.200Z", 200L);

產(chǎn)生以下結(jié)果:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSS'Z', date: 1970-01-01T00:00:00.200Z actual epoch -7199800 while expected epoch: 200

此模式不符合ISO 8601。 單引號字符是一個轉(zhuǎn)義序列,因此最后的“ Z”符號不會被視為時(shí)間指令(例如Zulu時(shí)間)。 解析后,我們將僅獲取本地時(shí)區(qū)的Date參考。

該測試是使用我當(dāng)前的系統(tǒng)默認(rèn)歐洲/雅典時(shí)區(qū)運(yùn)行的,截至撰寫本文時(shí),它比UTC提前兩個小時(shí)。

用例2

根據(jù)java.util.SimpleDateFormat文檔,以下模式: yyyy-MM-dd'T'HH:mm:ss.SSSZ應(yīng)該匹配ISO 8601日期/時(shí)間字符串值:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200Z", 200L);

但是相反,我們得到了以下異常:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSSZ, date: 1970-01-01T00:00:00.200Z threw ParseException

因此,此模式似乎無法解析Zulu時(shí)間UTC字符串值。

用例3

以下模式對于顯式偏移量非常適用:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200+0000", 200L);

用例4

此模式還與其他時(shí)區(qū)偏移量兼容:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200+0100", 200L - 1000 * 60 * 60);

用例5

為了匹配祖魯語時(shí)間符號,我們需要使用以下模式:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "1970-01-01T00:00:00.200Z", 200L);

用例6

不幸的是,最后一個模式與明確的時(shí)區(qū)偏移量不兼容:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "1970-01-01T00:00:00.200+0000", 200L);

最后出現(xiàn)以下異常:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSSXXX, date: 1970-01-01T00:00:00.200+0000 threw ParseException

org.joda.time.DateTime

與java.text.SimpleDateFormat相反, Joda-Time與任何ISO 8601模式兼容。 以下測試用例將用于即將推出的測試用例:

/*** Joda-Time parsing utility* @param dateTimeString date/time string value* @param expectedNumericTimestamp expected millis since epoch*/ private void jodaTimeParse(String dateTimeString, long expectedNumericTimestamp) {Date utcDate = DateTime.parse(dateTimeString).toDate();if(expectedNumericTimestamp != utcDate.getTime()) {LOGGER.warn("date: {} actual epoch {} while expected epoch: {}", new Object[]{dateTimeString, utcDate.getTime(), expectedNumericTimestamp});} }

Joda-Time與所有標(biāo)準(zhǔn)ISO 8601日期/時(shí)間格式兼容:

jodaTimeParse("1970-01-01T00:00:00.200Z", 200L); jodaTimeParse("1970-01-01T00:00:00.200+0000", 200L); jodaTimeParse("1970-01-01T00:00:00.200+0100", 200L - 1000 * 60 * 60);

結(jié)論

如您所見,古老的Java Date / Time實(shí)用程序不容易使用。 Joda-Time是更好的選擇,提供更好的時(shí)間處理功能。

如果您碰巧使用Java 8,則值得切換到Java 8 Date / Time API ,該API是從頭開始設(shè)計(jì)的,但深受Joda-Time啟發(fā) 。

  • 代碼可在GitHub上獲得 。

翻譯自: https://www.javacodegeeks.com/2014/11/a-beginners-guide-to-java-time-zone-handling.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的Java时区处理初学者指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。