如何在Java中将毫秒转换为"X分钟,X秒钟"?

630
我想在我的程序中记录用户开始某项任务时的时间,可以使用System.currentTimeMillis()。当用户完成任务后,我将从start变量中减去当前的System.currentTimeMillis(),然后我想以人类可读的格式显示已经过去的时间,例如 "XX小时,XX分钟,XX秒" 或者 "XX分钟,XX秒",因为不太可能需要一个小时的时间。
最佳方法是什么?

5
如果时间超过一个小时,你仍然可以打印出类似于“90分53秒”的内容。 - Peter Lawrey
30个回答

1329
使用 java.util.concurrent.TimeUnit 类:
String.format("%d min, %d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
注意:TimeUnit是Java 1.5规范的一部分,但toMinutes是在Java 1.6中添加的。
要为0-9的值添加前导零,只需执行以下操作:
String.format("%02d min, %02d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
如果不支持TimeUnittoMinutes(例如在 Android API 版本 9 之前),请使用以下方程式:
int seconds = (int) (milliseconds / 1000) % 60 ;
int minutes = (int) ((milliseconds / (1000*60)) % 60);
int hours   = (int) ((milliseconds / (1000*60*60)) % 24);
//etc...

5
月数 = (int)((float)天数 / 30.4368499f); 年数 = (int)((float)天数 / 365.242199f); - Nathan Schwermann
7
我建议使用 mod 60 而不是减去分钟,这样看起来更简洁。然而,需要知道 1 分钟等于 60 秒才能这样做... =) - Per Alexandersson
10
“int hours = (int) ((milliseconds / (10006060)) % 24)”这句话是错误的!应该改为“int hours = (int) (milliseconds / (10006060)) ;”。请注意不要改变原意。 - Muhammad Babar
7
如果有人需要一个视频文件的格式(h:mm:ss):String.format("%01d:%02d:%02d", hours, minutes, seconds); - kazy
2
有一个问题。当毫秒数为59999时,实际上它是1分钟,但它将被计算为59秒,999毫秒会丢失。 - Hexise
显示剩余7条评论

136
根据 @siddhadev 的回答,我编写了一个将毫秒转换成格式化字符串的函数:
   /**
     * Convert a millisecond duration to a string format
     * 
     * @param millis A duration to convert to a string form
     * @return A string of the form "X Days Y Hours Z Minutes A Seconds".
     */
    public static String getDurationBreakdown(long millis) {
        if(millis < 0) {
            throw new IllegalArgumentException("Duration must be greater than zero!");
        }

        long days = TimeUnit.MILLISECONDS.toDays(millis);
        millis -= TimeUnit.DAYS.toMillis(days);
        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);

        StringBuilder sb = new StringBuilder(64);
        sb.append(days);
        sb.append(" Days ");
        sb.append(hours);
        sb.append(" Hours ");
        sb.append(minutes);
        sb.append(" Minutes ");
        sb.append(seconds);
        sb.append(" Seconds");

        return(sb.toString());
    }

2
你应该在那里进行单/多检查,以避免这种情况:1天1小时1分钟1秒钟 - Daniel
12
我们可以使用模运算符替代减法:long days = TimeUnit.MILLISECONDS.toDays(millis); long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24; long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60; long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60; long milliseconds = millis % 1000;还可以使用 String.format 方法: return String.format("%d 天 %d 小时 %d 分钟 %d 秒 %d 毫秒", days, hours, minutes, seconds, milliseconds); - Jose Tepedino

93
long time = 1536259;

return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time));

输出:

25:36:259


