如何使用LocalDateTime解析/格式化日期? (Java 8)

本文翻译自:How to parse/format dates with LocalDateTime? (Java 8)

Java 8 added a new java.time API for working with dates and times ( JSR 310 ). Java 8添加了新的java.time API,用于处理日期和时间( JSR 310 )。

I have date and time as string (eg "2014-04-08 12:30" ). 我将日期和时间作为字符串(例如"2014-04-08 12:30" )。 How can I obtain a LocalDateTime instance from the given string? 如何从给定的字符串中获取LocalDateTime实例?

After I finished working with the LocalDateTime object: How can I then convert the LocalDateTime instance back to a string with the same format as shown above? 在完成LocalDateTime对象的工作后:如何将LocalDateTime实例转换回具有上述相同格式的字符串?


#1楼

参考:https://stackoom.com/question/1WFfS/如何使用LocalDateTime解析-格式化日期-Java


#2楼

Parsing date and time 解析日期和时间

To create a LocalDateTime object from a string you can use the static LocalDateTime.parse() method. 要从字符串创建LocalDateTime对象,可以使用静态LocalDateTime.parse()方法。 It takes a string and a DateTimeFormatter as parameter. 它需要一个字符串和一个DateTimeFormatter作为参数。 The DateTimeFormatter is used to specify the date/time pattern. DateTimeFormatter用于指定日期/时间模式。

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

Formatting date and time 格式化日期和时间

To create a formatted string out a LocalDateTime object you can use the format() method. 要在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"

Note that there are some commonly used date/time formats predefined as constants in DateTimeFormatter . 请注意,在DateTimeFormatter中有一些预定义为常量的常用日期/时间格式。 For example: Using DateTimeFormatter.ISO_DATE_TIME to format the LocalDateTime instance from above would result in the string "1986-04-08T12:30:00" . 例如:使用DateTimeFormatter.ISO_DATE_TIME从上面格式化LocalDateTime实例,将导致字符串"1986-04-08T12:30:00"

The parse() and format() methods are available for all date/time related objects (eg LocalDate or ZonedDateTime ) parse()format()方法可用于所有与日期/时间相关的对象(例如LocalDateZonedDateTime


#3楼

You can also use LocalDate.parse() or LocalDateTime.parse() on a String without providing it with a pattern, if the String is in ISO-8601 format . 您还可以使用LocalDate.parse()LocalDateTime.parse()String没有为它提供一个模式,如果String是ISO-8601格式 。

for example, 例如,

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);

Output , 输出

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

and use DateTimeFormatter only if you have to deal with other date patterns, For example, dd MMM uuuu represents the day of the month (two digits), three letters of the name of the month (Jan, Feb, Mar,...), and a four-digit year: 并仅在必须处理其他日期模式时使用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);

Output 输出量

04 Aug 2015 parses to 2015-08-04

also remember that the DateTimeFormatter object is bidirectional; 还记得DateTimeFormatter对象是双向的; it can both parse input and format output. 它可以解析输入和格式化输出。

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));

Output 输出量

2015-08-04 formats as 04 Aug 2015

(see complete list of Patterns for Formatting and Parsing DateFormatter ) (请参阅用于格式化和解析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

#4楼

Both answers above explain very well the question regarding string patterns. 上面的两个答案都很好地说明了有关字符串模式的问题。 However, just in case you are working with ISO 8601 there is no need to apply DateTimeFormatter since LocalDateTime is already prepared for it: 但是,如果您正在使用ISO 8601 ,则无需应用DateTimeFormatter因为已经为它准备了LocalDateTime:

Convert LocalDateTime to Time Zone ISO8601 String 将LocalDateTime转换为时区ISO8601字符串

LocalDateTime ldt = LocalDateTime.now(); 
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); //you might use a different zone
String iso8601 = zdt.toString();

Convert from ISO8601 String back to a LocalDateTime 从ISO8601字符串转换回LocalDateTime

String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();

#5楼

