【问题标题】:How to convert timestamp string to epoch time?如何将时间戳字符串转换为纪元时间?
【发布时间】:2018-01-27 03:17:22
【问题描述】:

我有2017-18-08 11:45:30.345格式的时间戳。
我想将它转换为纪元时间,所以我在下面做:

String timeDateStr = "2017-18-08 11:45:30.345"; 
DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
ZonedDateTime     zdt  = ZonedDateTime.parse(timeDateStr, dtf);        
System.out.println(zdt.toInstant().toEpochMilli());

我收到以下错误:

java.time.format.DateTimeParseException:无法解析文本“2017-18-08 11:45:30.345”:无法从 TemporalAccessor 获取 ZonedDateTime

我也尝试了不同的格式,但仍然出现错误。

【问题讨论】:

  • 那是错字,请检查更新的问题。谢谢指点。
  • @Assafs 不,不是唯一的错误。也请看一下图案符号“h”。
  • @User7723337 min is 60?
  • 问题解决了吗?
  • 当此模式没有关于时区的信息时 ZonedDateTime 是否有效?

标签: java timestamp epoch java-time datetime-parsing


【解决方案1】:

更正了您关于 yyyy-dd-MM 的代码。分钟值也可能是 1-59 而不是 60。您提供了 60。这是解决问题的另一种简单方法。只需使用 DateFormat 类。

String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-MM hh:mm:ss.SSS", Locale.ENGLISH);
try {
    Date d = df.parse(timeDateStr);
    System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
    e.printStackTrace();
}

