<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    LetsCoding.cn

    天地之間有桿秤,拿秤砣砸老百姓。

    Java 8:健壯、易用的時間/日期API終于來臨

    對很多應用來說,時間和日期的概念都是必須的。像生日,租賃期,事件的時間戳和商店營業時長,等等,都是基于時間和日期的;然而,Java卻沒有好的API來處理它們。在Java SE 8中,添加了一個新包:java.time,它提供了結構良好的API來處理時間和日期。

    歷史

    在Java剛剛發布,也就是版本1.0的時候,對時間和日期僅有的支持就是java.util.Date類。大多數開發者對它的第一印象就是,它根本不代表一個“日期”。實際上,它只是簡單的表示一個,從1970-01-01Z開始計時的,精確到毫秒的瞬時點。由于標準的toString()方法,按照JVM的默認時區輸出時間和日期,有些開發人員把它誤認為是時區敏感的。

    在升級Java到1.1期間,Date類被認為是無法修復的。由于這個原因,java.util.Calendar類被添加了進來。悲劇的是,Calendar類并不比java.util.Date好多少。它們面臨的部分問題是:

    • 可變性。像時間和日期這樣的類應該是不可變的。
    • 偏移性。Date中的年份是從1900開始的,而月份都是從0開始的。
    • 命名。Date不是“日期”,而Calendar也不真實“日歷”。
    • 格式化。格式化只對Date有用,Calendar則不行。另外,它也不是線程安全的。

    大約在2001年,Joda-Time項目開始了。它的目的很簡單,就是給Java提供一個高質量的時間和日期類庫。盡管被耽擱了一段時間,它的1.0版還是被發布。很快,它就成為了廣泛使用和流行的類庫。隨著時間的推移,有越來越多的需求,要在JDK中擁有一個像Joda-Time的這樣類庫。在來自巴西的Michael Nascimento Santos的幫助下,官方為JDK開發新的時間/日期API的進程:JSR-310,啟動了。

    綜述

    新的API:java.time,由5個包組成:

    大多數開發者只會用到基礎和format包,也可能會用到temporal包。因此,盡管有68個新的公開類型,大多數開發者,大概,將只會用到其中的三分之一。

    日期

    在新的API中,LocalDate是其中最重要的類之一。它是表示日期的不可變類型,不包含時間和時區。

    “本地”,這個術語,我們對它的熟悉來自于Joda-Time。它原本出自ISO-8061的時間和日期標準,它和時區無關。實際上,本地日期只是日期的描述,例如“2014年4月5日”。特定的本地時間,因你在地球上的不同位置,開始于不同的時間線。所以,澳大利亞的本地時間開始的比倫敦早10小時,比舊金山早18小時。

    LocalDate被設計成,它的所有方法,都是常用方法:

    1. LocalDate date = LocalDate.of(2014, Month.JUNE10);
    2. int year = date.getYear(); // 2014
    3. Month month = date.getMonth(); // 6月
    4. int dom = date.getDayOfMonth(); // 10
    5. DayOfWeek dow = date.getDayOfWeek(); // 星期二
    6. int len = date.lengthOfMonth(); // 30 (6月份的天數)
    7. boolean leap = date.isLeapYear(); // false (不是閏年)

    在上面的例子中,我們看到日期使用工廠方法(所有的構造方法都是私有的)創建。然后用它查詢了部分基本信息。注意:枚舉類型,MonthDayOfWeek,被設計用來增強代碼的可讀性和可靠性。

    在下面的例子中,我們來看看如何操作LocalDate的實例。由于它是不可變類型,每次操作都會產生一個新的實例,而原有實例不收任何影響。

    1. LocalDate date = LocalDate.of(2014, Month.JUNE10);
    2. date = date.withYear(2015); // 2015-06-10
    3. date = date.plusMonths(2); // 2015-08-10
    4. date = date.minusDays(1); // 2015-08-09

    上面這些都很簡單,但有時我們需要對日期進行更復雜的修改。java.time包含了對此的處理機制:TemporalAdjuster類。時間修改器背后的設計思想是,提供一個預裝包的、能操縱日期的功能,比如,根據月份的最后一天獲取日期的對象。API提供了一些通用的功能,你可以新增你自己的。修改器的使用很簡單,但使用靜態導入的方式,會讓你更方便:

    1. import static java.time.DayOfWeek.*
    2. import static java.time.temporal.TemporalAdjusters.*
    3.  
    4. LocalDate date = LocalDate.of(2014, Month.JUNE10);
    5. date = date.with(lastDayOfMonth());
    6. date = date.with(nextOrSame(WEDNESDAY));

    java.time包中,像所有主要的時間/日期類一樣,LocalDate類是固定于單個歷法系統的:ISO-8601標準定義的歷法。
    看見修改器的瞬間反應就是,這代碼跟業務邏輯長的差不多??!這對時間/日期類業務邏輯是很重的。我們最后想看的是多次手動修改日期。如果你的代碼庫里,有一個將會使用很多次的,對日期的通用操作,考慮把它做成修改器,然后告訴你的小組成員,把它當做一個寫好的,測試過的組件,直接拿來用吧。

    時間/日期值對象

    值得花點時間弄清楚,是什么導致了LocalDate類成為值類型。值類型是這樣一種簡單的數據類型:2個實例,只要內容相同,就該是可以相互替換的,是不是同一個實例,并不重要。String類就是一個標準的值類型的例子,只要字面值一樣,我們就認為它們相等,而不關心它們是不是同一個String對象的不同引用。

    大部分時間/日期類都應該是值類型的,java.time開發包印證了這一點。因此,我們沒有理由去用==來判斷2個LocalDate是不是相等,實際上,Javadoc也反對這樣做。

    對于值類型,想要更多了解的同學,可以參考我最近的文章,VALJOs:在Java中,它對值類型,定義了嚴格的規則集合,包括不可變性、工廠方法和良好定義的equals(),hashCode()toString()compareTo()方法。

    不同的歷法系統

    java.time包中,像所有主要的時間/日期類一樣,LocalDate是固定于單個歷法系統的:由ISO-8601標準定義。

    ISO-8601歷法系統是事實上的世界民用歷法系統,也就是公歷。平年有365天,閏年是366天。閏年的定義是:非世紀年,能被4整除;世紀年能被400整除。為了計算的一致性,公元1年的前一年被當做公元0年,以此類推。

    采用這套歷法,第一個影響就是,ISO-8601的日期不必跟GregorianCalendar一致。在GregorianCalendar中,凱撒歷格里高利歷之間有一個轉換日,一般默認在1582年10月15日。那天之前,用凱撒歷:每4年一個閏年,沒有例外。那天之后,用格里高利歷,也就是公歷,擁有稍微復雜點的閏年計算方式。

    既然凱撒歷和格里高利歷之間的轉換是個歷史事實,那為什么新的java.time開發包不參照它呢?原因就是,現在使用歷史日期的大部分Java應用程序,都是不正確的,繼續下去,是個錯誤。這是為什么呢?當年,羅馬的梵蒂岡,把歷法從凱撒歷改換成格里高利歷的時候,世界上大部分其他地區并沒有更換歷法。比如大英帝國,包括早期的美國,直到大約200后的1752年9月14日才換歷法,沙俄直到1918年2月14日,而瑞典的歷法轉換更是一團糟。因此,實際上,對1918之前的日期,解釋是相當多的;僅相信擁有單一轉換日的GregorianCalendar,是不靠譜的。LocalDate中沒有這種轉換,就是一個合理的選擇了。應用程序需要額外的上下文信息,在凱撒歷和格里高利歷間,精確的解釋特定的歷史日期。

    第二個影響是,我們需要額外的一組類來幫助處理其他歷法系統。Chronology接口,是其他歷法的主要入口點,它允許通過所屬的語言環境查找對應的歷法系統。Java 8支持額外的4個歷法系統:泰國佛教歷,中華民國歷,日本歷(沿襲中國古代帝位紀年),伊斯蘭歷。如有需要,應用程序也可以實現自己的歷法系統。

    每個歷法系統都有自己的日期類,有ThaiBuddhistDate,MinguoDate,JapaneseDate,HijrahDate。它們應在對本地化有嚴重需求的應用中使用,比如為日本政府開發的系統。它們4個都繼承了另外一個接口,ChronoLocalDate,可以讓代碼在不知道歷法系統的情況下,去操作它們。雖然如此,但還是希望少用這個接口。

    理解為什么少用ChronoLocalDate,對正確的使用整個java.time開發包很關鍵。真實情況是,當我們檢視當前的應用,盡量以歷法無關的方式來操作日期的大部分代碼,都有問題。例如,你不能假定一年有12個月,而開發者都是這樣認為的,并且增加12個月,他們就認為是增加了一年。你不能認為所有的月份都有相同的天數,比如,科普特人的歷法,包括12個30天的月份,還有一個月僅有5天,或者6天。你也不能認為,下一年的年份就比現在的年份大了1年,比如日本歷,在天皇換代時,會重新紀年,此時,還在那一年的年中(你甚至不能認為在同一個月的兩天,是屬于同一年的)。

    在一個大型的系統中,唯一的以歷法無關的方式開發的方法是:形成嚴格的代碼審查制度,對日期和時間相關的每行代碼都要做雙重檢查,以防偏向ISO歷法系統。因此,推薦的做法是,在系統中,全部使用LocalDate,包括存儲,操作和解釋業務規則。僅有的,使用ChronoLocalDate的時候是,本地化的輸入/輸出,典型的做法是使用用戶配置中首選的歷法;即使如此,大多數應用并不需要那樣的本地化級別。

    如需更全面的了解,查看ChronoLocalDate的Javadoc

    時間

    日期之后,下一個考慮的概念就是本地時間,LocalTime。典型的例子就是便利店的營業時間,例如從07:00到23:00(早上7點到晚上11點)??赡?,在這個時間段營業的便利店,遍布整個美利堅,但是這個時間是本地化的,跟時區無關。

    LocalTime是值類型,且跟日期和時區沒有關聯。當我們對時間進行加減操作時,以午夜基準,24小時一個周期。因此,20:00加上6小時,結果就是02:00。

    LocalTime的用法跟LocalDate相似:

    1. LocalTime time = LocalTime.of(2030);
    2. int hour = date.getHour(); // 20
    3. int minute = date.getMinute(); // 30
    4. time = time.withSecond(6); // 20:30:06
    5. time = time.plusMinutes(3); // 20:33:06

    修改器機制同樣適用于LocalTime,只是對它的復雜操作比較少。

    時間和日期組合

    下一個要考察的是LocalDateTime類。這個值類型只是LocalDateLocalTime的簡單組合。它表示一個跟時區無關的日期和時間。

    LocalDateTime可以直接創建,或者組合時間和日期:

    1. LocalDateTime dt1 = LocalDateTime.of(2014, Month.JUNE102030);
    2. LocalDateTime dt2 = LocalDateTime.of(date, time);
    3. LocalDateTime dt3 = date.atTime(2030);
    4. LocalDateTime dt4 = date.atTime(time);

    第三和第四行使用atTime()方法,平滑地構造一個LocalDateTime實例。大部分的時間和日期類都有“at“方法:以這樣的方式,把當前對象和其他對象組合,生成更復雜的對象。

    LocalDateTime的其他方法跟LocalDateLocalTime相似。這種相似的方法模式非常有利于API的學習。下面總結了用到的方法前綴:

    • of: 靜態工廠方法,從組成部分中創建實例
    • from: 靜態工廠方法,嘗試從相似對象中提取實例。from()方法沒有of()方法類型安全
    • now: 靜態工廠方法,用當前時間創建實例
    • parse: 靜態工廠方法,總字符串解析得到對象實例
    • get: 獲取時間日期對象的部分狀態
    • is: 檢查關于時間日期對象的描述是否正確
    • with: 返回一個部分狀態改變了的時間日期對象拷貝
    • plus: 返回一個時間增加了的、時間日期對象拷貝
    • minus: 返回一個時間減少了的、時間日期對象拷貝
    • to: 把當前時間日期對象轉換成另外一個,可能會損失部分狀態
    • at: 用當前時間日期對象組合另外一個,創建一個更大或更復雜的時間日期對象
    • format: 提供格式化時間日期對象的能力

    時間點

    在處理時間和日期的時候,我們通常會想到年,月,日,時,分,秒。然而,這只是時間的一個模型,是面向人類的。第二種通用模型是面向機器的,或者說是連續的。在此模型中,時間線中的一個點表示為一個很大的數。這有利于計算機處理。在UNIX中,這個數從1970年開始,以秒為的單位;同樣的,在Java中,也是從1970年開始,但以毫秒為單位。

    java.time包通過值類型Instant提供機器視圖。Instant表示時間線上的一點,而不需要任何上下文信息,例如,時區。概念上講,它只是簡單的表示自1970年1月1日0時0分0秒(UTC)開始的秒數。因為java.time包是基于納秒計算的,所以Instant的精度可以達到納秒級。

    1. Instant start = Instant.now();
    2. // perform some calculation
    3. Instant end = Instant.now();
    4. assert end.isAfter(start);

    Instant典型的用法是,當你需要記錄事件的發生時間,而不需要記錄任何有關時區信息時,存儲和比較時間戳。它額很多有趣的地方在于,你不能對它做什么,而不是你能做什么。例如,下面的幾行代碼會拋出異常:

    1. instant.get(ChronoField.MONTH_OF_YEAR);
    2. instant.plus(6, ChronoUnit.YEARS);

    拋出這些異常是因為,Instant只包含秒數和納秒數,不提供處理人類意義上的時間單位。如果確實有需要,你需要額外提供時區信息。

    時區

    時區的概念由大英帝國開始采用。鐵路的發明和通訊工具的改進,突然意味著人們的活動范圍,大到跟太陽時的改變有很大的關系。在此之前,每個城鎮和村莊,都通過太陽和日晷規定自己的時間。

    下面的英國布魯斯托交易所的時鐘照片,顯示了時區導致的最初混亂的一個例子。紅色的指針顯示格林威治時間,而黑色指針顯示布魯斯托時間,它們相差10分鐘。

    在技術的推動下,標準的時區系統慢慢演進,最終替代了老舊的本地太陽法計時。然而,關鍵的事實是,時區也是政治的產物。它們被用來顯示對一個地區的政治控制,例如,最近的克里米亞時改為莫斯科時。一旦和政治掛鉤,相關的規則常常不合邏輯。

    時區規則,由一個發布IANA時區數據庫的國際組織搜集和匯總。這些數據,包含地球上每個地區的標識和時區的歷史變化。標識的格式類似”歐洲/倫敦“,或者”美洲/紐約“。

    java.time之前,我們用TimeZone表示時區,而現在,用ZoneId。它們有2個主要的不同。第一,ZoneId是不可變的,它里面保存時區縮寫的靜態變量也是不可變的;第二,實際的規則集在ZoneRules里,不在ZoneId中,通過getRules()方法可以獲得。

    時區的常見情況,是從UTC/格林威治開始的一個固定偏移。我們通常在說時差的時候,會遇到它,例如,我們說紐約比倫敦晚5個小時。ZoneId的子類,ZoneOffset,代表了這種從倫敦格林威治零度子午線開始的時間偏移。

    作為一個開發者,如果不用去處理時區和它帶來的復雜性,將會是非常棒的。java.time開發包盡最大努力的幫助你那樣做。只要有可能,盡量使用LocalDate,LocalTime,LocalDateInstant。當你不能回避時區時,ZonedDateTime可以滿足你的需求。

    ZoneDateTime負責處理面向人類的(臺歷和掛鐘上看到的)時間和面向機器的時間(始終連續增長的秒數)之間的轉換。因此,你可以通過本地時間或時間點來創建ZoneDateTime實例:

    1. ZoneId zone = ZoneId.of("Europe/Paris");
    2.  
    3. LocalDate date = LocalDate.of(2014, Month.JUNE10);
    4. ZonedDateTime zdt1 = date.atStartOfDay(zone);
    5.  
    6. Instant instant = Instant.now();
    7. ZonedDateTime zdt2 = instant.atZone(zone);

    最惱人的時區問題之一就是夏令時。在夏令時中,從格林威治的偏移每年要調整兩次(也許更多);典型的做法是,春天調快時間,秋天再調回來。夏令時開始時,我們都需要手動調整家里的掛鐘時間。這些調整,在java.time包中,叫偏移過渡。春天時,跟本地時間相比,缺了一段時間;相反,秋天時,有的時間會出現兩次。

    ZonedDateTime在它的工廠方法和控制方法中處理了這些。例如,在夏令時切換的那天,增加一天會增加邏輯上的一天:可能多于24小時,也可能少于24小時。同樣的,方法atStartOfDay()之所以這樣命名,是因為你不能假定它的處理結果就一定是午夜零點,夏令時開始的那天,一天是從午夜1點開始的。

    下面是關于夏令時的最后一個小提示。如果你想證明,在夏令時結束那天的重疊時段,你有考慮過什么情況會發生,你可以用這兩個專門處理重疊時段的方法之一:

    1. zdt = zdt.withEarlierOffsetAtOverlap();
    2. zdt = zdt.withLaterOffsetAtOverlap();

    處于時間重疊時段時,使用這兩個方法之一,你可以得到調整之前或調整之后的時間。在其他情況下,這兩個方法是無效的。

    時間長度

    到目前為止,我們討論的時間/日期類以多種不同的方式表示時間線上的一個點。java.time還為時間長度額外提供了兩個值類型。

    Duration表示以秒和納秒為基準的時長。例如,“23.6秒”。

    Period表示以年、月、日衡量的時長。例如,“3年2個月零6天”。

    它們可以作為參數,傳給主要的時間/日期類的增加或減少時間的方法:

    1. Period sixMonths = Period.ofMonths(6);
    2. LocalDate date = LocalDate.now();
    3. LocalDate future = date.plus(sixMonths);

    解析和格式化

    java.time.format包是專門用來格式化輸出時間/日期的。這個包圍繞DateTimeFormatter類和它的輔助創建類DateTimeFormatterBuilder展開。

    靜態方法加上DateTimeFormatter中的常量,是最通用的創建格式化器的方式。包括:

    • 常用ISO格式常量,如ISO_LOCAL_DATE
    • 字母模式,如ofPattern(“dd/MM/uuuu”)
    • 本地化樣式,如ofLocalizedDate(FormatStyle.MEDIUM)

    很典型的,一旦有了格式化器,你可以把它傳遞給主要的時間/日期類的相關方法:

    1. DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/uuuu");
    2. LocalDate date = LocalDate.parse("24/06/2014", f);
    3. String str = date.format(f);

    這把你從格式化器自己的格式化和解析方法中隔離開來。

    如果你想控制格式化的語言環境,調用格式化器的withLocale(Locale)方法。相似的方式可以允許你控制格式化的歷法系統、時區、十進制數和解析度。

    如果你需要更多的控制權,查看DateTimeFormatterBuilder類吧,它允許你一步一步的構造更復雜的格式化器。它還提供大小寫不敏感的解析,松散的解析,字符填充和可選的格式。

    總結

    Java 8中的java.time是一個新的、復雜的時間/日期API。它把Joda-Time中的設計思想和實現推向了更高的層次,讓開發人員把java.util.DateCalendar拋在了身后。是時候重新享受時間/日期編程的樂趣了。

    本文譯自:Intuitive, Robust Date and Time Handling, Finally Comes to Java

    原創文章,轉載請注明: 轉載自Let’sCoding.cn
    本文鏈接地址: Java 8:健壯、易用的時間/日期API終于來臨

    posted on 2014-04-29 11:30 Rolandz 閱讀(11699) 評論(2)  編輯  收藏

    評論

    # re: Java 8:健壯、易用的時間/日期API終于來臨 2014-11-21 16:48 劉敏

    hello world   回復  更多評論   

    # re: Java 8:健壯、易用的時間/日期API終于來臨[未登錄] 2015-02-25 10:15 Michael

    很給力!  回復  更多評論   


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    導航

    統計

    留言簿(1)

    隨筆分類(12)

    隨筆檔案(19)

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产亚洲精品无码拍拍拍色欲| 免费一级毛片在线播放放视频| 亚洲五月综合缴情在线观看| 成人毛片免费视频| 99视频免费观看| a毛片在线免费观看| 国产成人亚洲综合a∨| 亚洲免费一级视频| 亚洲小视频在线观看| 中文字幕在亚洲第一在线| 成熟女人特级毛片www免费| **aaaaa毛片免费| 久热免费在线视频| 日本高清不卡aⅴ免费网站| 免费无码午夜福利片| 美女免费精品高清毛片在线视| 亚洲人成77777在线观看网| 亚洲另类古典武侠| 78成人精品电影在线播放日韩精品电影一区亚洲 | 国产日本亚洲一区二区三区| 亚洲电影一区二区三区| 亚洲精品成人无限看| 亚洲中文字幕无码爆乳AV| 亚洲精品综合久久| 亚洲免费日韩无码系列 | 亚洲欧美日韩综合久久久| 亚洲中文字幕久在线| 亚洲欧洲自拍拍偷综合| 亚洲欧洲综合在线| 亚洲大尺码专区影院| 亚洲国产成人精品无码区在线秒播| 亚洲白色白色永久观看| 亚洲成a人片7777| 亚洲午夜精品国产电影在线观看| 亚洲91精品麻豆国产系列在线| 亚洲国产日韩在线| 久久综合久久综合亚洲| 亚洲爆乳无码专区www| 偷自拍亚洲视频在线观看| 一级白嫩美女毛片免费| 两个人看的www免费高清|