1
如果没有其他原因,我最喜欢上述方法的原因是它的简单性!由于我们处理时间和持续时间,我通常使用Joda。例如,如果您有两个DateTime,分别为start和end: Duration dur = new Duration(start, end); long millis = dur.getMillis(); - TechTrip
1
我应该注意到使用Joda格式化程序的两种方法。第一种变化:DateTimeFormat.forPattern("mm:ss:SSS").print(new DateTime(time));或将Duration转换为Period,可以使用Joda PeriodFormatter自动打印。如果不是ISO年表,则转换可能会失去精度。假设由变量duration表示的持续时间。Period period = duration.toPeriod().normalizedStandard(PeriodType.time()); PeriodFormat.getDefault().print(period))输出很棒:1秒和581毫秒,回答了上面的主要问题。 - TechTrip
3
有点晚了 :) 但是除非这是你真实所在的时区,否则你需要在这里使用simpleDateFormat.setTimezone(TimeZone.getTimeZone("GMT"))来设置时区。 - Olle Söderström
1
@OlleSöderström 这里确实存在时区问题。但将其设置为 GMT 或 UTC 会导致所有持续时间小于 1 小时的小时部分为 12 而不是 0。 - Episodex
3
时区并不是唯一的问题......这个答案很误导人,可能会严重困惑Java新手。即使忽略了多余的外部括号,OP问的是如何表示一个持续时间(两个时间点之间的差异,以毫秒为单位测量)。将该持续时间称为“时间”,并将其处理为自1970年1月1日以来的偏移量,仅仅为了格式化其较小的组件,我认为是错误的。更重要的是,早在3年前,这个答案就已经建议使用完全相同的方法(请参见那里的评论以获取更多有关时区问题的详细信息)。 - Amos M. Carpenter
它构建了两个对象,其中一个非常重(日期对象),根据要处理的数据量,这可能会很慢。 - JBoy

38

在Java 8中使用java.time包

Instant start = Instant.now();
Thread.sleep(63553);
Instant end = Instant.now();
System.out.println(Duration.between(start, end));

输出格式为ISO 8601持续时间格式PT1M3.553S(1分钟和3.553秒)。


2
另请参阅 [如何在Java中格式化时间间隔?(例如格式化为H:MM:SS)](https://dev59.com/c3VC5IYBdhLWcg3whRgw) - Vadzim
3
(安卓)需要API级别26。 - Someone Somewhere
2
@SomeoneSomewhere 对于 API 级别低于 26 的 Android,请使用后移版本,请参见 如何在 Android 项目中使用 ThreeTenABP - Ole V.V.

36

嗯... 一秒有多少毫秒?一分钟呢?除法并不难。

int seconds = (int) ((milliseconds / 1000) % 60);
int minutes = (int) ((milliseconds / 1000) / 60);

就这样继续下去,几个小时、几天、几周、几个月、几年、几十年,反正时间可以随意。


3
实际上,如果涉及到夏令时(有23或24小时的日子)或闰年,做超过一小时的时间计算并不是一个好主意,因为结果可能会出现错误/令人费解。如果我看到“X将在1年/1个月内发生”,我期望它是相同的日期和时间。 - Michael Borgwardt
6
System.currentTimeMillis() 方法不受夏令时的影响,因此它不会因为额外或缺失的小时数而导致混淆。如果您需要显示两个特定日期之间的差异,最好构造具有给定时间的 Date 对象,并显示这两个对象之间的差异。 - Bombe
1
超过周数,由于月份长度是可变的,因此未定义。因此,您确实需要相对于给定的时间参考计算。 - PhiLho

30
我不会为此引入额外的依赖项(毕竟除法并不难),但如果您已经在使用Commons Lang,则可以使用DurationFormatUtils
示例用法(改编自这里):
import org.apache.commons.lang3.time.DurationFormatUtils

public String getAge(long value) {
    long currentTime = System.currentTimeMillis();
    long age = currentTime - value;
    String ageString = DurationFormatUtils.formatDuration(age, "d") + "d";
    if ("0d".equals(ageString)) {
        ageString = DurationFormatUtils.formatDuration(age, "H") + "h";
        if ("0h".equals(ageString)) {
            ageString = DurationFormatUtils.formatDuration(age, "m") + "m";
            if ("0m".equals(ageString)) {
                ageString = DurationFormatUtils.formatDuration(age, "s") + "s";
                if ("0s".equals(ageString)) {
                    ageString = age + "ms";
                }
            }
        }
    }
    return ageString;
}   

示例:

long lastTime = System.currentTimeMillis() - 2000;
System.out.println("Elapsed time: " + getAge(lastTime)); 

//Output: 2s

注意: 要从两个LocalDateTime对象获取毫秒值,可以使用以下代码:

long age = ChronoUnit.MILLIS.between(initTime, LocalDateTime.now())

1
太好了,我正想知道是否有一个包含这种东西的强大库。 - Lajcik
由于链接通常不是非常有用的,因此我添加了一个示例,说明它如何使用。 - lepe

28

要么使用手动划分方法,要么使用SimpleDateFormat API

long start = System.currentTimeMillis();
// do your work...
long elapsed = System.currentTimeMillis() - start;
DateFormat df = new SimpleDateFormat("HH 'hours', mm 'mins,' ss 'seconds'");
df.setTimeZone(TimeZone.getTimeZone("GMT+0"));
System.out.println(df.format(new Date(elapsed)));

Bombe 编辑:在评论中已经表明,这种方法只适用于较短的时间段(即少于一天)。


哇,那是一个邪恶的、依赖于时区的黑客技巧。当你有一个不是60分钟倍数的时区偏移量时(而且我们世界上有一些讨厌的30分钟偏移时区),它将无情地崩溃。 - Bombe
同样的,如果你在格式字符串中包含小时并且不在GMT+0时区,由于相同的原因它也会出现错误。 - Bombe
我们需要做什么?真的吗?在哪里?不是怀疑你,只是以前从未听说过 - 有些新的陷阱需要考虑;-) - Treb
是的。请在维基百科上查看“时区列表”,例如尼泊尔位于格林威治标准时间+05:45。 - Bombe
是的,海得拉巴是GMT+5.5:\ - GaZ
2
cadrian,现在它只能用于少于一天的持续时间。小时数将始终介于0和23之间,包括0和23。 - Bombe

