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

我有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

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

参考方案

注意: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解析”)
  • hhclock-hour-of-am-pm field,因此还需要完全解析AM / PM指示符。由于没有它,应该使用HH模式代替
  • 因此模式必须为yyyy-dd-MM HH:mm:ss.SSS,输入的分钟数不能为60(除非您使用宽大的解析,这将在下面解释),并且您不能直接将其解析为ZonedDateTime,因为它没有时区/偏移量指示符。
    一种替代方法是将其解析为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)。
    避免使用3个字母的缩写(例如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-18T 13:00 :30.345

    夏令时
    如果决定使用UTC或固定偏移量(使用ZoneOffset类),则可以忽略此部分。
    但是,如果您决定使用时区(带有ZoneId类),则还必须注意DST (Daylight Saving Time)问题。我将以我居住的时区为例(America/Sao_Paulo)。
    在圣保罗,夏令时开始于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]
    

    夏令时结束时:在2018年2月18日午夜,时钟从第17个的午夜移动到 1小时,从午夜到3:00t。因此,所有本地时间从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的开始和结束时间在您选择的时区,并相应地检查调整。

    Java-搜索字符串数组中的字符串 - java

    在Java中,我们是否有任何方法可以发现特定字符串是字符串数组的一部分。我可以避免出现一个循环。例如String [] array = {"AA","BB","CC" }; string x = "BB" 我想要一个if (some condition to tell wheth…

    Java:正则表达式模式匹配器是否有大小限制? - java

    我的模式类似于OR:“word1 | word2 | word3”我大约有800个字。可能有问题吗? 参考方案 您仅受记忆和理智的限制。 :)

    Java:线程池如何将线程映射到可运行对象 - java

    试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

    JAVA:字节码和二进制有什么区别? - java

    java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

    Java Scanner读取文件的奇怪行为 - java

    因此,在使用Scanner类从文件读取内容时,我遇到了一个有趣的问题。基本上,我试图从目录中读取解析应用程序生成的多个输出文件,以计算一些准确性指标。基本上,我的代码只是遍历目录中的每个文件,并使用扫描仪将其打开以处理内容。无论出于何种原因,扫描程序都不会读取其中的一些文件(所有UTF-8编码)。即使文件不是空的,scanner.hasNextLine()在…