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

时间:2017-08-18 12:24:59

标签: 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'无法解析:无法从TemporalAccessor获取ZonedDateTime

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

4 个答案:

答案 0 :(得分:7)

注意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”)< / LI>
  • 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)。

在圣保罗,DST于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]

当夏令时结束时:2018年2月18日 th 午夜,时钟从<午夜到晚上23点<强>返回 1小时 17 th < / SUP> 即可。因此,从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结束前后的日期具有不同的偏移量(-02:00-03:00)。这会影响epochMilli的价值。

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

答案 1 :(得分:2)

更正了有关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 :(得分:0)

我在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();
}

答案 3 :(得分:0)

您的代码将因以下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传递给它。
  4. 代码应如下所示:

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