Parsing a string with date and time into a particular point in time (Java calls it an " Instant ") is quite complicated. 将带有日期和时间的字符串解析为特定的时间点(Java将其称为“ Instant ”)非常复杂。 Java has been tackling this in several iterations. Java已经通过多次迭代解决了这个问题。 The latest one, java.time and java.time.chrono , covers almost all needs (except Time Dilation :) ). 最新的java.timejava.time.chrono满足了几乎所有需求( Time java.time.chrono :除外)。

However, that complexity brings a lot of confusion. 但是,这种复杂性带来了很多混乱。

The key to understand date parsing is: 了解日期解析的关键是:

Why does Java have so many ways to parse a date 为什么Java有这么多种方式解析日期

  1. 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 eg UNIX timestamp. 然后是例如UNIX时间戳。 Fortunately, the whole (business) world managed to use the same. 幸运的是,整个(商业)世界都使用了相同的东西。
  2. Historically, the systems were being switched from/to, for various reasons . 从历史上看,由于各种原因 ,系统被切换为切换。 Eg from Julian calendar to Gregorian calendar in 1582. So 'western' dates before that need to be treated differently. 例如从1582年的朱利安历法到公历。因此,在此之前的“西方”历法需要区别对待。
  3. And of course the change did not happen at once. 当然,更改不会立即发生。 Because the calendar came from the headquarteds of some religion and other parts of Europe believed in other dieties, for instance Germany did not switch until the year 1700. 因为日历来自某些宗教的起源,而欧洲其他地区也相信其他饮食,例如德国直到1700年才转换。

...and why is the LocalDateTime , ZonedDateTime et al. ...以及为什么是LocalDateTimeZonedDateTime等。 so complicated 太复杂了

  1. There are time zones . 有时区 。 A time zone is basically a "stripe" *[1] of the Earth's surface whose authorities follow the same rules of when does it have which time offset. 时区基本上是地球表面的“条带” * [1] ,其权限遵循何时具有哪个时间偏移的相同规则。 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. 一个时区的规则也会随着时间而改变 。

  2. There are time offsets. 有时间偏移。 That is not the same as time zones, because a time zone may be eg "Prague", but that has summer time offset and winter time offset. 这与时区不同,因为时区可能是“ Prague”(布拉格),但是具有夏令时和冬令时。
    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 2 different times, so without additional information, it can't be reliably converted. 如果获得带时区的时间戳,则偏移量可能会有所不同,具体取决于所在的年份。在the年期间,时间戳可能表示2个不同的时间,因此如果没有其他信息,它就不可能可靠转换。
    Note: By timestamp I mean "a string that contains a date and/or time, optionally with a time zone and/or time offset." 注意:通过时间戳记,我的意思是“包含日期和/或时间,以及时区和/或时间偏移的字符串。”

  3. Several time zones may share the same time offset for certain periods. 某些时区在某些时段可能共享相同的时间偏移。 For instance, GMT/UTC time zone is the same as "London" time zone when the summer time offset is not in effect. 例如,当夏令时未生效时,GMT / UTC时区与“伦敦”时区相同。

