在Java中计算日期/时间差异

173

我想要计算两个日期之间的小时/分钟/秒差。

这是我的代码,但它存在一个小问题:

String dateStart = "11/03/14 09:29:58";
String dateStop = "11/03/14 09:33:43";

// Custom date format
SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss");  

Date d1 = null;
Date d2 = null;
try {
    d1 = format.parse(dateStart);
    d2 = format.parse(dateStop);
} catch (ParseException e) {
    e.printStackTrace();
}    

// Get msec from each, and subtract.
long diff = d2.getTime() - d1.getTime();
long diffSeconds = diff / 1000;         
long diffMinutes = diff / (60 * 1000);         
long diffHours = diff / (60 * 60 * 1000);                      
System.out.println("Time in seconds: " + diffSeconds + " seconds.");         
System.out.println("Time in minutes: " + diffMinutes + " minutes.");         
System.out.println("Time in hours: " + diffHours + " hours."); 

这应该生成:

Time in seconds: 45 seconds.
Time in minutes: 3 minutes.
Time in hours: 0 hours.

但是我得到了这个结果:

Time in seconds: 225 seconds.
Time in minutes: 3 minutes.
Time in hours: 0 hours.

有人能看出我在这里做错了什么吗?


3
类似的问题可以在这里找到:https://dev59.com/RHRB5IYBdhLWcg3wbGxB - Yves Martin
2
有更好的方法:http://stackoverflow.com/a/15541322/562769 - Martin Thoma
2
对于Java 8+,有一种非常简单的方法:https://dev59.com/RHRB5IYBdhLWcg3wbGxB#23176621 - ᴠɪɴᴄᴇɴᴛ
17个回答

235

我更倾向于使用建议的java.util.concurrent.TimeUnit类。

long diff = d2.getTime() - d1.getTime();//as given

long seconds = TimeUnit.MILLISECONDS.toSeconds(diff);
long minutes = TimeUnit.MILLISECONDS.toMinutes(diff); 

13
@Mark 我不同意你的看法。你没有理解问题的要点。尽管他在帖子中使用了“以秒为单位的时间”一词,但他明确表示他并不想要简单的转换,而是想要余数。这比获得接受的答案(采用方法调用,即使在JVM字节码中也只有几个指令)更加低效,更不清晰(它更长,坦率地说,如果某些人觉得在那种情况下“1000”或“60”是神奇数字,那么他们就不是很聪明),而且最重要的是,它不能做到OP想要的。 - Parthian Shot
有一种方法可以编写一个通用的方法来实现这个。请参见https://dev59.com/c3I_5IYBdhLWcg3wBub4#10650881 - Sebastien Lorber
1
如果我想要,如何计算周、月、年之间的差异? - jose920405
@jose920405 在这种情况下,你需要使用Joda时间。 - John Henckel
1
这会给出 OP 指定不想要的答案。 - Peter Lawrey
1
它可能没有回答楼主的问题,但它回答了其他所有人的问题,包括我的。+1 - bertmoog

108

尝试

long diffSeconds = diff / 1000 % 60;  
long diffMinutes = diff / (60 * 1000) % 60; 
long diffHours = diff / (60 * 60 * 1000);

注意:这假设diff是非负的。


1
@vels4j 我假设原始代码的第三行,我刚刚添加了它。谢谢你指出来。 - Peter Lawrey
1
但是diffMinutes仍然是错误的。 - vels4j
1
@vels4j 是什么意思? - Peter Lawrey
1
我已更新答案,请检查。在澄清后请忽略我的编辑。 - vels4j
1
@vels4j 你在说哪一个是错误的?因为6似乎是正确答案,因为OP的问题中也有小时数。 - Peter Lawrey
抱歉,我误解了下面的回答。 - vels4j

46

如果您能够使用外部库,我建议您使用Joda-Time。需要注意的是:

Joda-Time是Java SE 8之前的事实标准日期和时间库。现在建议用户迁移到java.time(JSR-310)。

计算两个日期之间的示例:

Seconds.between(startDate, endDate);
Days.between(startDate, endDate);

10
请注意,如果你正在处理两个java.util.Date对象,则需要使用Days.daysBetween(LocalDate.fromDateFields(startDate), LocalDate.fromDateFields(endDate));来计算它们之间的天数。 - user799188
1
我已经创建了一个如何使用Joda时间的示例:http://stackoverflow.com/a/15541322/562769 - Martin Thoma
4
目前使用JSR-310实现它的方法是什么? - static_rtti

19

尝试使用以下代码友好地表示时间差异(以毫秒为单位):

