如何将时间戳字符串转换为纪元时间? [英] How to convert timestamp string to epoch time?
问题描述
我的格式时间戳 2017-18-08 11:45:30.345
。
我想把它转换为纪元时间,所以我在下面做:
I have time stamp in format 2017-18-08 11:45:30.345
.
I want to convert it to epoch time, so I am doing below:
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
java.time.format.DateTimeParseException: Text '2017-18-08 11:45:30.345' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor
我也尝试了不同的格式但仍然出错。
I also tried different formats but still getting errors.
推荐答案
注意:最初问题的输入 2017-18-08 12:60:30.345
(含 60
在分钟字段中),然后它已被编辑(时间从 12:60
更改为 11:45
),但我决定保留此答案讨论原始输入( 12:60
),因为它也适用于编辑版本( 11:45
)。
Note: originally the question had the input 2017-18-08 12:60:30.345
(with 60
in the minutes field), then it was edited (the time changed from 12:60
to 11:45
), but I decided to keep this answer discussing about the original input (12:60
), as it also works for the edited version (11:45
).
ZonedDateTime
需要时区或偏移,但输入字符串
没有它(它只有日期和时间)。
ZonedDateTime
needs a timezone or offset, but the input String
doesn't have it (it has only date and time).
输入中还有另一个细节:
There are also another details in the input:
- 分钟值
60
,不接受:有效值为从0到59(实际上有一种方法可以接受这个,请参阅Lenient解析以下) -
hh
是 clock-hour-am-pm 字段,所以它还需要完全解决AM / PM指示符。如你没有,你应该使用HH
模式
- the minute value is
60
, which is not accepted: the valid values are from 0 to 59 (actually there's a way to accept this, see "Lenient parsing" below) - the
hh
is the clock-hour-of-am-pm field, so it also needs the AM/PM designator to be fully resolved. As you don't have it, you should use theHH
pattern instead
所以模式必须是 yyyy-dd-MM HH:mm:ss.SSS
,输入不能 60
作为分钟值(除非你使用宽松解析,我将在下面解释)并且你不能直接将它解析为 ZonedDateTime
,因为它没有时区/偏移指示符。
So the pattern must be yyyy-dd-MM HH:mm:ss.SSS
, the input can't have 60
as the minutes value (unless you use lenient parsing, which I'll explain below) and you can't direclty parse it to a ZonedDateTime
because it doesn't have a timezone/offset designator.
一种方法是将其解析为 LocalDateTime
然后定义在哪个时区/抵消这个日期是。在下面的示例中,我假设它位于 UTC :
One alternative is to parse it to a LocalDateTime
and then define in which timezone/offset this date is. In the example below, I'm assuming it's in 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
1503061170345
相当于 2017-18-08 12:59:30.345
在 UTC 。
如果你想在另一个时区中使用日期,你可以使用 ZoneId
类:
If you want the date in another timezone, you can use the ZoneId
class:
// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
输出为:
1503057570345
1503057570345
请注意结果不同,因为相同的本地日期/时间代表不同的每个时区的即时
(在世界各地,本地日期/时间 2017-18-08 12:59:30.345
发生在另一个瞬间)。
Note that the result is different, because the same local date/time represents a different Instant
in each timezone (in each part of the world, the local date/time 2017-18-08 12:59:30.345
happened in a different instant).
另请注意,API使用 IANA时区名称(始终采用地区/城市
格式,如 America / Sao_Paulo
或欧/柏林
)。
避免使用3个字母的缩写(例如 CST
或 PST
),因为它们。
Also note that API uses IANA timezones names (always in the format Region/City
, like America/Sao_Paulo
or Europe/Berlin
).
Avoid using the 3-letter abbreviations (like CST
or PST
) because they are ambiguous and not standard.
您可以获得可用时区列表(和通过调用 ZoneId.getAvailableZoneIds()
来选择最适合您系统的那个。
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds()
.
您也可以使用系统的默认时区与 ZoneId.systemDefault()
,但即使在运行时也可以在不事先通知的情况下进行更改,因此最好明确使用特定的一个。
You can also use the system's default timezone with ZoneId.systemDefault()
, but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
还可以选择将 LocalDateTime
转换为抵消(例如 -05:00
或 +03:00
):
There's also the option to convert the LocalDateTime
to an offset (like -05:00
or +03:00
):
// get the LocalDateTime in +03:00 offset
System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());
输出将等于偏移 +中的本地日期/时间03:00
(比UTC早3小时):
The output will be equivalent to the local date/time in the offset +03:00
(3 hours ahead of UTC):
1503050370345
1503050370345
宽松解析
作为@MenoHochschild在评论中提醒我,你可以使用宽松的解析来接受分钟字段中的 60
(使用 java.time.format.ResolverStyle
类):
Lenient parsing
As @MenoHochschild reminded me in the comments, you can use lenient parsing to accept 60
in the minutes field (using the 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
将是:
In this case, 60 minutes are adjusted to the next hour, and the LocalDateTime
will be:
2017-08-18T 13:00 :30.345
夏令时
如果您决定使用UTC或固定偏移量(使用 ZoneOffset
类),您可以忽略此部分。
Daylight Saving Time
If you decide to use UTC or a fixed offset (using ZoneOffset
class), you can ignore this section.
但是如果您决定使用时区( ZoneId
类),您还必须照顾夏令时(夏令时)问题。我将使用我居住的时区作为示例( America / Sao_Paulo
)。
But if you decide to use a timezone (with ZoneId
class), you must also take care of DST (Daylight Saving Time) issues. I'm gonna use the timezone I live in as example (America/Sao_Paulo
).
在圣保罗, DST于2017年10月15日 th 开始:在午夜,时钟从午夜到凌晨1点向前1小时前进。因此,此时区内不存在00:00至00:59之间的所有当地时间。如果我在此时间间隔内创建了本地日期,则会将其调整为下一个有效时间:
In São Paulo, DST starts at October 15th 2017: at midnight, clocks shift 1 hour forward from midnight to 1 AM. So all local times between 00:00 and 00:59 don't exist in this timezone. If I create a local date in this interval, it's adjusted to the next valid moment:
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点从<午夜到晚上的 17 个 即可。所以从23:00到23:59的所有当地时间都存在两次(在夏令时和非DST中),你必须决定你想要哪一个:
When DST ends: in February 18th 2018 at midnight, clocks shift back 1 hour, from midnight to 23 PM of 17th. So all local times from 23:00 to 23:59 exist twice (in DST and in non-DST), and you must decide which one you want:
// 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的价值。
Note that the dates before and after DST ends have different offsets (-02:00
and -03:00
). This affects the value of epochMilli.
您必须检查DST何时开始并以您选择的时区结束,并相应地检查调整。
You must check when DST starts and ends in the timezone you choose and check the adjustments accordingly.
这篇关于如何将时间戳字符串转换为纪元时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!