To make it a bit more complicated (but that's not too important for your use case) : 使它更加复杂(但这对您的用例而言并不重要):

  1. 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. (因此2040-12-31 24:00:00可能是有效的日期时间。)这需要定期更新元数据,系统使用这些元数据来正确地进行日期转换。 Eg on Linux, you get regular updates to the Java packages including these new data. 例如,在Linux上,您会定期更新Java软件包,包括这些新数据。
  2. 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 . 如果这会导致软件中的错误,请考虑使用一些没有复杂规则的时间戳 ,例如UNIX timestamp 。

  3. Because of 7, for the future dates, we can't convert dates exactly with certainty. 由于7,对于将来的日期,我们无法完全确定地转换日期。 So, for instance, current parsing of 8524-02-17 12:00:00 may be off a couple of seconds from the future parsing. 因此,例如,当前的8524-02-17 12:00:00解析可能与将来的解析8524-02-17 12:00:00几秒钟。

JDK's APIs for this evolved with the contemporary needs 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. Java的早期发行版中只有java.util.Date ,它采用了一些天真的方法,假设只有年,月,日和时间。 This quickly did not suffice. 很快这还不够。
  • Also, the needs of the databases were different, so quite early, java.sql.Date was introduced, with it's own limitations. 而且,数据库的需求是不同的,因此很早就引入了java.sql.Date ,但它有其自身的局限性。
  • Because neither covered different calendars and time zones well, the Calendar API was introduced. 由于两者都无法很好地涵盖不同的日历和时区,因此引入了Calendar API。
  • 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. 但是,上述API的混合使用确实很痛苦。 So as Java developers started working on global web applications, libraries that targeted most use cases, like JodaTime, got quickly popular. 因此,随着Java开发人员开始研究全球Web应用程序,针对大多数用例的库(如JodaTime)迅速受到欢迎。 JodaTime was the de-facto standard for about a decade. JodaTime是事实上的标准,已有大约十年的历史。
  • But the JDK did not integrate with JodaTime, so working with it was a bit cumbersome. 但是JDK没有与JodaTime集成,因此使用它有点麻烦。 So, after a very long discussion on how to approach the matter, JSR-310 was created mainly based on JodaTime . 因此,在就如何解决此问题进行了很长时间的讨论之后, 主要基于JodaTime创建了JSR-310 。

How to deal with it in Java's java.time 如何在Java的java.time处理它

Determine what type to parse a timestamp to 确定解析时间戳的类型

When you are consuming a timestamp string, you need to know what information it contains. 使用时间戳字符串时,需要知道它包含哪些信息。 This is the crucial point. 这是关键点。 If you don't get this right, you end up with a cryptic exceptions like "Can't create Instant" or "Zone offset missing" or "unknown zone id" etc. 如果您做不到这一点,您将遇到一个神秘的异常,例如“无法创建即时”或“缺少区域偏移”或“未知区域ID”等。

  • Unable to obtain OffsetDateTime from TemporalAccessor 无法从TemporalAccessor获取OffsetDateTime
  • Unable to obtain ZonedDateTime from TemporalAccessor 无法从TemporalAccessor获取ZonedDateTime
  • Unable to obtain LocalDateTime from TemporalAccessor 无法从TemporalAccessor获取LocalDateTime
  • Unable to obtain Instant from TemporalAccessor 无法从TemporalAccessor获取即时

Does it contain the date and the time? 是否包含日期和时间?

  1. Does it have a time offset? 它有时间偏移吗?
    A time offset is the +hh:mm part. 时间偏移是+hh:mm一部分。 Sometimes, +00:00 may be substituted with Z as 'Zulu time', UTC as Universal Time Coordinated, or GMT as Greenwich Mean Time. 有时,+ +00:00可以用Z代替“ Zulu时间”,用UTC代替世界标准时间,或者用GMT代替格林威治标准时间。 These also set the time zone. 这些也设置了时区。
    For these timestamps, you use OffsetDateTime . 对于这些时间戳,请使用OffsetDateTime

  2. Does it have a time zone? 是否有时区?
    For these timestamps, you use ZonedDateTime . 对于这些时间戳,请使用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 . 由java.time.ZoneId表示的“区域ID”(“ America / Los_Angeles”,“欧洲/伦敦”)。

    The list of time zones is compiled by a "TZ database" , backed by ICAAN. 时区列表由ICAAN支持的“ TZ数据库”进行编译。

    According to ZoneId 's javadoc, The zone id's can also somehow be specified as Z and offset. 根据ZoneId的javadoc,区域ID也可以以某种方式指定为Z和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. 如果只有TZ的时间戳落入时间偏移量变化的a时间,则表示该模棱两可,并且解释受ResolverStyle约束,请参见下文。

  3. 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: 因此,需要将其解析为LocalDateTime并通过添加缺少的信息将其转换为OffsetDateTime

    • You can assume that it is a UTC time. 您可以假设这是UTC时间。 Add the UTC offset of 0 hours. 加上0小时的UTC偏移量。
    • 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 eg to compare or substract two times (see Duration ), or when you don't know and it doesn't really matter (eg local bus schedule). 这很有用,例如比较或减去两次(请参见Duration ),或者在您不知道且并不重要的时候(例如,本地公交车时刻表)。

Partial time information 部分时间信息

  • Based on what the timestamp contains, you can take LocalDate , LocalTime , OffsetTime , MonthDay , Year , or YearMonth out of it. 基于什么样的时间戳包含,你可以采取LocalDateLocalTimeOffsetTimeMonthDayYear ,或YearMonth出来。

If you have the full information, you can get a java.time.Instant . 如果您有完整的信息,则可以获取java.time.Instant This is also internally used to convert between OffsetDateTime and ZonedDateTime . 这在内部也用于在OffsetDateTimeZonedDateTime之间进行转换。

Figure out how to parse it 弄清楚如何解析

There is an extensive documentation on DateTimeFormatter which can both parse a timestamp string and format to string. 有关DateTimeFormatter的大量文档,可以解析时间戳字符串并可以将其格式化为字符串。

The pre-created DateTimeFormatter s should cover moreless all standard timestamp formats. 预先创建的DateTimeFormatter应当涵盖所有标准时间戳格式。 For instance, ISO_INSTANT can parse 2011-12-03T10:15:30.123457Z . 例如, ISO_INSTANT可以解析2011-12-03T10:15:30.123457Z

If you have some special format, then you can create your own DateTimeFormatter (which is also a parser). 如果您有某种特殊格式,则可以创建自己的DateTimeFormatter (它也是解析器)。

private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
   .parseCaseInsensitive()
   .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
   .toFormatter();

I recommend to look at the source code of DateTimeFormatter and get inspired on how to build one using DateTimeFormatterBuilder . 我建议看一下DateTimeFormatter的源代码,并从如何使用DateTimeFormatterBuilder构建一个源代码中获得启发。 While you're there, also have a look at ResolverStyle which controls whether the parser is LENIENT, SMART or STRICT for the formats and ambiguous information. 当您在那里时,还可以查看ResolverStyle ,它控制解析器是LENIENT,SMART还是STRICT来获取格式和不明确的信息。

TemporalAccessor 时间访问器

Now, the frequent mistake is to go into the complexity of TemporalAccessor . 现在,常见的错误是进入TemporalAccessor的复杂性。 This comes from how the developers were used to work with SimpleDateFormatter.parse(String) . 这来自于开发人员如何与SimpleDateFormatter.parse(String) Right, DateTimeFormatter.parse("...") gives you TemporalAccessor . 正确, DateTimeFormatter.parse("...")给您TemporalAccessor

// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");

But, equiped with the knowledge from the previous section, you can conveniently parse into the type you need: 但是,有了上一节的知识,您可以方便地解析为所需的类型:

OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);

