【问题标题】:Java - SimpleDateFormat formatter to return epoch time with milliseconds [duplicate]Java - SimpleDateFormat 格式化程序以毫秒返回纪元时间[重复]
【发布时间】:2017-12-24 16:24:49
【问题描述】:

我对 Java 和一般编码非常陌生 - 我有一些代码以以下格式返回时间戳 yyyy.MM.dd HH:mm:ss:ms,如下所示:

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

这会返回:

2017.07.19 11:42:30:423

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

1500464550423

我希望我可以修改 SimpleDateFormat formatter 代码的 ("yyyy.MM.dd HH:mm:ss:sss") 部分来做到这一点。

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

谢谢

【问题讨论】:

  • 我不是 100% 清楚。你是说你正在接收2017.07.19 11:42:30:423 作为一个字符串,并且你想将它转换为自纪元以来的毫秒数?
  • 没错 - 时间戳值将在后面的代码中使用,并用于制作一串变量 - 返回时间戳的部分是“+formatter.format(time)”,不带引号- 这就是为什么我希望我可以修改代码的 ("yyyy.MM.dd HH:mm:ss:sss") 部分以在毫秒内返回 epoc,因为我不确定如何修改要使用的代码人们在这里建议的其他方法...

标签: java date datetime timestamp epoch


【解决方案1】:

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

所以我建议您改用现代 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 中运行的其他软件更改,因此这很脆弱。

【讨论】:

  • 感谢您的精彩解释-我很难理解如何将其集成到我的代码中,因为我对 Java 还很陌生,但是我不怀疑您的建议是正确的-我需要花一些时间来解决这个问题
【解决方案2】:

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

无论如何,SimpleDateFormat 只是将String 解析为java.util.Date 或将Date 格式化为String。如果你想要 epoch millis 值,你必须从 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 中的指定日期和时间)。

反之亦然(从 millis 值创建日期),您必须创建一个 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 新日期/时间 API

旧的类(DateCalendarSimpleDateFormat)有 lots of problemsdesign issues,它们正在被新的 API 取代。

如果您使用的是 Java 8,请考虑使用 new java.time API。更简单,less bugged and less error-prone than the old APIs

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

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

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

// 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)。 避免使用三个字母的缩写(如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);

【讨论】:

    【解决方案3】:

    第一个建议是迁移到 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

    【讨论】:

    • 感谢您的评论 - 我上面引用的代码是代码块的一部分,我不确定如何整合您的建议 - 理想情况下,我需要了解是否/如何使用“SimpleDateFormat”函数返回一个纪元时间戳,因为这意味着我不需要重新调整任何其他代码,因为我的理解有限
    • 是的。而不是使用 SimpleDateFormat 使用 DateTimeFormatter
    • 谢谢,我会玩一玩,看看我是否可以在代码中将“SimpleDateFormat”换成“DateTimeFormatter”,并使用“SSS”作为格式化程序(?) - 我很新java所以请原谅我的无知!
    • 你需要 java 8 才能使用该类
    【解决方案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  
    

    【讨论】:

    • 不错的代码,但缺乏解释。仅代码的答案很少有帮助,因此请说明您是如何解决提问者的问题的。
    • 这个帖子可能会有所帮助stackoverflow.com/questions/6687433/…
    【解决方案5】:

    你可以试试

    long time = System.currentTimeMillis();
    

    【讨论】:

    • 我认为 op 想从字符串日期中获取毫秒
    • 你是对的......但是问题上没有指定
    • 这只是对一个问题的回应......假设作者是从系统中获取日期
    【解决方案6】:

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

    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 的属性。

    【讨论】:

    • 感谢您的评论 - 我上面引用的代码是代码块的一部分,我不确定如何整合您的建议 - 理想情况下,我需要了解是否/如何使用“SimpleDateFormat”函数返回一个纪元时间戳,因为这意味着我不需要重新调整任何其他代码,因为我的理解有限
    • 我已经更新了答案以包含一个示例,希望对您有所帮助。
    • 谢谢你的小故障 - 这确实有助于我的理解!
    猜你喜欢
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2011-01-20
    • 2017-04-10
    • 2019-12-14
    相关资源
    最近更新 更多