Java 8日期和时间
如今,一些應用程序仍在使用java.util.Date和java.util.Calendar API,包括使我們的生活更輕松地使用這些類型的庫,例如JodaTime。 但是,Java 8引入了新的API來處理日期和時間,這使我們可以對日期和時間表示進行更精細的控制,為我們提供不可變的datetime對象,更流暢的API以及大多數情況下的性能提升,而無需使用其他庫。 讓我們看一下基礎知識。
LocalDate / LocalTime / LocalDateTime
讓我們開始與最相關的新API的java.util.Date : LocalDate ,日期的API,表示沒有時間的日期; LocalTime ,不帶日期的時間表示形式; 和LocalDateTime ,這是前兩個的組合。 所有這些類型都表示一個區域的本地日期和/或時間,但是,就像java.util.Date一樣,它們包含有關表示區域的零信息,僅表示當前日期和時間。時區。
首先,這些API支持簡單的實例化:
LocalDate date = LocalDate.of(2018,2,13); // Uses DateTimeformatter.ISO_LOCAL_DATE for which the format is: yyyy-MM-dd LocalDate date = LocalDate.parse("2018-02-13");LocalTime time = LocalTime.of(6,30); // Uses DateTimeFormatter.ISO_LOCAL_TIME for which the format is: HH:mm[:ss[.SSSSSSSSS]] // this means that both seconds and nanoseconds may optionally be present. LocalTime time = LocalTime.parse("06:30");LocalDateTime dateTime = LocalDateTime.of(2018,2,13,6,30); // Uses DateTimeFormatter.ISO_LOCAL_DATE_TIME for which the format is the // combination of the ISO date and time format, joined by 'T': yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]] LocalDateTime dateTime = LocalDateTime.parse("2018-02-13T06:30");在它們之間進行轉換很容易:
// LocalDate to LocalDateTime LocalDateTime dateTime = LocalDate.parse("2018-02-13").atTime(LocalTime.parse("06:30"));// LocalTime to LocalDateTime LocalDateTime dateTime = LocalTime.parse("06:30").atDate(LocalDate.parse("2018-02-13"));// LocalDateTime to LocalDate/LocalTime LocalDate date = LocalDateTime.parse("2018-02-13T06:30").toLocalDate(); LocalTime time = LocalDateTime.parse("2018-02-13T06:30").toLocalTime();除此之外,使用`plus`和`minus`方法以及一些實用程序功能,對我們的日期和時間表示進行操作非常容易。
LocalDate date = LocalDate.parse("2018-02-13").plusDays(5); LocalDate date = LocalDate.parse("2018-02-13").plus(3, ChronoUnit.MONTHS);LocalTime time = LocalTime.parse("06:30").minusMinutes(30); LocalTime time = LocalTime.parse("06:30").minus(500, ChronoUnit.MILLIS);LocalDateTime dateTime = LocalDateTime.parse("2018-02-13T06:30").plus(Duration.ofHours(2));// using TemporalAdjusters, which implements a few useful cases: LocalDate date = LocalDate.parse("2018-02-13").with(TemporalAdjusters.lastDayOfMonth());現在我們如何從java.util.Date遷移到LocalDateTime及其變體? 好吧,這很簡單:我們可以將Date類型轉換為Instant類型,該類型表示從1970年1月1日開始的時間,然后我們可以使用Instant和當前區域實例化LocalDateTime 。
LocalDateTime dateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());要轉換回日期,我們可以簡單地使用Java 8時間類型表示的Instant。 但是要注意的一件事是,盡管LocalDate , LocalTime和LocalDateTime不包含任何Zone或Offset信息,但它們確實表示特定區域中的本地日期和/或時間,因此它們確實保留了當前的偏移量。在那個地區。 因此,我們需要提供一個偏移量以將特定類型正確轉換為Instant。
// represents Wed Feb 28 23:24:43 CET 2018 Date now = new Date();// represents 2018-02-28T23:24:43.106 LocalDateTime dateTime = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());// represent Wed Feb 28 23:24:43 CET 2018 Date date = Date.from(dateTime.toInstant(ZoneOffset.ofHours(1))); Date date = Date.from(dateTime.toInstant(ZoneId.systemDefault().getRules().getOffset(dateTime)));時間差異–持續時間和期間
您已經注意到,在以上示例之一中,我們使用了Duration對象。 Duration和Period是兩個日期之間時間的兩種表示形式,前者表示以秒和納秒為單位的時間差,后者以天,月和年表示。
什么時候應該使用這些? Period ,當你需要知道兩者之間的時間差LocalDate陳述:
Period period = Period.between(LocalDate.parse("2018-01-18"), LocalDate.parse("2018-02-14"));您在尋找具有時間信息的表示形式之間的差異時的Duration時間:
Duration duration = Duration.between(LocalDateTime.parse("2018-01-18T06:30"), LocalDateTime.parse("2018-02-14T22:58"));使用toString()輸出Period或Duration ,將基于ISO-8601標準使用特殊格式。 周期使用的模式是PnYnMnD,其中n定義周期內存在的年,月或日的數量。 這意味著P1Y2M3D定義為1年2個月3天。 。 模式中的“ P”是時段指示符,它告訴我們以下格式表示一個時段。 使用該模式,我們還可以使用parse()方法基于字符串創建句點。
// represents a period of 27 days Period period = Period.parse("P27D");使用Durations ,由于Java 8不使用相同的模式,因此我們稍微偏離了ISO-8601標準。 ISO-8601定義的模式是PnYnMnDTnHnMn.nS。 這基本上是“ Period模式,帶有時間表示形式。 在模式中,T是時間指示符,因此后面的部分定義了以小時,分鐘和秒為單位的持續時間。
Java的8個使用兩種特定模式的Duration ,解析字符串,一當就是PnDTnHnMn.nS Duration ,以及調用時PTnHnMn.nS toString()上的一個方法Duration實例。
最后但并非最不重要的一點是,我們還可以通過使用類型上的相應方法來檢索時間段或持續時間的各個部分。 但是,重要的是要知道各種日期時間類型也通過使用ChronoUnit枚舉類型來支持此功能。 讓我們看一些例子:
// represents PT664H28M Duration duration = Duration.between(LocalDateTime.parse("2018-01-18T06:30"), LocalDateTime.parse("2018-02-14T22:58"));// returns 664 long hours = duration.toHours();// returns 664 long hours = LocalDateTime.parse("2018-01-18T06:30").until(LocalDateTime.parse("2018-02-14T22:58"), ChronoUnit.HOURS);使用區域和偏移量– ZonedDateTime和OffsetDateTime
到目前為止,我們已經展示了新的日期API如何使一些事情變得容易一些。 但是,真正與眾不同的是在時區上下文中輕松使用日期和時間的能力。 Java 8為我們提供了ZonedDateTime和OffsetDateTime ,第一個是LocalDateTime其中包含特定區域(例如,歐洲/巴黎)的信息,第二個是帶有偏移量的LocalDateTime 。 有什么不同? OffsetDateTime使用UTC /格林威治標準時間和指定的日期之間的固定時差,而ZonedDateTime指定表示時間的區域,并將考慮夏令時。
轉換為以下兩種類型都很容易:
OffsetDateTime offsetDateTime = LocalDateTime.parse("2018-02-14T06:30").atOffset(ZoneOffset.ofHours(2)); // Uses DateTimeFormatter.ISO_OFFSET_DATE_TIME for which the default format is // ISO_LOCAL_DATE_TIME followed by the offset ("+HH:mm:ss"). OffsetDateTime offsetDateTime = OffsetDateTime.parse("2018-02-14T06:30+06:00");ZonedDateTime zonedDateTime = LocalDateTime.parse("2018-02-14T06:30").atZone(ZoneId.of("Europe/Paris")); // Uses DateTimeFormatter.ISO_ZONED_DATE_TIME for which the default format is // ISO_OFFSET_DATE_TIME followed by the the ZoneId in square brackets. ZonedDateTime zonedDateTime = ZonedDateTime.parse("2018-02-14T06:30+08:00[Asia/Macau]"); // note that the offset does not matter in this case. // The following example will also return an offset of +08:00 ZonedDateTime zonedDateTime = ZonedDateTime.parse("2018-02-14T06:30+06:00[Asia/Macau]");在它們之間進行切換時,必須記住,從ZonedDateTime轉換為OffsetDateTime將考慮夏時制,而在另一個方向上從OffsetDateTime至ZonedDateTime意味著您將沒有有關區域區域的信息,也不會對夏時制應用任何規則。 這是因為偏移量未定義任何時區規則,也未綁定到特定區域。
ZonedDateTime winter = LocalDateTime.parse("2018-01-14T06:30").atZone(ZoneId.of("Europe/Paris")); ZonedDateTime summer = LocalDateTime.parse("2018-08-14T06:30").atZone(ZoneId.of("Europe/Paris"));// offset will be +01:00 OffsetDateTime offsetDateTime = winter.toOffsetDateTime(); // offset will be +02:00 OffsetDateTime offsetDateTime = summer.toOffsetDateTime();OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime();OffsetDateTime offsetDateTime = LocalDateTime.parse("2018-02-14T06:30").atOffset(ZoneOffset.ofHours(5)); ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();現在,如果我們想知道特定時區或偏移時間在我們自己的時區中怎么辦? 嗯,為此還定義了一些方便的功能!
// timeInMacau represents 2018-02-14T13:30+08:00[Asia/Macau] ZonedDateTime timeInMacau = LocalDateTime.parse( "2018-02-14T13:30" ).atZone( ZoneId.of( "Asia/Macau" ) ); // timeInParis represents 2018-02-14T06:30+01:00[Europe/Paris] ZonedDateTime timeInParis = timeInMacau.withZoneSameInstant( ZoneId.of( "Europe/Paris" ) );OffsetDateTime offsetInMacau = LocalDateTime.parse( "2018-02-14T13:30" ).atOffset( ZoneOffset.ofHours( 8 ) ); OffsetDateTime offsetInParis = offsetInMacau.withOffsetSameInstant( ZoneOffset.ofHours( 1 ) );如果我們不得不一直在這些類型之間手動轉換以獲取所需的類型,那將是一件麻煩事。 這就是Spring框架為我們提供幫助的地方。 Spring為我們提供了很多現成的日期時間轉換器,這些日期時間轉換器已在ConversionRegistry中注冊,可以在org.springframework.format.datetime.standard.DateTimeConverters類中找到。
使用這些轉換器時,重要的是要知道它不會在區域或偏移之間轉換時間。 該ZonedDateTimeToLocalDateTimeConverter ,例如,將返回LocalDateTime因為它是在,而不是指定的區域LocalDateTime ,它會在你的應用程序的區域代表。
ZonedDateTime zonedDateTime = LocalDateTime.parse("2018-01-14T06:30").atZone(ZoneId.of("Asia/Macau")); // will represent 2018-01-14T06:30, regardless of the region your application has specified LocalDateTime localDateTime = conversionService.convert(zonedDateTime, LocalDateTime.class);最后但并非最不重要的一點是,您可以查詢ZoneId.getAvailableZoneIds()以找到所有可用的時區,或使用地圖ZoneId.SHORT_IDS ,其中包含一些時區的縮寫版本,例如EST,CST等。
格式化–使用
當然,世界各地都使用不同的格式來指定時間。 一個應用程序可能使用MM-dd-yyyy,而另一個應用程序使用dd / MM / yyyy。 一些應用程序希望消除所有混淆,并用yyyy-MM-dd表示其日期。 使用java.util.Date ,我們將快速轉向使用多個格式化程序。 但是, DateTimeFormatter類為我們提供了可選的模式,因此我們可以將單個格式化程序用于多種格式! 讓我們來看一些示例。
// Let’s say we want to convert all of patterns mentioned above. // 09-23-2018, 23/09/2018 and 2018-09-23 should all convert to the same LocalDate. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy-MM-dd][dd/MM/yyyy][MM-dd-yyyy]"); LocalDate.parse("09-23-2018", formatter); LocalDate.parse("23/09/2018", formatter); LocalDate.parse("2018-09-23", formatter);模式中的方括號定義了模式中的可選部分。 通過使我們的各種格式成為可選,與字符串匹配的第一個模式將用于轉換我們的日期表示形式。 當您使用多種模式時,這可能很難理解,因此讓我們看一下使用構建器模式創建DateTimeFormatter方法。
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendOptional( DateTimeFormatter.ofPattern( "yyyy-MM-dd" ) ).optionalStart().appendPattern( "dd/MM/yyyy" ).optionalEnd().optionalStart().appendPattern( "MM-dd-yyyy" ).optionalEnd().toFormatter();這些是包含多個模式的基礎,但是如果我們的模式僅稍有不同怎么辦? 讓我們看一下yyyy-MM-dd和yyyy-MMM-dd。
// 2018-09-23 and 2018-Sep-23 should convert to the same LocalDate. // Using the ofPattern example we’ve used above will work: DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy-MM-dd][yyyy-MMM-dd]" ); LocalDate.parse( "2018-09-23", formatter ); LocalDate.parse( "2018-Sep-23", formatter );// Using the ofPattern example where we reuse the common part of the pattern DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyy-[MM-dd][MMM-dd]" ); LocalDate.parse( "2018-09-23", formatter ); LocalDate.parse( "2018-Sep-23", formatter );但是,在轉換為字符串時,不應使用支持多種格式的格式化程序,因為當我們使用格式化程序將日期格式化為字符串表示形式時,它還將使用可選模式。
LocalDate date = LocalDate.parse("2018-09-23"); // will result in 2018-09-232018-Sep-23 date.format(DateTimeFormatter.ofPattern("[yyyy-MM-dd][yyyy-MMM-dd]" )); // will result in 2018-09-23Sep-23 date.format(DateTimeFormatter.ofPattern( "yyyy-[MM-dd][MMM-dd]" ));由于我們處于21世紀,因此顯然我們必須考慮全球化,并且我們希望為用戶提供本地化日期。 為確保您的DateTimeFormatter返回特定的語言環境,您只需執行以下操作:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "EEEE, MMM dd, yyyy" ).withLocale(Locale.UK);DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MMM-dd" ).toFormatter(Locale.UK);要查找可用的語言環境,可以使用Locale.getAvailableLocales() 。
現在可能是您收到的日期格式比您使用的類型包含更多信息。 一旦提供的日期表示形式與該模式不一致,則DateTimeFormatter將引發異常。 讓我們仔細研究這個問題以及如何解決它。
// The issue: this will throw an exception. LocalDate date = LocalDate.parse("2018-02-15T13:45"); // We provide a DateTimeFormatter that can parse the given date representation. // The result will be a LocalDate holding 2018-02-15. LocalDate date = LocalDate.parse("2018-02-15T13:45", DateTimeFormatter.ISO_LOCAL_DATE_TIME);讓我們創建一個可以處理ISO日期,時間和日期時間模式的格式化程序。
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendOptional( DateTimeFormatter.ISO_LOCAL_DATE ).optionalStart().appendLiteral( "T" ).optionalEnd().appendOptional( DateTimeFormatter.ISO_LOCAL_TIME ).toFormatter();現在,我們可以完美地執行以下所有操作:
// results in 2018-03-16 LocalDate date = LocalDate.parse( "2018-03-16T06:30", formatter ); LocalDate date = LocalDate.parse( "2018-03-16", formatter ); // results in 06:30 LocalTime time = LocalTime.parse( "2018-03-16T06:30", formatter ); LocalTime time = LocalTime.parse( "06:30", formatter ); LocalDateTime localDateTime = LocalDateTime.parse( "2018-03-16T06:30", formatter );現在,下一期是哪里來的? 如果您嘗試解析LocalDateTime的日期模式怎么辦? 如果您希望使用LocalTime并得到日期表示,反之亦然怎么辦?
// will throw an exception LocalDateTime localDateTime = LocalDateTime.parse("2018-03-16", formatter); LocalDate localDate = LocalDate.parse("06:30", formatter);對于這后兩種情況,沒有一個正確的解決方案,但這取決于您的要求,或者這些日期和時間代表或可能代表什么。 使用TemporalQuery可以找到其神奇之處,您可以使用它為模式的一部分創建默認值。
如果我們從LocalDateTime開始,而您只想要LocalDate或LocalTime ,則將收到LocalDateTime的相應部分。 要創建LocalDateTime ,我們需要其持有的日期和時間的默認值。 假設如果您不提供有關日期的信息,我們將返回今天的日期,并且如果您不提供時間,則將假設您的意思是一天的開始。
由于我們將返回LocalDateTime ,因此不會將其解析為LocalDate或LocalTime ,因此讓我們使用ConversionService來獲取正確的類型。
TemporalQuery<TemporalAccessor> myCustomQuery = new MyCustomTemporalQuery(); // results in 2018-03-16 LocalDateTime localDateTime = conversionService.convert( formatter.parse( "2018-03-16", myCustomQuery ), LocalDateTime.class ); // results in 00:00 LocalTime localTime = conversionService.convert( formatter.parse( "2018-03-16", myCustomQuery ), LocalTime.class );class MyCustomTemporalQuery implements TemporalQuery<TemporalAccessor> {@Overridepublic TemporalAccessor queryFrom( TemporalAccessor temporal ) {LocalDate date = temporal.isSupported( ChronoField.EPOCH_DAY )? LocalDate.ofEpochDay( temporal.getLong( ChronoField.EPOCH_DAY ) ) : LocalDate.now();LocalTime time = temporal.isSupported( ChronoField.NANO_OF_DAY )? LocalTime.ofNanoOfDay( temporal.getLong( ChronoField.NANO_OF_DAY ) ) : LocalTime.MIN;return LocalDateTime.of( date, time );} }使用TemporalQuery ,我們可以檢查存在的信息并為缺少的任何信息提供默認值,從而使我們能夠使用應用程序中有意義的邏輯輕松地轉換為所需的類型。
要了解如何撰寫有效的時間模式,請查看DateTimeFormatter文檔 。
結論
大多數新功能需要一些時間來理解和習慣,而Java 8 Date / Time API也不例外。 新的API使我們可以更好地訪問必要的正確格式,以及使用日期時間操作的更加標準化和可讀性強的方式。 使用這些技巧,我們幾乎可以涵蓋所有用例。
翻譯自: https://www.javacodegeeks.com/2018/03/java-8-date-and-time.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Java 8日期和时间的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓机代码从哪里输入(安卓机代码)
- 下一篇: linux SD卡 格式化(linux