You do not actually need to the DateTimeFormatter either. 您实际上也不需要DateTimeFormatter The types you want to parse have the parse(String) methods. 您要解析的类型具有parse(String)方法。

OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");

Regarding TemporalAccessor , you can use it if you have a vague idea of what information there is in the string, and want to decide at runtime. 关于TemporalAccessor ,如果您不清楚字符串中包含哪些信息,并且想在运行时决定,则可以使用它。

I hope I shed some light of understanding onto your soul :) 我希望我对你的灵魂有所了解:)

Note: There's a backport of java.time to Java 6 and 7: ThreeTen-Backport . 注意: java.time有一个向Java 6和7的java.time移植: ThreeTen-Backport 。 For Android it has ThreeTenABP . 对于Android,它具有ThreeTenABP 。

[1] Not just that they are not stripes, but there also some weird extremes. [1]不仅因为它们不是条纹,而且还有一些怪异的极端。 For instance, some neighboring pacific islands have +14:00 and -11:00 time zones. 例如, 某些邻近的太平洋岛屿具有+14:00和-11:00时区。 That means, that while on one island, there is 1st May 3 PM, on another island not so far, it is still 30 April 12 PM (if I counted correctly :) ) 这就是说,在一个岛上,是5月1日下午3点,而在另一个岛上,则是4月30日,下午12点(如果我算错了:))

你可能感兴趣的:(java,datetime,java-8,timestamp,java-time)