【讨论】:

    【解决方案2】:

    注意originally 问题的输入是 2017-18-08 12:60:30.345(分钟字段中有 60),然后是 it was edited时间12:60 更改为11:45),但我决定继续讨论原始输入(12:60),因为它也适用于编辑版本(11:45)。


    ZonedDateTime 需要时区或偏移量,但输入 String 没有它(它只有日期和时间)。

    输入中还有另一个细节:

    • 分钟值为60,不接受:有效值为0到59(其实有办法接受,见下文“Lenient parsing”
    • hhclock-hour-of-am-pm field,因此还需要完全解析 AM/PM 指示符。因为你没有,你应该改用HH 模式

    所以模式必须是yyyy-dd-MM HH:mm:ss.SSS,输入不能有60 作为分钟值(除非你使用宽松的解析,我将在下面解释)并且你不能直接将它解析为@ 987654344@ 因为它没有时区/偏移量指示符。

    另一种方法是将其解析为LocalDateTime,然后定义该日期所在的时区/偏移量。在下面的示例中,我假设它位于UTC

    // change 60 minutes to 59 (otherwise it doesn't work)
    String timeDateStr = "2017-18-08 12:59:30.345";
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
    // parse to LocalDateTime
    LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
    
    // assume the LocalDateTime is in UTC
    Instant instant = dt.toInstant(ZoneOffset.UTC);
    System.out.println(instant.toEpochMilli());
    

    这将输出:

    1503061170345

    相当于UTC中的2017-18-08 12:59:30.345

    如果您想要另一个时区的日期,您可以使用ZoneId 类:

    // get the LocalDateTime in some timezone
    ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
    System.out.println(z.toInstant().toEpochMilli());
    

    输出是:

    1503057570345

    请注意,结果是不同的,因为相同的本地日期/时间在每个时区代表不同的Instant(在世界的每个地方,本地日期/时间2017-18-08 12:59:30.345 发生在不同的瞬间)。

    另请注意,API 使用IANA timezones names(始终采用Region/City 格式,如America/Sao_PauloEurope/Berlin)。 避免使用三个字母的缩写(如CSTPST),因为它们是ambiguous and not standard

    您可以致电ZoneId.getAvailableZoneIds() 获取可用时区列表(并选择最适合您系统的时区)。

    您也可以将系统的默认时区ZoneId.systemDefault() 一起使用,但这可以在不通知的情况下更改,即使在运行时也是如此,因此最好明确使用特定的时区。


    还有将LocalDateTime 转换为offset 的选项(如-05:00+03:00):

    // get the LocalDateTime in +03:00 offset
    System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());
    

    输出将等同于偏移量+03:00 中的本地日期/时间(比 UTC 提前 3 小时):

    1503050370345


    宽松解析

    作为@MenoHochschild reminded me in the comments,您可以使用宽松解析在分钟字段中接受60(使用java.time.format.ResolverStyle类):

    String timeDateStr = "2017-18-08 12:60:30.345";
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS")
        // use lenient parsing
        .withResolverStyle(ResolverStyle.LENIENT);
    // parse to LocalDateTime
    LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
    

    在这种情况下,将 60 分钟调整为下一小时,LocalDateTime 将是:

    2017-08-18T13:00:30.345


    夏令时

    如果您决定使用 UTC 或固定偏移量(使用 ZoneOffset 类),您可以忽略此部分。

    但如果您决定使用时区(使用ZoneId 类),您还必须注意DST (Daylight Saving Time) 问题。我将以我所在的时区为例 (America/Sao_Paulo)。

    在圣保罗,夏令时从 2017 年 10 月 15 日th开始:午夜时分,时钟从午夜向前移动 1 小时到凌晨 1 点。因此,该时区不存在 00:00 到 00:59 之间的所有当地时间。如果我在此间隔内创建本地日期,则会将其调整为下一个有效时刻:

    ZoneId zone = ZoneId.of("America/Sao_Paulo");
    
    // October 15th 2017 at midnight, DST starts in Sao Paulo
    LocalDateTime d = LocalDateTime.of(2017, 10, 15, 0, 0, 0, 0);
    ZonedDateTime z = d.atZone(zone);
    System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]
    

    DST 结束时:2018 年 2 月 18 日th 午夜,时钟将后移 1 小时,从午夜到 17th。因此,从 23:00 到 23:59 的所有当地时间都存在 两次(在 DST 和非 DST 中),您必须决定要使用哪一个:

    // February 18th 2018 at midnight, DST ends in Sao Paulo
    // local times from 23:00 to 23:59 at 17th exist twice
    LocalDateTime d = LocalDateTime.of(2018, 2, 17, 23, 0, 0, 0);
    // by default, it gets the offset before DST ends
    ZonedDateTime beforeDST = d.atZone(zone);
    System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]
    
    // get the offset after DST ends
    ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
    System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]
    

    请注意,夏令时结束前后的日期有不同的偏移量(-02:00-03:00)。这会影响 epochMilli 的值。

    您必须检查 DST 在您选择的时区的开始和结束时间,并相应地检查调整。

    【讨论】:

    • 您是唯一一个也看到 h 与 H 错误的回答者(因此赞成)。 about the minute = 60 -value:好吧,宽松的解析(通过构建器)可以解决这个问题(未经测试,但我想解析器会选择下一分钟,即下一小时和分钟 = 零)。
    • @MenoHochschild 我通常避免宽松的解析,因为我不喜欢这些自动转换。但我已经测试过了,它会将时间调整为13:00:30.345。我已经更新了答案,非常感谢!
    • 很好的答案!恭喜。
    • 他的 ZonedDateTime 变量无法工作,因为 sis 模式没有关于时区的信息,不是吗?
    【解决方案3】:

    只是我对 nagendra547 的回答做了一点改动

    请参考以下代码:-

    String timeDateStr = "2017-18-08 12:59:30.345";
    DateFormat df = new SimpleDateFormat("yyyy-dd-mm hh:mm:ss.SSS", Locale.ENGLISH);
    try {
        Date d = df.parse(timeDateStr);
        System.out.println(d.toInstant().toEpochMilli());
    } catch (ParseException e) {
        e.printStackTrace();
    }
    

    【讨论】:

      【解决方案4】:

      您的代码将因以下 3 个原因而失败。

      1. 您的日期字符串 (2017-18-08 12:60:30.345) 与您使用的格式化程序不匹配。它应该是 yyyy-MM-dd HH:mm:ss.SSS 而不是 yyyy-dd-MM hh:mm:ss.SSS
      2. 分钟的范围是 (0-59),60 不在这个范围内。
      3. 即使您已根据上述点更正了代码,它也不会为ZonedDateTime 运行。因此,您需要先创建一个LocalDateTime,然后将ZoneId 传递给它。

      代码应如下所示:

      String timeDateStr = "2017-18-08 12:59:30.345"; 
      DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
      LocalDateTime date = LocalDateTime.parse(timeDateStr, dtf);
      ZonedDateTime     zdt  = date.atZone(ZoneId.of("Europe/London"));
      System.out.println(zdt.toInstant().toEpochMilli());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-08
        • 2016-07-23
        • 2011-06-21
        • 2013-01-25
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        • 1970-01-01
        相关资源
        最近更新 更多