关于日期:Java – SimpleDateFormat格式化程序,以毫秒为单位返回纪元时间

Java - SimpleDateFormat formatter to return epoch time with milliseconds

本问题已经有最佳答案,请猛点这里访问。

我是Java和编码的新手 - 我有一些代码以下列格式yyyy.MM.dd HH:mm:ss:ms返回时间戳,如下所示:

1
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");

返回:

1
2017.07.19 11:42:30:423

有没有办法编辑上面的"SimpleDateFormat格式化程序"代码,将日期/时间作为包含毫秒的纪元时间戳返回,以便返回的值按照下面的格式进行格式化?

1
1500464550423

我希望我可以修改SimpleDateFormat formatter代码的("yyyy.MM.dd HH:mm:ss:sss")部分来执行此操作。

非常感谢任何帮助或建议。

谢谢


在格式模式字符串中使用大小写时遇到一个简单的错误(这些区分大小写)。更糟糕的是,你正在使用旧的麻烦的SimpleDateFormat类。它的许多问题之一是它没有告诉你问题是什么。

所以我建议您使用现代Java日期和时间API(我故意使用您的格式模式字符串逐字):

1
2
3
4
5
    String receivedTimetamp ="2017.07.19 11:42:30:423";
    DateTimeFormatter parseFormatter
            = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:sss");
    LocalDateTime dateTime = LocalDateTime.parse(receivedTimetamp, parseFormatter);
    System.out.println(dateTime);

此代码抛出IllegalArgumentException: Too many pattern letters: s。我希望这可以让你意识到这样一个事实,即你使用的是两秒钟的秒数和三秒钟的秒数。如果仍然不清楚,文档将告诉您小写s在几秒内是正??确的,而分数需要大写s。我们来修理:

1
2
    DateTimeFormatter parseFormatter
            = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");

现在代码打印2017-07-19T11:42:30.423,因此我们设法正确解析字符串。

要转换为毫秒,我们仍然缺少一个重要的信息:在什么时区应该解释时间戳?我认为两个明显的猜测是UTC和你当地的时区(我不知道)。试试UTC:

1
    System.out.println(dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli());

这会产生1500464550423,这是您要求的数字。我想我们已经完成了。

如果您想要JVM的时区设置,请使用.atZone(ZoneId.systemDefault())而不是.atOffset(ZoneOffset.UTC),但要注意该设置可能会被同一JVM中运行的其他软件更改,因此这很脆弱。


首先,查看SimpleDateFormat的文档。对应于毫秒的模式是大写S,而小写S对应于秒。问题是SimpleDateFormat通常不会抱怨并尝试以秒为单位解析423,将此金额添加到结束日期(给出不正确的结果)。

无论如何,SimpleDateFormat只是将String解析为java.util.Date或将Date格式化为String。如果你想要epoch millis值,你必须从Date对象获取它:

1
2
3
4
5
6
7
8
9
// input string
String s ="2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
// parse to a date
Date date = formatter.parse(s);
// get epoch millis
long millis = date.getTime();
System.out.println(millis); // 1500475350423

问题是SimpleDateFormat使用系统的默认时区,因此上面的最终值(1500475350423)将等于系统时区中的特定日期和时间(可能与您的时区不同 - 仅用于记录,我的系统的默认时区是America/Sao_Paulo)。如果要指定此日期在哪个时区,则需要在格式化程序中设置(在调用parse之前):

1
2
// set a timezone to the formatter (using UTC as example)
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

这样,millis的结果将是1500464550423(相当于UTC中的指定日期和时间)。

要执行相反的操作(从millis值创建日期),您必须创建一个Date对象,然后将其传递给格式化程序(同时注意设置格式化程序的时区):

1
2
3
4
5
6
7
// create date from millis
Date date = new Date(1500464550423L);
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// format date
String formatted = formatter.format(date);

Java新的日期/时间API

旧类(DateCalendarSimpleDateFormat)存在许多问题和设计问题,并且它们将被新API替代。

如果您使用的是Java 8,请考虑使用新的java.time API。与旧的API相比,它更容易,更少出错并且更不容易出错。

如果您使用Java <= 7,则可以使用ThreeTen Backport,这是Java 8的新日期/时间类的一个很好的后端。对于Android,还有ThreeTenABP(更多关于如何在这里使用它)。

以下代码适用于两者。
唯一的区别是包名称(在Java 8中是java.time,在ThreeTen Backport(或Android的ThreeTenABP)中是org.threeten.bp),但类和方法名称是相同的。

由于输入String没有时区信息(仅限日期和时间),首先我将其解析为LocalDateTime(表示没有时区的日期和时间的类)。然后我将此日期/时间转换为特定时区并从中获取millis值:

1
2
3
4
5
6
7
8
9
10
11
// input string
String s ="2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// as the input string has no timezone information, parse it to a LocalDateTime
LocalDateTime dt = LocalDateTime.parse(s, formatter);

// convert the LocalDateTime to a timezone
ZonedDateTime zdt = dt.atZone(ZoneId.of("Europe/London"));
// get the millis value
long millis = zdt.toInstant().toEpochMilli(); // 1500460950423

该值现在1500460950423,相当于伦敦时区的指定日期和时间。

请注意,API使用IANA时区名称(始终采用Region/City格式,如America/Sao_PauloEurope/Berlin)。
避免使用3个字母的缩写(如CSTPST),因为它们不明确且不标准。

您可以通过调用ZoneId.getAvailableZoneIds()获取可用时区列表(并选择最适合您系统的时区)。

如果要使用UTC,也可以使用ZoneOffset.UTC常量。

相反,您可以获取millis值来创建Instant,将其转换为时区并将其传递给formatter:

1
2
3
4
5
6
7
8
// create Instant from millis value
Instant instant = Instant.ofEpochMilli(1500460950423L);
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// convert to timezone
ZonedDateTime z = instant.atZone(ZoneId.of("Europe/London"));
// format
String formatted = z.format(formatter);


你可以试试

1
long time = System.currentTimeMillis();


1
2
3
4
    String strDate ="Jun 13 2003 23:11:52.454 UTC";
    DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
    ZonedDateTime     zdt  = ZonedDateTime.parse(strDate,dtf);        
    System.out.println(zdt.toInstant().toEpochMilli());  // 1055545912454


第一个建议是转移到java8 java.time API而不是学习损坏的java.date API

然后做:

1
2
Instant i = Instant.now();
System.out.println(i.toEpochMilli());

在你的情况下你可以做:

1
2
3
LocalDateTime myldt = LocalDateTime.parse("2017-06-14 14:29:04",
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(myldt.toInstant(ZoneOffset.UTC).toEpochMilli());

请注意,只要您使用api玩更多内容,您就会找到更多方法来实现相同的目标,最后您将结束调用toEpochMilli


如果你有java.util.Date那么调用getTime()将返回自纪元以来的millis数。例如:

1
2
3
4
5
6
7
8
9
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");

Date dateToBeFormatted = new Date();

// this will print a datetime literal on the above format
System.out.println(formatter.format(dateToBeFormatted));

// this will print the number of millis since the Java epoch
System.out.println(dateToBeFormatted.getTime());

这里的关键点是,为了获得自纪元以来的毫秒数,您不需要SimpleDateFormatter,因为自纪元以来的毫秒数是Date的属性。