String friendlyTimeDiff(long timeDifferenceMilliseconds) {
    long diffSeconds = timeDifferenceMilliseconds / 1000;
    long diffMinutes = timeDifferenceMilliseconds / (60 * 1000);
    long diffHours = timeDifferenceMilliseconds / (60 * 60 * 1000);
    long diffDays = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24);
    long diffWeeks = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 7);
    long diffMonths = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 30.41666666));
    long diffYears = timeDifferenceMilliseconds / ((long)60 * 60 * 1000 * 24 * 365);

    if (diffSeconds < 1) {
        return "less than a second";
    } else if (diffMinutes < 1) {
        return diffSeconds + " seconds";
    } else if (diffHours < 1) {
        return diffMinutes + " minutes";
    } else if (diffDays < 1) {
        return diffHours + " hours";
    } else if (diffWeeks < 1) {
        return diffDays + " days";
    } else if (diffMonths < 1) {
        return diffWeeks + " weeks";
    } else if (diffYears < 1) {
        return diffMonths + " months";
    } else {
        return diffYears + " years";
    }
}

2
在这行代码中:long diffYears = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 365)); 你需要稍后将其转换为 long 类型,否则除数是 int 类型并会溢出。可以这样修改:diffYears = (timeDifferenceMilliseconds / ((long)60 * 60 * 1000 * 24 * 365)); - Pavel Niedoba
2
我已经修复了代码。 - Pavel Niedoba

19
自从Java 5版本以后,你可以使用java.util.concurrent.TimeUnit来避免在代码中使用像1000和60这样的魔数(Magic Numbers)。
顺便提一下,在计算时要注意闰秒(leap seconds):一年的最后一分钟可能会额外增加一个闰秒,所以该分钟实际上会持续61秒而不是预期的60秒。ISO规范甚至考虑了可能有61秒的情况。你可以在java.util.Date的javadoc中找到详细信息。

2
除非有充分的商业理由包含那些偏离的闰秒,否则你可以将其视为有趣但不重要的科学好奇心,这样做是相当安全的。 - phatfingers
1
我同意“闰秒”只是一个小技巧。但是夏令时或时区差异呢? - Yves Martin
1
@YvesMartin "时区差异" 看看他的日期格式。没有时间区域的地方。如果他确实想要跨越时区工作,他会有更大的问题。不过,夏令时是一个问题。 - Parthian Shot
2
它确实持续了60秒,而不是预期的59秒。你的意思是它持续了61秒(0..60),而不是预期的60秒(0..59)。;) - Andrea Lazzarotto
1
修正了我的评论...一个经典的拾音/间距问题!感谢指出。 - Yves Martin

11

这里有一个建议,使用 TimeUnit 来获取每个时间部分并格式化它们。

private static String formatDuration(long duration) {
    long hours = TimeUnit.MILLISECONDS.toHours(duration);
    long minutes = TimeUnit.MILLISECONDS.toMinutes(duration) % 60;
    long seconds = TimeUnit.MILLISECONDS.toSeconds(duration) % 60;
    long milliseconds = duration % 1000;
    return String.format("%02d:%02d:%02d,%03d", hours, minutes, seconds, milliseconds);
}

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SSS");
Date startTime = sdf.parse("01:00:22,427");
Date now = sdf.parse("02:06:38,355");
long duration = now.getTime() - startTime.getTime();
System.out.println(formatDuration(duration));

结果为:01:06:15,928


8
这更多是一个数学问题,而非Java问题。
您收到的结果是正确的。这是因为225秒等于3分钟(当进行整数除法时)。您需要做的是:
  • 将其除以1000以获取秒数 -> 剩余的是毫秒
  • 将其除以60以获取分钟数 -> 剩余的是秒数
  • 将其除以60以获取小时数 -> 剩余的是分钟数
或者在java中:
int millis = diff % 1000;
diff/=1000;
int seconds = diff % 60;
diff/=60;
int minutes = diff % 60;
diff/=60;
hours = diff;

7

我知道这是一个旧问题,但是我最终做了与被接受的答案稍有不同的事情。人们谈论TimeUnit类,但没有使用它以OP想要的方式回答。

因此,如果有人错过了它,这里有另一个解决方案;-)

