SimpleDateFormat 有时会错误的小时格式

SimpleDateFormat sometimes wrong hours formatting

我想将我的时间戳转换成 XXhours:YYminutes 字符串。我的方法有时可以正常工作,除非它获得了更大的价值。例如,

193500000 -> 05hours:45minutes(错误,正确的是53hours:45minutes)
60300000 -> 16hours:45minutes(正确)
63900000 -> 17hours:45minutes(正确)
108000000-> 06hours:00minutes(错误,正确的是30hours:00minutes)
117000000 -> 08hours:30minutes(错误,正确的是32hours:30minutes)

    SimpleDateFormat dateFormat = new SimpleDateFormat("HH'hours' mm'minutes'");
    dateFormat.setTimeZone( TimeZone.getTimeZone( "GMT"));
    String string  = dateFormat.format(totalTime);

193500000 -> 05hours:45minutes(错误,正确的是53hours:45minutes)
那是因为53小时=2天5小时

60300000 -> 16hours:45minutes(正确)
63900000 -> 17hours:45minutes(正确)

108000000->06hours:00minutes(错误,正确的是30hours:00minutes)
那是因为30小时=1天6小时

117000000 -> 08hours:30minutes(错误,正确的是32hours:30minutes)
那是因为32小时=1天8小时

如果只想计算毫秒数的小时数:

193500000/(1000*60*60)   = number of hours  
(193500000/(1000*60))%60 = number of remaining minutes 

不幸的是,SimpleDateFormat 是不可能的。 "HH" 表示一天中的小时。如果您的日期超过 24 小时,结果将以 24 为模。

我建议你用新的时间编写自定义代码API。

示例如下:

    Duration duration = Duration.ofMillis(193500000);

    long minutes = duration.toMinutes() % 60;
    long hours = duration.toHours();

    System.out.println("" + hours + "hours:"+minutes+"minutes");

您似乎在尝试转换持续时间,而不是日期时间。 SimpleDateFormat 用于将日期时间格式化为不同区域设置的不同格式。

您可能正在寻找类似这样的东西来在持续时间之间进行转换:

String.format("%d hours, %d minutes", 
    TimeUnit.MILLISECONDS.toHours(millis),
    TimeUnit.MILLISECONDS.toMinutes(millis) - 
    TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis))
);

再仔细读一遍:https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

HH 是 中的几个小时,永远不会超过 24 :-)

你想要的大概是这样的:

    int msecPerHour = 3600*1000;
    int hours = totalTime/msecPerHour;
    int msecPerMinute = 60*1000;
    int minutes = totalTime/msecPerMinute - hours*60;
    String str = "" + hours + " hours " + minutes + " minutes";

The answer by talex 是正确的,您应该为此使用 Duration。在 Java 9 中,格式化 Duration 变得更容易,也更易于书写和阅读:

public static String formatDuration(long totalTimeMillis) {
    Duration totalDuration = Duration.ofMillis(totalTimeMillis);
    return String.format(Locale.ENGLISH,
            "%02d hours %02d minutes",
            totalDuration.toHours(),
            totalDuration.toMinutesPart());
}

我们不再需要模运算(或类似运算)来查找会议记录。为了演示我使用问题中的时间值调用了上述方法:

    System.out.println(formatDuration(60300000));
    System.out.println(formatDuration(63900000));
    System.out.println(formatDuration(108000000));
    System.out.println(formatDuration(117000000));
    System.out.println(formatDuration(193500000));

它打印出您要求的结果:

16 hours 45 minutes
17 hours 45 minutes
30 hours 00 minutes
32 hours 30 minutes
53 hours 45 minutes

我在数字和单位之间加了一个space,我觉得这样更易读,如果你不想要它可以去掉它。

格式字符串中的 %02d 说明符可确保您在问题 formatDuration(14700000) 中根据需要获得带前导零的两位数字,例如产生 04 hours 05 minutes.

SimpleDateFormat 用于格式化日期 and/or 一天中的某个时间,而不是持续时间或经过的时间。我说“是”是因为 class 现在已经过时了,而且它也出了名的麻烦,我建议你永远不要再使用它。要格式化日期或一天中的小时,请使用 java.time.

中的 DateTimeFormatter

SimpleDateFormat 是这个要求的错误 class

您的要求是计算持续时间而不是日期时间。 SimpleDateFormatjava.time.format.DateTimeFormatter(现代日期时间 API 的一部分)应该用于表示 date/time/date-time 即表示时间点而不是 period/duration 的时间。记住这一点的经验法则是:

  1. 使用 SimpleDateFormatjava.time.format.DateTimeFormatter 表示您在英语语法时态中使用 since 表示的内容。
  2. 使用 Period and Duration 表示您在英语语法时态中用 for 表示的内容。

勾选The Difference between Since and For – English grammar

为了从毫秒获取持续时间,您可以使用java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation。随着 Java-9 引入了一些更方便的方法。

演示:

import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        Duration duration = Duration.ofMillis(193500000);
        // Default format
        System.out.println(duration);

        // Custom format
        // ####################################Java-8####################################
        String formattedElapsedTime = String.format("%02d hours %02d minutes", duration.toHours(),
                duration.toMinutes() % 60);
        System.out.println(formattedElapsedTime);
        // ##############################################################################

        // ####################################Java-9####################################
        formattedElapsedTime = String.format("%02d hours %02d minutes", duration.toHours(), duration.toMinutesPart());
        System.out.println(formattedElapsedTime);
        // ##############################################################################
    }
}

输出:

PT53H45M
53 hours 45 minutes
53 hours 45 minutes

Trail: Date Time.

了解现代日期时间 API
  • 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它向后移植了大部分 java.time Java 6 和 7 的功能。
  • 如果您正在为 Android 项目工作,并且您的 Android API 水平仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring and