Java 8增加了一个新的Java。时间API,用于处理日期和时间(JSR 310)。
我将日期和时间作为字符串(例如,“2014-04-08 12:30”)。如何从给定的字符串获得LocalDateTime实例?
在我完成与LocalDateTime对象的工作后:然后如何将LocalDateTime实例转换回与上面所示的相同格式的字符串?
Java 8增加了一个新的Java。时间API,用于处理日期和时间(JSR 310)。
我将日期和时间作为字符串(例如,“2014-04-08 12:30”)。如何从给定的字符串获得LocalDateTime实例?
在我完成与LocalDateTime对象的工作后:然后如何将LocalDateTime实例转换回与上面所示的相同格式的字符串?
当前回答
将带有日期和时间的字符串解析为特定的时间点(Java称之为“即时”)是相当复杂的。Java已经在几次迭代中解决了这个问题。最新的是java。Time和java.time。Chrono,涵盖了几乎所有的需求(除了时间膨胀:))。
然而,这种复杂性带来了很多困惑。
理解日期解析的关键是:
为什么Java有这么多解析日期的方法?
There are several systems to measure a time. For instance, the historical Japanese calendars were derived from the time ranges of the reign of the respective emperor or dynasty. Then there is, e.g., the Unix timestamp. Fortunately, the whole (business) world managed to use the same. Historically, the systems were being switched from/to, for various reasons. E.g., from the Julian calendar to the Gregorian calendar in 1582; so, the 'western' dates before that need to be treated differently. And, of course, the change did not happen at once. Because the calendar came from the headquarters of some religion and other parts of Europe believed in other deities, for instance Germany did not switch until the year 1700.
...以及为什么LocalDateTime, ZonedDateTime等如此复杂
There are time zones. A time zone is basically a "stripe"*[3] of the Earth's surface whose authorities follow the same rules of when does it have which time offset. This includes summer time rules. The time zones change over time for various areas, mostly based on who conquers whom. And one time zone's rules change over time as well. There are time offsets. That is not the same as time zones, because a time zone may be, e.g., "Prague", but that has summer time offset and winter time offset. If you get a timestamp with a time zone, the offset may vary, depending on what part of the year it is in. During the leap hour, the timestamp may mean two different times, so without additional information, it can't be reliably converted. Note: By timestamp I mean "a string that contains a date and/or time, optionally with a time zone and/or time offset." Several time zones may share the same time offset for certain periods. For instance, the GMT/UTC time zone is the same as the "London" time zone when the summer time offset is not in effect.
为了让它更复杂一点(但这对你的用例来说并不太重要):
The scientists observe Earth's dynamic, which changes over time; based on that, they add seconds at the end of individual years. (So 2040-12-31 24:00:00 may be a valid date-time.) This needs regular updates of the metadata that systems use to have the date conversions right. E.g., on Linux, you get regular updates to the Java packages including these new data. The updates do not always keep the previous behavior for both historical and future timestamps. So it may happen that parsing of the two timestamps around some time zone's change comparing them may give different results when running on different versions of the software. That also applies to comparing between the affected time zone and other time zone. Should this cause a bug in your software, consider using some timestamp that does not have such complicated rules, like Unix timestamp. Because of 7, for the future dates, we can't convert dates exactly with certainty. So, for instance, current parsing of 8524-02-17 12:00:00 may be off a couple of seconds from the future parsing.
JDK的api是随着当代需求而发展的
The early Java releases had just java.util.Date which had a bit naive approach, assuming that there's just the year, month, day, and time. This quickly did not suffice. Also, the needs of the databases were different, so quite early, java.sql.Date was introduced, with its own limitations. Because neither covered different calendars and time zones well, the Calendar API was introduced. This still did not cover the complexity of the time zones. And yet, the mix of the above APIs was really a pain to work with. So as Java developers started working on global web applications, libraries that targeted most use cases, like JodaTime, got quickly popular. JodaTime was the de facto standard for about a decade. But the JDK did not integrate with JodaTime, so working with it was a bit cumbersome. So, after a very long discussion on how to approach the matter, JSR-310 was created mainly based on JodaTime.
如何在Java的Java .time中处理它
确定要解析时间戳的类型
当您使用时间戳字符串时,您需要知道它包含什么信息。这是关键的一点。如果你没有做到这一点,你就会看到一些神秘的异常,如“无法创建即时”,“区域偏移量缺失”,“未知区域id”等等。
无法从TemporalAccessor获取OffsetDateTime 无法从TemporalAccessor获取ZonedDateTime 无法从TemporalAccessor获取LocalDateTime 无法从TemporalAccessor获取Instant
它包含日期和时间吗?
Does it have a time offset? A time offset is the +hh:mm part. Sometimes, +00:00 may be substituted with Z as 'Zulu time', UTC as Universal Time Coordinated, or GMT as Greenwich Mean Time. These also set the time zone. For these timestamps, you use OffsetDateTime. Does it have a time zone? For these timestamps, you use ZonedDateTime. Zone is specified either by name ("Prague", "Pacific Standard Time", "PST"), or "zone ID" ("America/Los_Angeles", "Europe/London"), represented by java.time.ZoneId. The list of time zones is compiled by a "TZ database", backed by ICAAN. According to ZoneId's javadoc, the zone id's can also somehow be specified as Z and offset. I'm not sure how this maps to real zones. If the timestamp, which only has a TZ, falls into a leap hour of time offset change, then it is ambiguous, and the interpretation is subject of ResolverStyle, see below. If it has neither, then the missing context is assumed or neglected. And the consumer has to decide. So it needs to be parsed as LocalDateTime and converted to OffsetDateTime by adding the missing info: You can assume that it is a UTC time. Add the UTC offset of 0 hours. You can assume that it is a time of the place where the conversion is happening. Convert it by adding the system's time zone. You can neglect and just use it as is. That is useful e.g. to compare or subtract two times (see Duration), or when you don't know and it doesn't really matter (e.g., local bus schedule).
部分时间信息
根据时间戳所包含的内容,您可以从中提取LocalDate、LocalTime、OffsetTime、MonthDay、Year或YearMonth。
如果你有完整的信息,你可以得到一个java.time.Instant。这也在内部用于OffsetDateTime和zoneeddatetime之间的转换。
弄清楚如何解析它
有一个关于DateTimeFormatter的详细文档,它既可以解析时间戳字符串,也可以格式化为字符串。
预先创建的DateTimeFormatters应该或多或少地涵盖所有标准时间戳格式。例如,ISO_INSTANT可以解析2011-12-03T10:15:30.123457Z。
如果您有一些特殊的格式,那么您可以创建自己的DateTimeFormatter(它也是一个解析器)。
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
我建议查看DateTimeFormatter的源代码,并获得如何使用DateTimeFormatterBuilder构建一个DateTimeFormatterBuilder的灵感。当你在那里的时候,还要看看ResolverStyle,它控制了解析器对于格式和模糊信息是LENIENT、SMART还是STRICT。
TemporalAccessor
现在,常见的错误是进入到TemporalAccessor的复杂性。这来自于开发人员使用SimpleDateFormatter.parse(String)的方式。是的,DateTimeFormatter.parse("…")给你提供了TemporalAccessor。
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
但是,有了上一节的知识,你可以方便地解析成你需要的类型:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
实际上也不需要DateTimeFormatter。要解析的类型有parse(String)方法。
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
关于TemporalAccessor,如果您对字符串中有什么信息有一个模糊的概念,并且希望在运行时决定,那么可以使用它。
我希望我能给你的灵魂带来一些理解:)
注意:有一个java的后端口。Java 6和7:ThreeTen-Backport。对于Android,它有ThreeTenABP。
不仅因为它们不是条纹,而且还有一些奇怪的极端。例如,一些邻近的太平洋岛屿有+14:00和-11:00时区。这意味着,在一个岛屿上,现在是5月1日下午3点,在另一个不远处的岛屿上,现在仍然是4月30日下午12点(如果我没数错:))
其他回答
让我们回答两个问题,示例字符串“2014-04-08 12:30”
如何从给定的字符串获得LocalDateTime实例?
import java.time.format.DateTimeFormatter
import java.time.LocalDateTime
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
// Parsing or conversion
final LocalDateTime dt = LocalDateTime.parse("2014-04-08 12:30", formatter)
Dt应该允许您执行所有与日期时间相关的操作
然后如何将LocalDateTime实例转换回具有相同格式的字符串?
final String date = dt.format(formatter)
将带有日期和时间的字符串解析为特定的时间点(Java称之为“即时”)是相当复杂的。Java已经在几次迭代中解决了这个问题。最新的是java。Time和java.time。Chrono,涵盖了几乎所有的需求(除了时间膨胀:))。
然而,这种复杂性带来了很多困惑。
理解日期解析的关键是:
为什么Java有这么多解析日期的方法?
There are several systems to measure a time. For instance, the historical Japanese calendars were derived from the time ranges of the reign of the respective emperor or dynasty. Then there is, e.g., the Unix timestamp. Fortunately, the whole (business) world managed to use the same. Historically, the systems were being switched from/to, for various reasons. E.g., from the Julian calendar to the Gregorian calendar in 1582; so, the 'western' dates before that need to be treated differently. And, of course, the change did not happen at once. Because the calendar came from the headquarters of some religion and other parts of Europe believed in other deities, for instance Germany did not switch until the year 1700.
...以及为什么LocalDateTime, ZonedDateTime等如此复杂
There are time zones. A time zone is basically a "stripe"*[3] of the Earth's surface whose authorities follow the same rules of when does it have which time offset. This includes summer time rules. The time zones change over time for various areas, mostly based on who conquers whom. And one time zone's rules change over time as well. There are time offsets. That is not the same as time zones, because a time zone may be, e.g., "Prague", but that has summer time offset and winter time offset. If you get a timestamp with a time zone, the offset may vary, depending on what part of the year it is in. During the leap hour, the timestamp may mean two different times, so without additional information, it can't be reliably converted. Note: By timestamp I mean "a string that contains a date and/or time, optionally with a time zone and/or time offset." Several time zones may share the same time offset for certain periods. For instance, the GMT/UTC time zone is the same as the "London" time zone when the summer time offset is not in effect.
为了让它更复杂一点(但这对你的用例来说并不太重要):
The scientists observe Earth's dynamic, which changes over time; based on that, they add seconds at the end of individual years. (So 2040-12-31 24:00:00 may be a valid date-time.) This needs regular updates of the metadata that systems use to have the date conversions right. E.g., on Linux, you get regular updates to the Java packages including these new data. The updates do not always keep the previous behavior for both historical and future timestamps. So it may happen that parsing of the two timestamps around some time zone's change comparing them may give different results when running on different versions of the software. That also applies to comparing between the affected time zone and other time zone. Should this cause a bug in your software, consider using some timestamp that does not have such complicated rules, like Unix timestamp. Because of 7, for the future dates, we can't convert dates exactly with certainty. So, for instance, current parsing of 8524-02-17 12:00:00 may be off a couple of seconds from the future parsing.
JDK的api是随着当代需求而发展的
The early Java releases had just java.util.Date which had a bit naive approach, assuming that there's just the year, month, day, and time. This quickly did not suffice. Also, the needs of the databases were different, so quite early, java.sql.Date was introduced, with its own limitations. Because neither covered different calendars and time zones well, the Calendar API was introduced. This still did not cover the complexity of the time zones. And yet, the mix of the above APIs was really a pain to work with. So as Java developers started working on global web applications, libraries that targeted most use cases, like JodaTime, got quickly popular. JodaTime was the de facto standard for about a decade. But the JDK did not integrate with JodaTime, so working with it was a bit cumbersome. So, after a very long discussion on how to approach the matter, JSR-310 was created mainly based on JodaTime.
如何在Java的Java .time中处理它
确定要解析时间戳的类型
当您使用时间戳字符串时,您需要知道它包含什么信息。这是关键的一点。如果你没有做到这一点,你就会看到一些神秘的异常,如“无法创建即时”,“区域偏移量缺失”,“未知区域id”等等。
无法从TemporalAccessor获取OffsetDateTime 无法从TemporalAccessor获取ZonedDateTime 无法从TemporalAccessor获取LocalDateTime 无法从TemporalAccessor获取Instant
它包含日期和时间吗?
Does it have a time offset? A time offset is the +hh:mm part. Sometimes, +00:00 may be substituted with Z as 'Zulu time', UTC as Universal Time Coordinated, or GMT as Greenwich Mean Time. These also set the time zone. For these timestamps, you use OffsetDateTime. Does it have a time zone? For these timestamps, you use ZonedDateTime. Zone is specified either by name ("Prague", "Pacific Standard Time", "PST"), or "zone ID" ("America/Los_Angeles", "Europe/London"), represented by java.time.ZoneId. The list of time zones is compiled by a "TZ database", backed by ICAAN. According to ZoneId's javadoc, the zone id's can also somehow be specified as Z and offset. I'm not sure how this maps to real zones. If the timestamp, which only has a TZ, falls into a leap hour of time offset change, then it is ambiguous, and the interpretation is subject of ResolverStyle, see below. If it has neither, then the missing context is assumed or neglected. And the consumer has to decide. So it needs to be parsed as LocalDateTime and converted to OffsetDateTime by adding the missing info: You can assume that it is a UTC time. Add the UTC offset of 0 hours. You can assume that it is a time of the place where the conversion is happening. Convert it by adding the system's time zone. You can neglect and just use it as is. That is useful e.g. to compare or subtract two times (see Duration), or when you don't know and it doesn't really matter (e.g., local bus schedule).
部分时间信息
根据时间戳所包含的内容,您可以从中提取LocalDate、LocalTime、OffsetTime、MonthDay、Year或YearMonth。
如果你有完整的信息,你可以得到一个java.time.Instant。这也在内部用于OffsetDateTime和zoneeddatetime之间的转换。
弄清楚如何解析它
有一个关于DateTimeFormatter的详细文档,它既可以解析时间戳字符串,也可以格式化为字符串。
预先创建的DateTimeFormatters应该或多或少地涵盖所有标准时间戳格式。例如,ISO_INSTANT可以解析2011-12-03T10:15:30.123457Z。
如果您有一些特殊的格式,那么您可以创建自己的DateTimeFormatter(它也是一个解析器)。
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
我建议查看DateTimeFormatter的源代码,并获得如何使用DateTimeFormatterBuilder构建一个DateTimeFormatterBuilder的灵感。当你在那里的时候,还要看看ResolverStyle,它控制了解析器对于格式和模糊信息是LENIENT、SMART还是STRICT。
TemporalAccessor
现在,常见的错误是进入到TemporalAccessor的复杂性。这来自于开发人员使用SimpleDateFormatter.parse(String)的方式。是的,DateTimeFormatter.parse("…")给你提供了TemporalAccessor。
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
但是,有了上一节的知识,你可以方便地解析成你需要的类型:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
实际上也不需要DateTimeFormatter。要解析的类型有parse(String)方法。
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
关于TemporalAccessor,如果您对字符串中有什么信息有一个模糊的概念,并且希望在运行时决定,那么可以使用它。
我希望我能给你的灵魂带来一些理解:)
注意:有一个java的后端口。Java 6和7:ThreeTen-Backport。对于Android,它有ThreeTenABP。
不仅因为它们不是条纹,而且还有一些奇怪的极端。例如,一些邻近的太平洋岛屿有+14:00和-11:00时区。这意味着,在一个岛屿上,现在是5月1日下午3点,在另一个不远处的岛屿上,现在仍然是4月30日下午12点(如果我没数错:))
解析日期和时间
要从字符串创建LocalDateTime对象,可以使用静态的LocalDateTime.parse()方法。它接受一个字符串和一个DateTimeFormatter作为参数。DateTimeFormatter用于指定日期/时间模式。
String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
格式化日期和时间
要从LocalDateTime对象中创建格式化字符串,可以使用format()方法。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"
注意,在DateTimeFormatter中有一些常用的日期/时间格式预定义为常量。例如:使用DateTimeFormatter。从上面格式化LocalDateTime实例的ISO_DATE_TIME将导致字符串“1986-04-08T12:30:00”。
parse()和format()方法可用于所有与日期/时间相关的对象(例如LocalDate或ZonedDateTime)
你也可以使用LocalDate.parse()或LocalDateTime.parse()在不提供模式的字符串上,如果字符串是ISO 8601格式。
例如,
String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);
String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);
输出,
Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30
只有在必须处理其他日期模式时才使用DateTimeFormatter。
例如,在下面的例子中,dd MMM uuuu表示月份的日期(两位数字),月份名称的三个字母(Jan, Feb, Mar,…)和一个四位数的年份:
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);
输出
04 Aug 2015 parses to 2015-08-04
还记得DateTimeFormatter对象是双向的;它既可以解析输入,也可以格式化输出。
String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));
输出
2015-08-04 formats as 04 Aug 2015
(请参阅格式化和解析DateFormatter的模式的完整列表。)
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
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
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
关于LocalDateTime还有一点需要注意。解析的一个缺点是,您不能将它与只有日期格式化程序字符的自定义格式化程序一起使用,例如uuuuMMdd。在本例中,您应该使用LocalDate。解析。例如:
String s = "20210223";
// ok
LocalDate.parse(s, DateTimeFormatter.ofPattern("uuuuMMdd"));
// java.time.format.DateTimeParseException
LocalDateTime.parse(s, DateTimeFormatter.ofPattern("uuuuMMdd"));