1. Overview
1.概述
In this tutorial, we’ll review the Java 8 DateTimeFormatter class and its formatting patterns. We’ll also discuss possible use cases for this class.
在本教程中,我们将回顾Java 8的DateTimeFormatter类及其格式化模式。我们还将讨论这个类的可能用例。
We can use DateTimeFormatter to uniformly format dates and times in an app with predefined or user-defined patterns.
我们可以使用DateTimeFormatter,以预定义或用户定义的模式统一格式化应用程序中的日期和时间。
2. DateTimeFormatter With Predefined Instances
2.具有预定义实例的DateTimeFormatter
DateTimeFormatter comes with multiple predefined date/time formats that follow ISO and RFC standards. For example, we can use the ISO_LOCAL_DATE instance to parse a date such as ‘2018-03-09′:
DateTimeFormatter带有多种预定义日期/时间格式,遵循ISO和RFC标准。例如,我们可以使用ISO_LOCAL_DATE实例来解析 “2018-03-09 “这样的日期。
DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.of(2018, 3, 9));
To parse a date with an offset, we can use ISO_OFFSET_DATE to get an output like ‘2018-03-09-03:00′:
要解析一个带有偏移量的日期,我们可以使用ISO_OFFSET_DATE来获得类似 “2018-03-09-03:00 “的输出。
DateTimeFormatter.ISO_OFFSET_DATE.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));
Most of the predefined instances of the DateTimeFormatter class are focused on the ISO-8601 standard. ISO-8601 is an international standard for date and time formatting.
DateTimeFormatter类的大多数预定义实例都集中在ISO-8601标准上。ISO-8601是一个关于日期和时间格式化的国际标准。
There is, however, one different predefined instance that parses RFC-1123, Requirement for Internet Hosts, published by the IETF:
然而,有一个不同的预定义实例,它解析了RFC-1123,即Internet主机的要求,由IETF发布的。
DateTimeFormatter.RFC_1123_DATE_TIME.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));
This snippet generates ‘Fri, 9 Mar 2018 00:00:00 -0300.‘
该片段生成了’Fri, 9 Mar 2018 00:00:00 -0300.‘
Sometimes we have to manipulate the date we receive as a String of a known format. For this, we can make use of the parse() method:
有时我们必须将收到的日期作为已知格式的String进行处理。为此,我们可以使用parse()方法。
LocalDate.from(DateTimeFormatter.ISO_LOCAL_DATE.parse("2018-03-09")).plusDays(3);
The result of this code snippet is a LocalDate representation for March 12th, 2018.
这个代码片段的结果是2018年3月12日的LocalDate表示。
3. DateTimeFormatter With FormatStyle
3.DateTimeFormatter与FormatStyle
Sometimes we may want to print dates in a human-readable way.
有时我们可能想以人类可读的方式来打印日期。
In such cases, we may use java.time.format.FormatStyle enum (FULL, LONG, MEDIUM, SHORT) values with our DateTimeFormatter:
在这种情况下,我们可以使用java.time.format.FormatStyle枚举(FULL, LONG, MEDIUM, SHORT)值与我们的DateTimeFormatter。
LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(anotherSummerDay));
The output of these different formatting styles of the same date are:
这些不同的格式化风格对同一日期的输出是。
Tuesday, August 23, 2016
August 23, 2016
Aug 23, 2016
8/23/16
We may also use predefined formatting styles for date and time. To use FormatStyle with time, we have to use the ZonedDateTime instance, otherwise, a DateTimeException will be thrown:
我们也可以为日期和时间使用预定义的格式化样式。要对时间使用FormatStyle,我们必须使用ZonedDateTime实例,否则,将抛出DateTimeException。
LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
LocalTime anotherTime = LocalTime.of(13, 12, 45);
ZonedDateTime zonedDateTime = ZonedDateTime.of(anotherSummerDay, anotherTime, ZoneId.of("Europe/Helsinki"));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.format(zonedDateTime));
Note that we used the ofLocalizedDateTime() method of DateTimeFormatter this time.
注意,这次我们使用了DateTimeFormatter的ofLocalizedDateTime()方法。
The output we get is:
我们得到的输出是。
Tuesday, August 23, 2016 1:12:45 PM EEST
August 23, 2016 1:12:45 PM EEST
Aug 23, 2016 1:12:45 PM
8/23/16 1:12 PM
We can also use FormatStyle to parse a date time String, converting it to ZonedDateTime, for example.
我们还可以使用FormatStyle来解析一个日期时间字符串,将其转换为ZonedDateTime,例如。
We can then use the parsed value to manipulate the date and time variable:
然后我们可以使用解析后的值来操作日期和时间变量。
ZonedDateTime dateTime = ZonedDateTime.from(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
.parse("Tuesday, August 23, 2016 1:12:45 PM EET"));
System.out.println(dateTime.plusHours(9));
The output of this snippet is “2016-08-23T22:12:45+03:00[Europe/Bucharest].” Notice that the time has changed to “22:12:45.”
这个片段的输出是 “2016-08-23T22:12:45+03:00[欧洲/布加勒斯特]”。注意,时间已经变成了 “22:12:45″。
4. DateTimeFormatter With Custom Formats
4.DateTimeFormatter与自定义格式
Predefined and built-in formatters and styles can cover a lot of situations. However, sometimes we need to format a date and time somewhat differently. This is when custom formatting patterns come into play.
预定义和内置的格式化器和样式可以涵盖很多情况。然而,有时我们需要以某种不同的方式来格式化一个日期和时间。这时,自定义格式化模式就会发挥作用。
4.1. DateTimeFormatter for Date
4.1.DateTimeFormatter for Date
Suppose we want to present a java.time.LocalDate object using a regular European format like 31.12.2018. To do this, we could call the factory method DateTimeFormatter.ofPattern(“dd.MM.yyyy”).
假设我们想使用常规的欧洲格式(如31.12.2018)来呈现一个java.time.LocalDate对象。为此,我们可以调用工厂方法DateTimeFormatter.ofPattern(“dd.MM.yyy”)。
This will create an appropriate DateTimeFormatter instance that we can use to format our date:
这将创建一个适当的DateTimeFormatter实例,我们可以用它来格式化我们的日期。
String europeanDatePattern = "dd.MM.yyyy";
DateTimeFormatter europeanDateFormatter = DateTimeFormatter.ofPattern(europeanDatePattern);
System.out.println(europeanDateFormatter.format(LocalDate.of(2016, 7, 31)));
The output of this code snippet will be “31.07.2016.”
这个代码片断的输出将是 “31.07.2016”。
There are many different pattern letters that we can use to create a format for dates that will suit our needs:
有许多不同的模式字母,我们可以用它们来创建一个适合我们需要的日期格式。
Symbol Meaning Presentation Examples
------ ------- ------------ -------
u year year 2004; 04
y year-of-era year 2004; 04
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
This is an extract of the official Java documentation to DateTimeFormatter class.
这是官方Java文档中对DateTimeFormatter类的一个摘录。
The number of letters in the pattern format is significant.
图案格式中的字母数量很重要。
If we use a two-letter pattern for the month, we’ll get a two-digit month representation. If the month number is less than 10, it will be padded with a zero. When we don’t need the mentioned padding with zeroes, we can use a one-letter pattern “M,” which will show January as “1.”
如果我们使用一个两个字母的模式来表示月份,我们将得到一个两位数的月份表示。如果月份的数字小于10,它将被填充一个零。当我们不需要提到的用零填充时,我们可以使用一个字母图案 “M”,这将显示一月为 “1”。
If we happen to use a four-letter pattern for the month, “MMMM,” then we’ll get a “full form” representation. In our example, it would be “July.” A five-letter pattern, “MMMMM,” will make the formatter use the “narrow form.” In our case, “J” would be used.
如果我们碰巧使用一个四字母模式的月份,”MMMM”,那么我们将得到一个 “完整形式 “的表示。在我们的例子中,它将是 “七月”。一个五个字母的模式,”MMMM”,将使格式化器使用 “狭义形式”。在我们的例子中,将使用 “J”。
Likewise, custom formatting patterns can also be used to parse a String that holds a date:
同样地,自定义格式化模式也可以用来解析一个持有日期的字符串。
DateTimeFormatter europeanDateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
System.out.println(LocalDate.from(europeanDateFormatter.parse("15.08.2014")).isLeapYear());
This code snippet checks whether the date “15.08.2014” is one of a leap year, which it isn’t.
这个代码片段检查日期”15.08.2014“是否是闰年的一个日期,它不是。
4.2. DateTimeFormatter for Time
4.2.DateTimeFormatter for Time
There are also pattern letters that can be used for time patterns:
还有一些可用于时间模式的模式字母。
Symbol Meaning Presentation Examples
------ ------- ------------ -------
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
n nano-of-second number 987654321
It’s quite simple to use DateTimeFormatter to format a java.time.LocalTime instance. Suppose we want to show time (hours, minutes and seconds) delimited with a colon:
使用DateTimeFormatter来格式化一个java.time.LocalTime实例是非常简单的。假设我们想显示以冒号为界的时间(小时、分钟和秒)。
String timeColonPattern = "HH:mm:ss";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));
This will generate output “17:35:50.”
这将产生输出”17:35:50.“
If we want to add milliseconds to the output, we should add “SSS” to the pattern:
如果我们想在输出中加入毫秒,我们应该在模式中加入 “SSS”。
String timeColonPattern = "HH:mm:ss SSS";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50).plus(329, ChronoUnit.MILLIS);
System.out.println(timeColonFormatter.format(colonTime));
This gives us the output “17:35:50 329.”
这给我们的输出”17:35:50 329.“
Note that “HH” is an hour-of-day pattern that generates the output of 0-23. When we want to show AM/PM, we should use lower-case “hh” for hours and add an “a” pattern:
请注意,”HH “是一天中的小时模式,产生0-23的输出。当我们想显示AM/PM时,我们应该用小写的 “hh “表示小时,并添加一个 “a “模式。
String timeColonPattern = "hh:mm:ss a";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));
The generated output is “05:35:50 PM.”
产生的输出是”05:35:50 PM.“
We may want to parse a time String with our custom formatter and check if it’s before noon:
我们可能想用我们的自定义格式化器解析一个时间String并检查它是否在中午之前。
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
System.out.println(LocalTime.from(timeFormatter.parse("12:25:30 AM")).isBefore(LocalTime.NOON));
The output of this last snippet shows that the given time is actually before noon.
这最后一个片段的输出显示,给定的时间实际上是在中午之前。
4.3. DateTimeFormatter for Time Zones
4.3.用于时区的DateTimeFormatter
Often we want to see a time zone of some specific date-time variable. If we use New York-based date-time (UTC -4), we can use “z” pattern-letter for time-zone name:
通常情况下,我们希望看到某些特定日期时间变量的时区。如果我们使用基于纽约的日期时间(UTC-4),我们可以使用 “z “模式字母作为时区名称。
String newYorkDateTimePattern = "dd.MM.yyyy HH:mm z";
DateTimeFormatter newYorkDateFormatter = DateTimeFormatter.ofPattern(newYorkDateTimePattern);
LocalDateTime summerDay = LocalDateTime.of(2016, 7, 31, 14, 15);
System.out.println(newYorkDateFormatter.format(ZonedDateTime.of(summerDay, ZoneId.of("UTC-4"))));
This will generate the output “31.07.2016 14:15 UTC-04:00.”
这将产生输出 “31.07.2016 14:15 UTC-04:00″。
We can parse date time strings with time zones just like we did earlier:
我们可以像先前那样解析带有时区的日期时间字符串。
DateTimeFormatter zonedFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm z");
System.out.println(ZonedDateTime.from(zonedFormatter.parse("31.07.2016 14:15 GMT+02:00")).getOffset().getTotalSeconds());
The output of this code is “7200” seconds, or 2 hours, as we’d expect.
这段代码的输出是 “7200 “秒,或2小时,正如我们所期望的那样。
We have to make sure that we provide a correct date time String to the parse() method. If we pass “31.07.2016 14:15” without a time zone to the zonedFormatter from the last code snippet, we’ll get a DateTimeParseException.
我们必须确保向parse()方法提供一个正确的日期时间String。如果我们把没有时区的 “31.07.2016 14:15 “传递给上一段代码中的zonedFormatter,我们会得到一个DateTimeParseException。
4.4. DateTimeFormatter for Instant
4.4.DateTimeFormatter for Instant
DateTimeFormatter comes with a great ISO instant formatter called ISO_INSTANT. As the name implies, this formatter provides a convenient way to format or parse an instant in UTC.
DateTimeFormatter附带了一个伟大的ISO即时格式,称为ISO_INSTANT。顾名思义,这个格式化器提供了一个方便的方法,以UTC格式化或解析一个瞬间。
According to the official documentation, an instant cannot be formatted as a date or time without specifying a time zone. So, attempting to use ISO_INSTANT on LocalDateTime or LocalDate objects will lead to an exception:
根据官方文档,如果不指定时区,瞬间不能被格式化为一个日期或时间。因此,试图在LocalDateTime或LocalDate对象上使用ISO_INSTANT将会导致一个异常。
@Test(expected = UnsupportedTemporalTypeException.class)
public void shouldExpectAnExceptionIfInputIsLocalDateTime() {
DateTimeFormatter.ISO_INSTANT.format(LocalDateTime.now());
}
However, we can use ISO_INSTANT to format a ZonedDateTime instance without any issue:
然而,我们可以使用ISO_INSTANT来格式化一个ZonedDateTime实例,没有任何问题。
@Test
public void shouldPrintFormattedZonedDateTime() {
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 02, 15, 0, 0, 0, 0, ZoneId.of("Europe/Paris"));
String formattedZonedDateTime = DateTimeFormatter.ISO_INSTANT.format(zonedDateTime);
Assert.assertEquals("2021-02-14T23:00:00Z", formattedZonedDateTime);
}
As we can see, we created our ZonedDateTime with “Europe/Paris” time zone. However, the formatted result is in UTC.
我们可以看到,我们创建的ZonedDateTime带有 “Europe/Paris “时区。但是格式化的结果是以UTC为准。
Similarly, when parsing to ZonedDateTime, we need to specify the time zone:
同样,当解析到ZonedDateTime时,我们需要指定时区。
@Test
public void shouldParseZonedDateTime() {
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2021-10-01T05:06:20Z", formatter);
Assert.assertEquals("2021-10-01T05:06:20Z", DateTimeFormatter.ISO_INSTANT.format(zonedDateTime));
}
Failing to do so will lead to DateTimeParseException:
如果不这样做,将导致DateTimeParseException。
@Test(expected = DateTimeParseException.class)
public void shouldExpectAnExceptionIfTimeZoneIsMissing() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2021-11-01T05:06:20Z", DateTimeFormatter.ISO_INSTANT);
}
It’s also worth mentioning that parsing requires specifying at least the seconds field. Otherwise, DateTimeParseException will be thrown.
还值得一提的是,解析时至少需要指定seconds字段。否则,DateTimeParseException将被抛出。
5. Conclusion
5.总结
In this article, we discussed how to use the DateTimeFormatter class to format dates and times. We also examined real-life example patterns that often arise when we work with date-time instances.
在这篇文章中,我们讨论了如何使用DateTimeFormatter 类来格式化日期和时间。我们还研究了现实生活中的例子模式,这些例子在我们处理日期-时间实例时经常出现。
We can find out more about Java 8’s Date/Time API in previous tutorials. As always, the source code used in this article is available over on GitHub.
我们可以在以前的教程中了解更多有关Java 8的Date/Time API的信息。一如既往,本文中使用的源代码可在GitHub上查阅。