Java 比较具有不同精度级别的 Date 对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1671001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:58:38  来源:igfitidea点击:

Compare Date objects with different levels of precision

javajunit

提问by brainimus

I have a JUnit test that fails because the milliseconds are different. In this case I don't care about the milliseconds. How can I change the precision of the assert to ignore milliseconds (or any precision I would like it set to)?

我有一个 JUnit 测试失败,因为毫秒不同。在这种情况下,我不关心毫秒。如何更改断言的精度以忽略毫秒(或我希望设置的任何精度)?

Example of a failing assert that I would like to pass:

我想通过的失败断言示例:

Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);
assertEquals(dateOne, dateTwo);

采纳答案by Joachim Sauer

Use a DateFormatobject with a format that shows only the parts you want to match and do an assertEquals()on the resulting Strings. You can also easily wrap that in your own assertDatesAlmostEqual()method.

使用DateFormat格式仅显示要匹配的部分的对象,assertEquals()并对结果字符串执行 an 。您也可以轻松地将其包装在您自己的assertDatesAlmostEqual()方法中。

回答by Michael Easter

I don't know if there is support in JUnit, but one way to do it:

我不知道 JUnit 是否支持,但有一种方法可以做到:

import java.text.SimpleDateFormat;
import java.util.Date;

public class Example {

    private static SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");

    private static boolean assertEqualDates(Date date1, Date date2) {
        String d1 = formatter.format(date1);            
        String d2 = formatter.format(date2);            
        return d1.equals(d2);
    }    

    public static void main(String[] args) {
        Date date1 = new Date();
        Date date2 = new Date();

        if (assertEqualDates(date1,date2)) { System.out.println("true!"); }
    }
}

回答by Seth

You could do something like this:

你可以这样做:

assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));

No String comparisons needed.

不需要字符串比较。

回答by Esko

Yet another workaround, I'd do it like this:

另一种解决方法,我会这样做:

assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);

回答by Dan Watt

There are libraries that help with this:

有一些库可以帮助解决这个问题:

Apache commons-lang

Apache 公共语言

If you have Apache commons-langon your classpath, you can use DateUtils.truncateto truncate the dates to some field.

如果您的类路径上有Apache commons-lang,您可以使用DateUtils.truncate将日期截断到某个字段。

assertEquals(DateUtils.truncate(date1,Calendar.SECOND),
             DateUtils.truncate(date2,Calendar.SECOND));

There is a shorthand for this:

有一个简写:

assertTrue(DateUtils.truncatedEquals(date1,date2,Calendar.SECOND));

Note that 12:00:00.001 and 11:59:00.999 would truncate to different values, so this might not be ideal. For that, there is round:

请注意, 12:00:00.001 和 11:59:00.999 会截断为不同的值,因此这可能并不理想。为此,有圆:

assertEquals(DateUtils.round(date1,Calendar.SECOND),
             DateUtils.round(date2,Calendar.SECOND));

AssertJ

断言J

Starting with version 3.7.0, AssertJadded an isCloseToassertions, if you are using the Java 8 Date / Time API.

从 3.7.0 版开始,如果您使用的是 Java 8 Date / Time API ,AssertJ添加了一个isCloseTo断言。

LocalTime _07_10 = LocalTime.of(7, 10);
LocalTime _07_42 = LocalTime.of(7, 42);
assertThat(_07_10).isCloseTo(_07_42, within(1, ChronoUnit.HOURS));
assertThat(_07_10).isCloseTo(_07_42, within(32, ChronoUnit.MINUTES));

It also works with legacy java Dates as well:

它也适用于旧的 Java 日期:

Date d1 = new Date();
Date d2 = new Date();
assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());

回答by Ophidian

This is actually a harder problem than it appears because of the boundary cases where the variance that you don't care about crosses a threshold for a value you are checking. e.g. the millisecond difference is less than a second but the two timestamps cross the second threshold, or the minute threshold, or the hour threshold. This makes any DateFormat approach inherently error-prone.

这实际上是一个比看起来更难的问题,因为在边界情况下,您不关心的方差超过了您正在检查的值的阈值。例如,毫秒差异小于一秒,但两个时间戳超过第二个阈值、分钟阈值或小时阈值。这使得任何 DateFormat 方法本质上都容易出错。