public class DateTesting {
    public static void main(String[] args) {
        String dateStart = "11/03/14 09:29:58";
        String dateStop = "11/03/14 09:33:43";

        // Custom date format
        SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss");  

        Date d1 = null;
        Date d2 = null;
        try {
            d1 = format.parse(dateStart);
            d2 = format.parse(dateStop);
        } catch (ParseException e) {
            e.printStackTrace();
        }    

        // Get msec from each, and subtract.
        long diff = d2.getTime() - d1.getTime();

        long days = TimeUnit.MILLISECONDS.toDays(diff);
        long remainingHoursInMillis = diff - TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(remainingHoursInMillis);
        long remainingMinutesInMillis = remainingHoursInMillis - TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(remainingMinutesInMillis);
        long remainingSecondsInMillis = remainingMinutesInMillis - TimeUnit.MINUTES.toMillis(minutes);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(remainingSecondsInMillis);

        System.out.println("Days: " + days + ", hours: " + hours + ", minutes: " + minutes + ", seconds: " + seconds);
    }
}

虽然只是自己计算时间差也可以做到,但这样做并没有太多意义,我认为 TimeUnit 是一个被高度忽视的类。


2
谢谢!我也是这样想的,但你加速了它 :) - Akbolat SSS

5
使用您的时间差作为构造函数创建一个Date对象,然后使用日历方法获取值。
Date diff = new Date(d2.getTime() - d1.getTime());

Calendar calendar = Calendar.getInstance();
calendar.setTime(diff);
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);

4
这些方法已被弃用。 - djechlin
2
运行良好,但您不能忘记设置时区: Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - amra

4

Java中两个日期之间的差异

从链接中提取了代码

public class TimeDiff {
    /**
     * (For testing purposes)
     *
     */
    public static void main(String[] args) {
        Date d1 = new Date();
        try { Thread.sleep(750); } catch(InterruptedException e) { /* ignore */ }      
        Date d0 = new Date(System.currentTimeMillis() - (1000*60*60*24*3)); // About 3 days ago
        long[] diff = TimeDiff.getTimeDifference(d0, d1);

        System.out.printf("Time difference is %d day(s), %d hour(s), %d minute(s), %d second(s) and %d millisecond(s)\n",
                diff[0], diff[1], diff[2], diff[3], diff[4]);
        System.out.printf("Just the number of days = %d\n",
                TimeDiff.getTimeDifference(d0, d1, TimeDiff.TimeField.DAY));
    }

    /**
     * Calculate the absolute difference between two Date without
     * regard for time offsets
     *
     * @param d1 Date one
     * @param d2 Date two
     * @param field The field we're interested in out of
     * day, hour, minute, second, millisecond
     *
     * @return The value of the required field
     */
    public static long getTimeDifference(Date d1, Date d2, TimeField field) {
        return TimeDiff.getTimeDifference(d1, d2)[field.ordinal()];
    }

    /**
     * Calculate the absolute difference between two Date without
     * regard for time offsets
     *
     * @param d1 Date one
     * @param d2 Date two
     * @return The fields day, hour, minute, second and millisecond
     */
    public static long[] getTimeDifference(Date d1, Date d2) {
        long[] result = new long[5];
        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(TimeZone.getTimeZone("UTC"));
        cal.setTime(d1);

        long t1 = cal.getTimeInMillis();
        cal.setTime(d2);

        long diff = Math.abs(cal.getTimeInMillis() - t1);
        final int ONE_DAY = 1000 * 60 * 60 * 24;
        final int ONE_HOUR = ONE_DAY / 24;
        final int ONE_MINUTE = ONE_HOUR / 60;
        final int ONE_SECOND = ONE_MINUTE / 60;

        long d = diff / ONE_DAY;
        diff %= ONE_DAY;

        long h = diff / ONE_HOUR;
        diff %= ONE_HOUR;

        long m = diff / ONE_MINUTE;
        diff %= ONE_MINUTE;

        long s = diff / ONE_SECOND;
        long ms = diff % ONE_SECOND;
        result[0] = d;
        result[1] = h;
        result[2] = m;
        result[3] = s;
        result[4] = ms;

        return result;
    }

    public static void printDiffs(long[] diffs) {
        System.out.printf("Days:         %3d\n", diffs[0]);
        System.out.printf("Hours:        %3d\n", diffs[1]);
        System.out.printf("Minutes:      %3d\n", diffs[2]);
        System.out.printf("Seconds:      %3d\n", diffs[3]);
        System.out.printf("Milliseconds: %3d\n", diffs[4]);
    }

    public static enum TimeField {DAY,
        HOUR,
        MINUTE,
        SECOND,
        MILLISECOND;
    }
}

1
好的。时间偏移很重要 - 即使基于相同的偏移计算差异,但两个“日历日期”之间的任何夏令时差异也会对结果产生影响。您应该使用java.util.concurrent.TimeUnit来定义常量。 - Yves Martin

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