【问题标题】:Java - Incorrect result when convert Millisecond to `HH:mm:ss:SSS` in Netbeans IDEJava - 在 Netbeans IDE 中将毫秒转换为“HH:mm:ss:SSS”时结果不正确
【发布时间】:2017-11-14 23:55:50
【问题描述】:

我正在尝试将5007 转换为HH:mm:ss:SSS,我正在使用Netbeans IDE v8.2

Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date);

但这给了我一个不正确的结果:

01:00:05:007
^^-------------------------why does it print to me 01 in the hours?

但它应该得到:

00:00:05:007
^^-------------------------correct result

例如,当我使用 ideone 时,它会给我一个正确的结果:

对这个问题有什么解释吗?

我测试了两个版本:Java 7 和 Java 8。

【问题讨论】:

  • 我的猜测是它与夏令时有关。时间 0 大概是 1970-01-01 01:00:00:000
  • 可能是不同的jdk?当您映射从纪元到日期的毫秒数时,可能只是不考虑夏令时。我不知道细节,所以我没有把它作为答案发布。
  • @TedCassirer 不,不是不同的 JDK,而是不同的默认时区。所以我猜你是在正确的轨道上。
  • Ideone 使用 GMT 作为其默认时区:ideone.com/9qxi16 并且 OP 的 CET 为 UTC + 1(当前)。
  • 想知道这个问题以前被问过多少次。请通过搜索帮助自己快速找到答案。

标签: java netbeans simpledateformat epoch time-format


【解决方案1】:

当你要求解释你的问题时,我会诚实地告诉你:你滥用SimpleDateFormat 来格式化持续时间。它从来没有用于此用途,并且如果您尝试它,也不是为了给您预期的结果。您遇到的问题并不是您将遇到的唯一问题。如果您的持续时间增长到超过 24 小时,则整日将不会被格式化,因此您的结果将以不同的方式出错。

除此之外,这些天我建议不要使用 SimpleDateFormat 类。它的替代品是java.time.format.DateTimeFormatter,它于 2014 年问世,带来的负面惊喜要少得多。不过,它也不是格式化持续时间的好选择。

那么什么是好的选择呢?我会先给你Java 9的答案,因为它很简单:

        Duration dur = Duration.ofMillis(5007);
        String hmss = String.format("%02d:%02d:%02d:%03d", 
                                    dur.toHours(),
                                    dur.toMinutesPart(), 
                                    dur.toSecondsPart(), 
                                    dur.toMillisPart​());

主要优势甚至不是轻松,而是清晰。例如,有了这段代码,没有人需要问我在评论中提出的问题,是时间点还是持续时间?代码非常清楚地表明它是一个持续时间。

对于 Java 6、7 和 8,您仍然可以使用 Duration 类(在 Java 6 和 7 中通过 ThreeTen Backport),但它不适合格式化,所以我想我会诉诸使用TimeUnit 枚举,如the question I had linked to(您要求我再次删除的链接):

    long millis = 5007;
    long hours = TimeUnit.MILLISECONDS.toHours(millis);
    millis -= TimeUnit.HOURS.toMillis(hours);
    long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
    millis -= TimeUnit.MINUTES.toMillis(minutes);
    long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
    millis -= TimeUnit.SECONDS.toMillis(seconds);
    String hmss = String.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);

结果是我们想要的

00:00:05:007

为了说明我试图说明当持续时间超过 24 小时时会发生什么,让我们尝试给它 100 000 000 毫秒(一天有 86 400 000 毫秒):

27:46:40:000

【讨论】:

  • 我同意。 Duration 将是一个更合适的类。
【解决方案2】:
Date date = new Date(5007);

January 1, 1970, 00:00:00 GMT 之后 5007 毫秒创建一个日期。

当您使用SimpleDateFormat 对其进行格式化时,它会转换为您机器的时区(我假设是 GMT+1)。

当我运行你的代码时,我得到了

02:00:05:007

我的时区是 GMT+2。

如果您将DateFormat 实例的时区设置为GMT,您将获得预期的输出:

Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
formatter.setTimeZone (TimeZone.getTimeZone ("GMT"));
String dateFormatted = formatter.format(date);
System.out.println (dateFormatted);

打印出来

00:00:05:007

【讨论】:

  • 谢谢@Eran,那么您建议将毫秒转换为HH:mm:ss:SSS 格式还是我应该做一些类似stackoverflow.com/a/16520928/5558072 的事情?
  • 将时区设置为 GMT 应该可以。 docs.oracle.com/javase/7/docs/api/java/text/…
  • 是的,它现在可以工作了@DavideSpataro,谢谢你,我很感激
  • @YCF_L 您是否希望date 变量实际包含由毫秒偏移量表示的Date,但在您自己的本地时区中,还是只想将其打印为@987654334 @?如果是后者,只需添加formatter.setTimeZone (TimeZone.getTimeZone ("GMT"));,如我的答案所示。
【解决方案3】:

这绝对是一个语言环境问题。当您不指定一个时,它将使用(您的系统)默认的一个。覆盖它;

尝试添加这个

formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

或在构造 SimpleDateFormat 实例时传递区域设置。例如:

DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS", Locale.ENGLISH); 

我假设 Locale.ENGLISH 有 UTC 时区顺便说一句,但我希望你明白。

【讨论】:

  • 提供一个明确的语言环境是个好主意,它可以让读者更清楚你的意图。不过,在这种情况下,它对输出几乎没有任何影响。
猜你喜欢
  • 2019-02-07
  • 2016-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
相关资源
最近更新 更多