CodeReview:java日期差异(以天分辨率)

时间:2009-05-19 16:47:53

标签: java datetime

请注意以下代码。

我需要计算2个Date对象之间的差异。确保两个Date对象都在同一个TimeZone中。

public class DateUtils {
public final static long DAY_TIME_IN_MILLIS = 24 * 60 * 60 * 1000;

/**
 * Compare between 2 dates in day resolution.
 * 
 * @return positive integer if date1 > date2, negative if date1 < date2. 0 if they are equal.
 */
public static int datesDiffInDays(final Date date1, final Date date2){
    long date1DaysMS = date1.getTime() - (date1.getTime() % DAY_TIME_IN_MILLIS);
    long date2DaysMS = date2.getTime() - (date2.getTime() % DAY_TIME_IN_MILLIS);

    long timeInMillisDiff = (date1DaysMS - date2DaysMS);
    int ret = (int) (timeInMillisDiff / DAY_TIME_IN_MILLIS); 
    return ret;
}

你能指出一个我可能错过的问题吗?

编辑:@mmyers问我是否通过了我的单元测试。嗯,是。但我没有真正的约会经验,我知道这是一个很大的主题。发布在我正在使用的单元测试下面。

public class TestMLDateUtils {

@Test
public final void testDatesDiffInDays() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

    // 00:00:00.000 1.1.1970
    Calendar cal1970 = Calendar.getInstance();
    cal1970.setTimeInMillis(0);

    Calendar tested = Calendar.getInstance();
    tested.setTimeInMillis(0);

    // Add 1 millisecond, date = 00:00:00.001 1.1.1970
    tested.add(Calendar.MILLISECOND, 1);

    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);

    // Add 1 second, date = 00:00:01.001 1.1.1970
    tested.add(Calendar.SECOND, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);

    // Add 1 minute, date = 00:01:01.001 1.1.1970
    tested.add(Calendar.MINUTE, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);

    // Add 1 hour, date = 01:01:01.001 1.1.1970
    tested.add(Calendar.HOUR_OF_DAY, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);

    // date = 23:59:59.999 1.1.1970
    tested.setTimeInMillis(0);
    tested.add(Calendar.MILLISECOND, 999);
    tested.add(Calendar.SECOND, 59);
    tested.add(Calendar.MINUTE, 59);
    tested.add(Calendar.HOUR_OF_DAY, 23);
    //System.out.println("D: " + tested.getTime());
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);

    // date = 00:00:00.000 2.1.1970
    tested.setTimeInMillis(0);
    tested.add(Calendar.DAY_OF_MONTH, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);

    // date = 00:00:00.001 2.1.1970
    tested.add(Calendar.MILLISECOND, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);

    // date = 00:00:01.001 2.1.1970
    tested.add(Calendar.SECOND, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);

    // date = 00:01:01.001 2.1.1970
    tested.add(Calendar.MINUTE, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);

    // date = 01:01:01.001 2.1.1970
    tested.add(Calendar.HOUR_OF_DAY, 1);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);

    // date = 13:01:01.001 2.1.1970
    tested.add(Calendar.HOUR_OF_DAY, 12);
    assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
    assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
}
}

5 个答案:

答案 0 :(得分:8)

  • 即时问题:由于夏令时变化,天数可能少于或超过24小时。

  • 次要问题:通常当人们在几天内思考时,他们的确意味着“人类的日子”,而不是“24小时的时间”。换句话说,很多人会说第二天晚上7点到7点是一天的差异,而同一天早上7点到晚上7点是零天的差异。两者都是12个小时。此时,您确实需要知道正在考虑的日历。

当然,这对你的情况可能无关紧要,但我们真的不知道那是什么。

  • 第三个问题:您使用的是内置日历API,而不是Joda Time。这几乎从来都不是一个好主意 - 它是可怕的,充满了陷阱和问题。是的,这里的常客会告诉你,当涉及到Java日期和时间时,总是我的答案的一部分 - 并且有充分的理由。这真的很重要。

编辑:您的测试将默认时区设置为UTC。这不是一个好主意(特别是没有在finally语句中重置它)。时区很棘手,但你应该考虑一下你有什么价值,它们意味着什么,以及涉及的时区。

答案 1 :(得分:1)

由于您正在使用getTime(),因此Date对象中的时区(如果有)无关紧要。 “[r] eturns自1970年1月1日00:00:00 GMT以来该Date对象所代表的毫秒数。”

但是,您没有考虑闰秒,某些实现可能会返回。因此,如果您给出的日期范围中有一个或多个闰秒,并且您的时间接近一天中的同一时间,那么您的计算可能会错误一天。也就是说,似乎没有办法看看是否有任何特定的实现占闰秒(我希望大多数都没有),不管怎样,这种差异相当微不足道。

答案 2 :(得分:1)

尚未提及的另一个问题是leap seconds。由于UTC时间的调整,天数可能会多于或少于24 * 60 * 60秒,以使其与平均太阳年或多或少保持同步。对你的使用可能没什么大不了的,但你至少应该意识到这种可能性。

如果您有处理日期和时间的非平凡要求,那么您需要一个好的API。在Jon Skeet的答案中,Joda Time的链接似乎已被破坏,因此here is a link确实有效。

答案 3 :(得分:1)

代码审核有很多方面;而不是正确性,由他人解决,让我关注一点风格。这当然比专注于正确性的评论更为主观。

我会内联“ret”变量。它增加了方法的大小而没有增强可读性。

我会考虑将毫秒和天之间的转换分成单独的函数。您的全班可能会在多个地方执行该划分。即使没有,它也很有用,因为命名只能做一件事的函数更容易。

说到命名,我会重命名这个函数,也许是“dayDifference” - 缩写引起很多问题,其中最重要的是难以记住在哪种情况下使用哪个缩写。如果你不使用任何一个,那么就会消除这种混乱的特殊根源。同样,我会将常量重命名为MILLISECONDS_PER_DAY

答案 4 :(得分:-4)

Date已经有了这个方法,在Javadoc中查找Date.compareTo(Date)。