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

CodeReview:java日期差异(日分辨率),java,datetime,Java,Datetime,请您对以下代码发表意见 我需要计算两个日期对象之间的差异(以天为单位)。可以确保两个日期对象位于同一时区内 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,

请您对以下代码发表意见

我需要计算两个日期对象之间的差异(以天为单位)。可以确保两个日期对象位于同一时区内

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;
}

Date已具有此方法,请在Javadoc中查找Date.compareTo(日期)。

  • 直接问题:由于夏令时的变化,一天可能少于或超过24小时

  • 第二个问题:通常当人们以天为单位思考时,他们实际上指的是“人类的日子”,而不是“24小时的时段”。换句话说,很多人会说,第二天晚上7点到早上7点是一天的差异,而同一天早上7点到晚上7点是零天的差异。都是12小时。在这一点上,您确实需要知道正在考虑的日历

当然,这可能与你的情况无关,但我们并不知道这是什么

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

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

日期对象中的时区(如果有的话)是不相关的,因为你使用的是
getTime()
;“[r]返回此日期对象表示的自1970年1月1日00:00:00 GMT以来的毫秒数。”


但是,您没有考虑闰秒,有些实现可能会返回闰秒。因此,如果您给出的日期范围内有一个或多个闰秒,并且您的时间与一天中的同一时间足够接近,那么您的计算可能会错误一天。这就是说,似乎没有办法确定是否有任何特定的实现涉及闰秒(我希望大多数实现都不涉及闰秒),而且差别非常小。

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


如果您对处理日期和时间有非常重要的要求,那么您需要一个好的API。Jon Skeet的答案中与Joda Time的链接似乎已断开,因此这确实有效。

代码审查有许多方面;让我来关注一下风格,而不是别人所说的正确性。当然,这比专注于正确性的审查更具主观性

我将内联“ret”变量。它在不增强可读性的情况下增加了方法的大小

<>我会考虑把毫秒和天之间的转换分离成一个单独的函数。你的全班同学可能会在多个地方进行这种划分。即使没有,它也很有用,因为它更容易命名只做一件事的函数


说到命名,我会将函数重命名为“dayDifference”-缩写会导致许多问题,其中最重要的问题是难以记住在哪种情况下使用了哪种缩写。如果您不使用任何选项,则会消除特定的混淆源。类似地,我会将常量重命名为
millizes\u PER_DAY

它通过单元测试了吗?发布代码审查问题以解决常见问题不合适吗?不,此方法专门以天数返回差异。compareTo()没有指定正负数。compareTo()适用于完整日期(最多相差毫秒)。我需要以天为单位解析答案。因此,例如19.5.2009 20:01:28.555与19.5.2009 21:01:28.555相比,应该是0。与20.5.2009 20:01:28.555相比,19.5.2009 20:01:28.555应该是1compareTo()最有可能只返回-1、0或1,这取决于所讨论的对象是否小于、等于或大于方法的参数。它只是没有义务这么做。那么,与2009年5月21日10:10:10:100相比,2009年5月19日20:01:28:555的回报率是多少?1还是2?问题的声明和代码注释使我相信它是compareTo();谢谢你的详细回答。说得好。关于乔达时间-我知道这个图书馆,很遗憾不能使用。重新评价另外两点-我需要考虑一下。
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);
}
}