java - 如何将时间戳字符串转换为纪元时间?

标签 java timestamp epoch java-time datetime-parsing

我有 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: Text '2017-18-08 11:45:30.345' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor



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

最佳答案

注意 :originally 问题有输入 2017-18-08 12:60:30.345(在分钟字段中带有 60),然后 it was edited( 时间 12:60 更改为 11:45 ),但我决定保留这个输入的原始答案(讨论)因为它也适用于编辑后的版本 ( 12:60 )。
11:45 需要时区或偏移量,但输入 ZonedDateTime 没有它(它只有日期和时间)。
输入中还有另一个细节:

  • 分钟值是 String ,这是不被接受的:有效值是从 0 到 59(实际上有一种方法可以接受这个,请参阅下面的“宽松解析”)
  • 60clock-hour-of-am-pm field ,因此还需要完全解析 AM/PM 指示符。由于您没有它,您应该使用 hh 模式而不是

  • 所以模式必须是 HH ,输入不能有 yyyy-dd-MM HH:mm:ss.SSS 作为分钟值(除非你使用宽松的解析,我将在下面解释)并且你不能直接将它解析为 60 因为它没有时区/偏移指示符。
    一种替代方法是将其解析为 ZonedDateTime,然后定义此日期所在的时区/偏移量。在下面的示例中,我假设它在 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 中的 LocalDateTime
    如果你想要另一个时区的日期,你可以使用 2017-18-08 12:59:30.345 类:
    // get the LocalDateTime in some timezone
    ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
    System.out.println(z.toInstant().toEpochMilli());
    
    输出是:

    1503057570345


    请注意,结果是不同的,因为相同的本地日期/时间在每个时区代表不同的 ZoneId(在世界的每个部分,本地日期/时间 Instant 发生在不同的时刻)。
    另请注意,API 使用 IANA timezones names(始终采用 2017-18-08 12:59:30.345 格式,如 Region/CityAmerica/Sao_Paulo )。
    避免使用 3 个字母的缩写(如 Europe/BerlinCST ),因为它们是 ambiguous and not standard
    您可以通过调用 PST 获取可用时区列表(并选择最适合您系统的时区)。
    您还可以将 系统的默认时区 ZoneId.getAvailableZoneIds() 一起使用,但这可以在不通知的情况下更改,即使在运行时也是如此,因此最好明确使用特定的时区。

    还有将 ZoneId.systemDefault() 转换为 offset 的选项(如 LocalDateTime-05: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 ,您可以使用宽松解析来接受分钟字段中的 +03:00 (使用 60 类):
    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 分钟被调整到下一小时,java.time.format.ResolverStyle 将是:

    2017-08-18T13:00:30.345



    夏令时
    如果您决定使用 UTC 或固定偏移量(使用 LocalDateTime 类),则可以忽略此部分。
    但是如果您决定使用时区(使用 ZoneOffset 类),您还必须处理 DST (Daylight Saving Time) 问题。我将使用我居住的时区作为示例( ZoneId )。
    在圣保罗,夏令时从 2017 年 10 月 15 日开始:午夜时分,时钟从午夜到凌晨 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 日午夜,时钟将 向后移动 1 小时,从午夜到 17 日 的下午 23 点。因此,从 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]
    
    请注意,DST 结束前后的日期具有不同的偏移量( America/Sao_Paulo-02:00 )。这会影响 epochMilli 的值。
    您必须检查 DST 在您选择的时区的开始和结束时间,并相应地检查调整。

    关于java - 如何将时间戳字符串转换为纪元时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45756595/

    相关文章:

    python - 时间戳转换为日期时间 Python、Pandas

    python - 使用来自 Python 的时间戳更新 Googlesheet 单元格

    javascript - 将这些时间参数转换为纪元时间

    java - 我怎样才能提高 Java 中这个 n 次根算法的精度?

    java - org.apache.catalina.LifecycleException : Failed to initialize component [Connector[HTTP/1. 1-8443]]

    java - 更改主刻度间距的第一个索引

    python - 训练神经网络时,Tensorflow 完成后是否会自动恢复到最佳时期?

    java - 将 @OneToMany 与 @OneToOne 一起使用是否正确?

    python - 如何在 Python 中构建朴素贝叶斯模型时使用时间戳数据

    php - 将纪元时间转换为 H :mm timestamp in PHP