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

在...创建的问题 Sat, Jun 8, 2019 12:00 AM

Java 8添加了一个新的 java.time 用于处理日期和时间的API( JSR 310 )。

我将日期和时间作为字符串(例如"2014-04-08 12:30")。如何从中获取 LocalDateTime 实例给定字符串?

使用完LocalDateTime对象之后:如何将LocalDateTime实例转换回格式与上图相同的字符串?

    
291
  1. 仅供参考,大部分时间大多数人都想要 ZonedDateTime 而不是 LocalDateTime 。这个名字是违反直觉的; Local通常意味着任何位置而不是特定时区。因此,LocalDateTime对象与时间线无关。为了有意义,要在时间线上获得指定时刻,您必须应用时区。
    2015-10-11 00:51:18Z
  2. 请参阅我的答案,解释LocalDateTimeZonedDateTime vs. OffsetDateTime vs. Instant vs. LocalDate vs. LocalTime,如何保持冷静,为什么它如此复杂以及如何在第一次拍摄时做得很好。
    2019-06-08 17:40:28Z
  3. 如果它不是不切实际的长,LocalDateTime可能会被命名为ZonelessOffsetlessDateTime
    2019-06-10 04:41:05Z
  4. 醇>
    4个答案                              4 跨度>                         

    解析日期和时间

    要从字符串创建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.ISO_DATE_TIME从上面格式化LocalDateTime实例将导致字符串"1986-04-08T12:30:00"

    parse()format()方法适用于所有日期/时间相关对象(例如LocalDateZonedDateTime

        
    469
    2016-02-11 16:09:25Z
    1. 只是要注意DateTimeFormatter是不可变的和线程安全的,因此推荐的方法是尽可能将它存储在静态常量中。
      2014-05-27 10:00:38Z
    2. @ DawoodAbbasi试试DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
      2016-05-28 23:08:20Z
    3. 我在LocalDateTime中没有任何format()方法...
      2016-08-19 06:56:19Z
    4. @ Loenix也许是因为你试图在LocalDateTime类而不是实例上调用format()?至少,这就是我的所作所为:我在上面的例子中将DateTimedateTime混淆了。
      2016-08-26 15:10:12Z
    5. 不要忘记MM上的大写
      2017-06-25 03:50:41Z
    6. 醇>

    如果LocalDate.parse()位于LocalDateTime.parse()上使用StringString而不提供图案time /format /DateTimeFormatter.html#ISO_LOCAL_DATE_TIME“rel =”noreferrer“> 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
    
        
    132
    2017-09-02 08:45:47Z
    1. 这个答案涉及一个重要的主题:尽可能使用预定义的格式化程序,例如不要在“yyyy-MM-dd”上创建格式化程序,而是使用DateTimeFormatter.ISO_LOCAL_DATE。它会让你的代码看起来更清晰。此外,尝试最大限度地使用ISO8061格式,从长远来看它将带来红利。
      2016-08-03 18:08:45Z
    2. 惊人的答案,有助于转换任何类型的日期。非常感谢...
      2017-04-06 14:40:50Z
    3. 我想解析像2018-08-09 12:00:08这样的验证日期,但是当我解析时,我看到添加了T,我不需要。有办法吗?
      2018-09-14 05:38:38Z
    4. 醇>

    上面的两个答案都很好地解释了有关字符串模式的问题。但是,如果您正在使用 ISO 8601 ,则无需申请DateTimeFormatter,因为LocalDateTime是已为此做好准备:

    将LocalDateTime转换为时区ISO8601字符串

     
    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();
    
        
    31
    2017-03-29 21:37:46Z

    将具有日期和时间的字符串解析为特定时间点(Java将其称为“ Instant ”)非常复杂。 Java已经在几次迭代中解决了这个问题。最新的java.timejava.time.chrono几乎涵盖了所有需求(时间膨胀 :)除外。

    然而,这种复杂性带来了很多困惑。

    理解日期解析的关键是:

    为什么Java有很多方法来解析日期

    1. 有几种系统可以测量时间。例如,历史上的日本历法来自各自皇帝或王朝统治的时间范围。然后有例如UNIX时间戳。 幸运的是,整个(商业)世界设法使用相同的。
    2. 从历史上看,系统正在从/向=“https://www.history.com/news/6-things-you-may-not-know-about-the-gregorian-切换。日历“rel =”nofollow noreferrer“>各种原因。例如。从朱利安历法到公元1582年的格里高利历。所以在此之前的“西方”日期需要区别对待。
    3. 当然,改变并非立即发生。因为日历来自某些宗教的总部,而欧洲的其他地方也相信其他食物德国直到1700年才开始转换。
    4. 醇>

      ......为什么LocalDateTime,ZonedDateTime等人。这么复杂

      1. 时区。 时区基本上是地球表面的“条纹” * [1] ,其权限遵循相同的规则,它何时具有时间偏移。这包括夏令时规则。
        各个地区的时区随时间而变化,主要取决于谁征服了谁。一个时区的规则也是随时间变化

      2. 有时间抵消。这与时区不同,因为时区可以是例如时区。 “布拉格”,但有夏季时间抵消和冬季时间抵消 如果您获得带有时区的时间戳,则偏移量可能会有所不同,具体取决于它所在年份的哪一部分。在闰日期间,时间戳可能意味着2个不同的时间,因此如果没有其他信息,则无法可靠地转换。结果 注意:通过 timestamp 我的意思是“包含日期和/或时间的字符串,可选择带有时区和/或时间偏移。”

      3. 某些时区可能会共享相同的时间偏移。例如,当夏令时偏移无效时,GMT /UTC时区与“伦敦”时区相同。

      4. 醇>

        使它更复杂一点(但这对你的用例来说并不重要):

        1. 科学家观察地球的动态,随着时间的推移而变化;基于此,他们在个别年末增加秒数。 (因此2040-12-31 24:00:00可能是有效的日期时间。)这需要定期更新系统用于使日期转换正确的元数据。例如。在Linux上,您可以定期更新Java包,包括这些新数据。
        2. 更新并不总是保留历史和未来时间戳的先前行为。因此,可能会解析围绕某个时区的两个时间戳的变化,比较它们可能会给出不同的结果。这也适用于比较受影响的时区和其他时区。

          如果这会导致您的软件出现错误,请考虑使用一些没有这么复杂规则的时间戳,例如 UNIX时间戳

        3. 由于7,对于将来的日期,我们无法准确转换日期。因此,例如,8524-02-17 12:00:00的当前解析可能会在未来解析后的几秒内完成。

        4. 醇>

          JDK的API随着当代需求而发展

    • 早期的Java发布只有java.util.Date,它有点天真的方法,假设只有年,月,日和时间。这很快就不够了。
    • 此外,数据库的需求也不同,所以很早就引入了java.sql.Date,它有自己的局限性。
    • 由于没有很好地涵盖不同的日历和时区,因此引入了Calendar API。
    • 这仍然没有涵盖时区的复杂性。然而,上述API的混合使用真的很痛苦。因此,当Java开发人员开始研究全球Web应用程序时,针对大多数用例的库(如JodaTime)很快就会受到欢迎。 JodaTime是大约十年的事实上的标准。
    • 但JDK没有与JodaTime集成,因此使用它有点麻烦。因此,经过长时间讨论如何解决问题后, JSR-310 创建了主要基于JodaTime

    如何在Java java.time中处理它

    确定将时间戳解析为

    的类型

    当您使用时间戳字符串时,您需要知道它包含哪些信息。 这是关键点。如果你没有做到这一点,你最终会得到一个神秘的例外,例如“无法创建即时”或“区域偏移丢失”或“未知区域ID”等

    是否包含日期和时间?

    1. 是否有时间偏移?
      时间偏移是+hh:mm部分。有时,+00:00可以用Z代替'祖鲁时间',UTC代替世界时间协调,或GMT代替格林威治标准时间。这些也设置了时区。
      对于这些时间戳,您可以使用 OffsetDateTime

    2. 是否有时区?
      对于这些时间戳,您可以使用 ZonedDateTime 。点击 区域由

      指定
      • name(“Prague”,“Pacific Standard Time”,“PST”)或
      • “区域ID”(“America /Los_Angeles”,“Europe /London”),由 java.time.ZoneId

      时区列表由“TZ数据库”编制,由ICAAN提供支持

      根据ZoneId的javadoc,区域id也可以某种方式指定为Z和偏移量。我不确定这是如何映射到真实区域的。 如果只有TZ的时间戳落入时间偏移变化的闰日,那么它是不明确的,并且解释是ResolverStyle的主题,见下文。

    3. 如果它既没有,则假设或忽略缺少的上下文。消费者必须做出决定。因此需要将其解析为LocalDateTime并通过添加缺少的信息转换为OffsetDateTime

      • 您可以假设它是UTC时间。添加0小时的UTC偏移量。
      • 您可以假设它是转换发生地点的时间。通过添加系统的时区来转换它。
      • 你可以忽视并按原样使用它。这很有用,例如比较或减少两次(见 Duration ),或者当你不知道并且它并不重要时(例如当地的公交车时刻表)。
    4. 醇>

      部分时间信息

    • 根据时间戳的内容,您可以选择LocalDate,LocalTime,OffsetTime,MonthDay,YearYearMonth

    如果您有完整的信息,可以获得 java.time.Instant 。这也在内部用于在OffsetDateTimeZonedDateTime之间进行转换。

    弄清楚如何解析它

    有关 DateTimeFormatter的大量文档可以解析时间戳字符串和格式化为字符串。

    预先创建的DateTimeFormatter应该涵盖所有标准时间戳格式。例如,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构建一个的灵感。当你在那里时,还要看看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 6和7有一个java.time的后端: ThreeTen-Backport 。对于Android,它有 ThreeTenABP

    [1] 不仅仅是它们不是条纹,还有一些奇怪的极端。例如,一些邻近的太平洋岛屿有+14: 00和-11:00时区。这意味着,虽然在一个岛上,有一个5月1日下午,在另一个岛上,到目前为止,它仍然是4月30日下午12点(如果我算得正确:))

        
    3
    2019-06-08 22:44:01Z
来源放置 这里