如何将时间戳字符串转换为纪元时间?
How to convert timestamp string to epoch time?
我有 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
我也尝试了不同的格式,但仍然出现错误。
更正了有关 yyyy-dd-MM 的代码。分钟值也可以是 1-59 而不是 60。您提供了 60。这是解决问题的另一种简单方法。只需使用 DateFormat class.
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();
}
注意:originally the question had the input 2017-18-08 12:60:30.345
(with 60
in the minutes field), then it was edited(时间由12:60
改为11:45
),但我决定继续讨论原始输入 (12:60
),因为它也适用于编辑后的版本 (11:45
)。
ZonedDateTime
需要时区或偏移量,但输入 String
没有(它只有日期和时间)。
输入中还有一个细节:
- 分钟值是
60
,这是不被接受的:有效值是从0到59(实际上有一种方法可以接受这个,参见“Lenient parsing” 下面)
-
hh
是 clock-hour-of-am-pm field,因此它还需要 AM/PM 指示符才能完全解析。因为你没有它,你应该使用 HH
模式来代替
所以模式必须是 yyyy-dd-MM HH:mm:ss.SSS
,输入不能有 60
作为分钟值(除非你使用宽松的解析,我将在下面解释)而且你不能direclty 将其解析为 ZonedDateTime
因为它没有 timezone/offset 指示符。
一种替代方法是将其解析为 LocalDateTime
,然后定义此日期所在的 timezone/offset。在下面的例子中,我假设它在 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
class:
// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
输出为:
1503057570345
注意结果不一样,因为同一个地方date/time在每个时区代表不同的Instant
(在世界的每个地方,地方date/time2017-18-08 12:59:30.345
发生在不同的瞬间)。
另请注意,API 使用 IANA timezones names(始终采用 Region/City
格式,如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 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
中的本地date/time(比UTC提前3小时):
1503050370345
宽松的解析
作为,您可以使用宽松的解析在分钟字段中接受60
(使用java.time.format.ResolverStyle
class):
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
class),则可以忽略此部分。
但是如果您决定使用时区(ZoneId
class),您还必须注意 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 日th 午夜,时钟偏移后 1 小时,从午夜到 [=23 PM 17第。所以从23:00到23:59的所有本地时间都存在两次(夏令时和非夏令时),你必须决定你想要哪个:
// 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 的开始和结束时间,并相应地检查调整。
只是我对 nagendra 547 的回答做了一点改动
请参考以下代码:-
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 个原因而失败。
- 您的日期字符串 (
2017-18-08 12:60:30.345
) 与您使用的格式化程序不匹配。应该是 yyyy-MM-dd HH:mm:ss.SSS 而不是 yyyy-dd-MM hh:mm:ss.SSS
- 分钟的范围是(0-59),60不在这个范围内。
- 即使您根据上述观点更正了代码,它也不会 运行
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());
我有 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
我也尝试了不同的格式,但仍然出现错误。
更正了有关 yyyy-dd-MM 的代码。分钟值也可以是 1-59 而不是 60。您提供了 60。这是解决问题的另一种简单方法。只需使用 DateFormat class.
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();
}
注意:originally the question had the input 2017-18-08 12:60:30.345
(with 60
in the minutes field), then it was edited(时间由12:60
改为11:45
),但我决定继续讨论原始输入 (12:60
),因为它也适用于编辑后的版本 (11:45
)。
ZonedDateTime
需要时区或偏移量,但输入 String
没有(它只有日期和时间)。
输入中还有一个细节:
- 分钟值是
60
,这是不被接受的:有效值是从0到59(实际上有一种方法可以接受这个,参见“Lenient parsing” 下面) -
hh
是 clock-hour-of-am-pm field,因此它还需要 AM/PM 指示符才能完全解析。因为你没有它,你应该使用HH
模式来代替
所以模式必须是 yyyy-dd-MM HH:mm:ss.SSS
,输入不能有 60
作为分钟值(除非你使用宽松的解析,我将在下面解释)而且你不能direclty 将其解析为 ZonedDateTime
因为它没有 timezone/offset 指示符。
一种替代方法是将其解析为 LocalDateTime
,然后定义此日期所在的 timezone/offset。在下面的例子中,我假设它在 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
class:
// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
输出为:
1503057570345
注意结果不一样,因为同一个地方date/time在每个时区代表不同的Instant
(在世界的每个地方,地方date/time2017-18-08 12:59:30.345
发生在不同的瞬间)。
另请注意,API 使用 IANA timezones names(始终采用 Region/City
格式,如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 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
中的本地date/time(比UTC提前3小时):
1503050370345
宽松的解析
作为60
(使用java.time.format.ResolverStyle
class):
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
class),则可以忽略此部分。
但是如果您决定使用时区(ZoneId
class),您还必须注意 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 日th 午夜,时钟偏移后 1 小时,从午夜到 [=23 PM 17第。所以从23:00到23:59的所有本地时间都存在两次(夏令时和非夏令时),你必须决定你想要哪个:
// 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 的开始和结束时间,并相应地检查调整。
只是我对 nagendra 547 的回答做了一点改动
请参考以下代码:-
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 个原因而失败。
- 您的日期字符串 (
2017-18-08 12:60:30.345
) 与您使用的格式化程序不匹配。应该是 yyyy-MM-dd HH:mm:ss.SSS 而不是 yyyy-dd-MM hh:mm:ss.SSS - 分钟的范围是(0-59),60不在这个范围内。
- 即使您根据上述观点更正了代码,它也不会 运行
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());