Instead, I would suggest comparing the actual millisecond timestamps and provide a variance delta indicating what you consider an acceptable difference between the two date objects. An overly verbose example follows:

相反,我建议比较实际的毫秒时间戳并提供一个方差增量,表明您认为两个日期对象之间可接受的差异。一个过于冗长的例子如下:

public static void assertDateSimilar(Date expected, Date actual, long allowableVariance)
{
    long variance = Math.abs(allowableVariance);

    long millis = expected.getTime();
    long lowerBound = millis - allowableVariance;
    long upperBound = millis + allowableVariance;

    DateFormat df = DateFormat.getDateTimeInstance();

    boolean within = lowerBound <= actual.getTime() && actual.getTime() <= upperBound;
    assertTrue(MessageFormat.format("Expected {0} with variance of {1} but received {2}", df.format(expected), allowableVariance, df.format(actual)), within);
}

回答by Bino Manjasseril

Something like this might work:

像这样的事情可能会奏效:

assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne),
                   new SimpleDateFormat("dd MMM yyyy").format(dateTwo));

回答by Torbj?rn ?sterdahl

Using JUnit 4 you could also implement a matcherfor testing dates according to your chosen precision. In this example the matcher takes a string format expression as a parameter. The code is not any shorter for this example. However the matcher class may be reused; and if you give it a describing name you can document the intention with the test in an elegant way.

使用 JUnit 4,您还可以根据您选择的精度为测试日期实现匹配器。在此示例中,匹配器将字符串格式表达式作为参数。此示例的代码并不短。但是匹配器类可以重用;如果你给它一个描述性的名字,你可以用优雅的方式记录测试的意图。

import static org.junit.Assert.assertThat;
// further imports from org.junit. and org.hamcrest.

@Test
public void testAddEventsToBaby() {
    Date referenceDate = new Date();
    // Do something..
    Date testDate = new Date();

    //assertThat(referenceDate, equalTo(testDate)); // Test on equal could fail; it is a race condition
    assertThat(referenceDate, sameCalendarDay(testDate, "yyyy MM dd"));
}

public static Matcher<Date> sameCalendarDay(final Object testValue, final String dateFormat){

    final SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);

    return new BaseMatcher<Date>() {

        protected Object theTestValue = testValue;


        public boolean matches(Object theExpected) {
            return formatter.format(theExpected).equals(formatter.format(theTestValue));
        }

        public void describeTo(Description description) {
            description.appendText(theTestValue.toString());
        }
    };
}

回答by Gabriel Belingueres

In JUnit you can program two assert methods, like this:

在 JUnit 中,您可以编写两个断言方法,如下所示:

public class MyTest {
  @Test
  public void test() {
    ...
    assertEqualDates(expectedDateObject, resultDate);

    // somewhat more confortable:
    assertEqualDates("01/01/2012", anotherResultDate);
  }

  private static final String DATE_PATTERN = "dd/MM/yyyy";

  private static void assertEqualDates(String expected, Date value) {
      DateFormat formatter = new SimpleDateFormat(DATE_PATTERN);
      String strValue = formatter.format(value);
      assertEquals(expected, strValue);
  }

  private static void assertEqualDates(Date expected, Date value) {
    DateFormat formatter = new SimpleDateFormat(DATE_PATTERN);
    String strExpected = formatter.format(expected);
    String strValue = formatter.format(value);
    assertEquals(strExpected, strValue);
  }
}

回答by Oliver Hernandez

Just compare the date parts you're interested in comparing:

只需比较您有兴趣比较的日期部分:

Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);

assertEquals(dateOne.getMonth(), dateTwo.getMonth());
assertEquals(dateOne.getDate(), dateTwo.getDate());
assertEquals(dateOne.getYear(), dateTwo.getYear());

// alternative to testing with deprecated methods in Date class
Calendar calOne = Calendar.getInstance();
Calendar calTwo = Calendar.getInstance();
calOne.setTime(dateOne);
calTwo.setTime(dateTwo);

assertEquals(calOne.get(Calendar.MONTH), calTwo.get(Calendar.MONTH));
assertEquals(calOne.get(Calendar.DATE), calTwo.get(Calendar.DATE));
assertEquals(calOne.get(Calendar.YEAR), calTwo.get(Calendar.YEAR));