Java - SimpleDateFormat 格式化程序到 return 以毫秒为单位的纪元时间

Java - SimpleDateFormat formatter to return epoch time with milliseconds

我对 Java 和一般编码还很陌生 - 我有一些代码 return 是以下格式 yyyy.MM.dd HH:mm:ss:ms 的时间戳,如下所示:

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

这个returns:

2017.07.19 11:42:30:423

有没有办法将上面的 "SimpleDateFormat formatter" 代码编辑为 return date/time 作为包含毫秒的纪元时间戳,以便格式化值 returned根据以下?

1500464550423

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

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

谢谢

如果您有 java.util.Date,那么调用 getTime() 将 return 自纪元以来的毫秒数。例如:

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.

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

然后做:

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

在你的情况下你可以这样做:

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

    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  

你可以试试

long time = System.currentTimeMillis();

首先,检查documentation of SimpleDateFormat毫秒对应的模式是大写S,而小写s对应。问题是 SimpleDateFormat 通常不会抱怨并尝试将 423 解析为秒,将此数量添加到您的结束日期(给出不正确的结果)。

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

// 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之前):

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

由此,millis 的结果将是 1500464550423(相当于 UTC 中的特定日期和时间)。

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

// 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 新 date/time API

旧的 classes(DateCalendarSimpleDateFormat)有 lots of problems and design issues,它们正在被新的 APIs.

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).

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

由于输入String没有时区信息(只有日期和时间),首先我将它解析为一个LocalDateTime(一个class表示没有时区的日期和时间).然后我将此 date/time 转换为特定时区并从中获取毫秒值:

// 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 timezones names(始终采用 Region/City 格式,如 America/Sao_PauloEurope/Berlin)。 避免使用 3 个字母的缩写(如 CSTPST),因为它们是 ambiguous and not standard.

您可以通过调用 ZoneId.getAvailableZoneIds().

获取可用时区列表(并选择最适合您的系统的时区)

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

相反,你可以获取毫秒值来创建一个 Instant,将其转换为时区并将其传递给格式化程序:

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

您在格式模式字符串中使用大小写有一个简单的错误(区分大小写)。更糟糕的是,您正在使用旧的和麻烦的 SimpleDateFormat class。它的众多问题之一是它没有告诉您问题是什么。

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

    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 表示几分之一秒。如果仍然不清楚,the documentation 会告诉您小写 s 是正确的秒数,而您需要大写 S 来表示分数。让我们修复:

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

现在代码打印 2017-07-19T11:42:30.423,所以我们成功地正确解析了字符串。

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

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

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

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