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点(如果我没数错:))
其他回答
Sufiyan Ghori和micha的回答都很好地解释了关于弦模式的问题。然而,以防你正在使用ISO 8601,没有任何必要应用DateTimeFormatter,因为LocalDateTime已经为它准备好了:
将LocalDateTime转换为时区ISO 8601字符串
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); // You might use a different zone
String iso8601 = zdt.toString();
从ISO8601字符串转换回LocalDateTime
String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
获取所需格式的当前UTC时间
// Current the UTC time
OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC);
// Get LocalDateTime
LocalDateTime localDateTime = utc.toLocalDateTime();
System.out.println("*************" + localDateTime);
// Formatted UTC time
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
System.out.println(" formats as " + dTF.format(localDateTime));
// Get the UTC time for the current date
Date now = new Date();
LocalDateTime utcDateTimeForCurrentDateTime = Instant.ofEpochMilli(now.getTime()).atZone(ZoneId.of("UTC")).toLocalDateTime();
DateTimeFormatter dTF2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
System.out.println(" formats as " + dTF2.format(utcDateTimeForCurrentDateTime));
通用方法如下所示。它适用于:
yyyy-MM-dd HH: mm: ss。瑞士 yyyy-MM-dd HH: mm: ss。年代 yyyy-MM-dd HH: mm: ss yyyy-MM-dd HH: mm yyyy-MM-dd HH yyyy-MM-dd public static final String DATE_FORMAT_YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS"; stringtollocaldatetime (String s){ LocalDateTime返回。解析(年代,DateTimeFormatter.ofPattern (DATE_FORMAT_YYYY_MM_DD_HH_MM_SS_SSS。substring (0, s.length ()))); }
将带有日期和时间的字符串解析为特定的时间点(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点(如果我没数错:))
我发现像这样涵盖日期时间格式的多种变体非常棒:
final DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"))
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);