24

只是为了提供更多信息,如果您想格式化为:HH:mm:ss

0 <= HH <= 无穷大

0 <= mm < 60

0 <= ss < 60

请使用以下代码:

int h = (int) ((startTimeInMillis / 1000) / 3600);
int m = (int) (((startTimeInMillis / 1000) / 60) % 60);
int s = (int) ((startTimeInMillis / 1000) % 60);

我刚遇到这个问题,现在找到了解决方法


1
最佳答案在这里。为什么每个人都想把事情搞得那么复杂呢?这只是简单的数学问题,伙计们。 - Lambart

14

最简短的解决方案:

这可能是最简短的解决方案,同时也处理了时区问题。

System.out.printf("%tT", millis-TimeZone.getDefault().getRawOffset());

例如,输出如下:
00:18:32

解释:

%tT 是按照24小时制格式化的时间,格式为 %tH:%tM:%tS

%tT 也可以接受长整型作为输入,因此不需要创建一个 Date 对象。 printf() 只会打印以毫秒为单位指定的时间,但是在当前时区,因此我们必须减去当前时区的原始偏移量,这样0毫秒将是0小时,而不是当前时区的时间偏移值。

注意 #1: 如果您需要将结果作为 String 返回,可以像这样获取它:

String t = String.format("%tT", millis-TimeZone.getDefault().getRawOffset());

注意事项 #2:仅当millis小于一天时,此方法才能给出正确的结果,因为输出中不包括天数部分。


这是一个很好的快捷方式,但当我使用它时,我没有得到预期的结果。所以我有:long millis = 8398; 当我将其传递给 String.format("%tT", millis) 时,我的输出是19:00:08。19从哪里来的? - Nelda.techspiress
1
@Nelda.techspiress 这是你所在时区的偏移量。你需要从中减去 TimeZone.getDefault().getRawOffset(),就像答案中写的一样:String.format("%tT", millis-TimeZone.getDefault().getRawOffset()) - icza
处理好了。谢谢您的澄清。 - Nelda.techspiress

13

重新审视 @brent-nash 的贡献,我们可以使用模数函数而非减法,并使用 String.format 方法来生成结果字符串:

  /**
   * Convert a millisecond duration to a string format
   * 
   * @param millis A duration to convert to a string form
   * @return A string of the form "X Days Y Hours Z Minutes A Seconds B Milliseconds".
   */
   public static String getDurationBreakdown(long millis) {
       if (millis < 0) {
          throw new IllegalArgumentException("Duration must be greater than zero!");
       }

       long days = TimeUnit.MILLISECONDS.toDays(millis);
       long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24;
       long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;
       long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;
       long milliseconds = millis % 1000;

       return String.format("%d Days %d Hours %d Minutes %d Seconds %d Milliseconds",
                            days, hours, minutes, seconds, milliseconds);
   }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接