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

Java 比较具有不同精度级别的日期对象,java,junit,Java,Junit,我有一个JUnit测试失败,因为毫秒不同。在这种情况下,我不关心毫秒。如何将断言的精度更改为忽略毫秒(或我希望将其设置为的任何精度) 我希望通过的失败断言示例: Date dateOne = new Date(); dateOne.setTime(61202516585000L); Date dateTwo = new Date(); dateTwo.setTime(61202516585123L); assertEquals(dateOne, dateTwo); 使用仅显示要匹配的部分的格式

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

我希望通过的失败断言示例:

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

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

我不知道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!"); }
    }
}

你可以这样做:

assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
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);
  }
}

无需进行字符串比较。

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

assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
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);
  }
}
assertTrue(“日期之间的距离不够近!”,(date2.getTime()-date1.getTime())<1000);

有一些库可以帮助实现这一点:

Apache commons lang

如果类路径上有,可以使用将日期截断为某个字段

assertEquals(DateUtils.truncate(date1,Calendar.SECOND),
             DateUtils.truncate(date2,Calendar.SECOND));
这里有一个速记:

assertTrue(DateUtils.truncatedEquals(date1,date2,Calendar.SECOND));
请注意,12:00:00.001和11:59:00.999将截断为不同的值,因此这可能并不理想。为此,有以下几点:

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

从版本3.7.0开始,如果您使用的是Java8日期/时间API,则添加了断言

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));
它也适用于传统的java日期:

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

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

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

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);
}
public static void assertdatesimilor(预期日期、实际日期、长期允许差异)
{
长方差=数学绝对值(允许方差);
long millis=预期的.getTime();
长下限=毫秒-允许方差;
长上限=毫秒+允许方差;
DateFormat df=DateFormat.getDateTimeInstance();

boolean in=lowerBound类似的方法可能会起作用:

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

使用JUnit 4,您还可以根据选择的精度实现用于测试日期的matcher。在本例中,matcher将字符串格式表达式作为参数。本例中的代码不会更短。但是matcher类可以重用;如果您给它一个描述名称,则可以使用以一种优雅的方式

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());
        }
    };
}
import static org.junit.Assert.assertThat;
//从org.junit.和org.hamcrest进一步导入。
@试验
公共无效测试Deventstobaby(){
日期引用日期=新日期();
//做点什么。。
日期testDate=新日期();
//断言(referenceDate,equalTo(testDate));//对equal的测试可能失败;这是一个竞争条件
资产(参考日期,同一贷款日(测试日期,yyyy-MM-dd));
}
公共静态匹配器SameCalenday(最终对象测试值,最终字符串日期格式){
最终SimpleDataFormat格式化程序=新SimpleDataFormat(日期格式);
返回新的BaseMatcher(){
受保护对象测试值=测试值;
公共布尔匹配(对象为预期){
返回formatter.format(theExpected).equals(formatter.format(theTestValue));
}
公共无效说明(说明){
description.appendText(testValue.toString());
}
};
}

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

assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
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);
  }
}

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

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

如果您使用的是Joda,则可以使用。

我将对象转换为java.util.Date并进行比较

assertEquals((Date)timestamp1,(Date)timestamp2);

您可以创建一个小型协作者,而不是直接使用
newdate
,您可以在测试中模拟该协作者:

public class DateBuilder {
    public java.util.Date now() {
        return new java.util.Date();
    }
}
创建DateBuilder成员,并将调用从
newdate
更改为
DateBuilder.now()

主要方法将产生:

Dates are the same: false
在测试中,您可以插入
DateBuilder
的存根,并让它返回您喜欢的任何值。例如,使用Mockito或重写
now()
的匿名类:


对Joda时间()使用AssertJ断言

测试失败消息更具可读性

java.lang.AssertionError: 
Expecting:
  <2014-07-28T08:00:00.000+08:00>
to have same year, month, day, hour, minute and second as:
  <2014-07-28T08:10:00.000+08:00>
but had not.
java.lang.AssertionError:
期望:
具有相同的年、月、日、时、分和秒:
但是没有。

使用SimpleDateFromat将日期转换为字符串,在构造函数中指定所需的日期/时间字段,并比较字符串值:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String expectedDate = formatter.format(dateOne));
String dateToTest = formatter.format(dateTwo);
assertEquals(expectedDate, dateToTest);

我做了一个小类,可能对一些在这里结束的Google有帮助:

< PUnit有一个内置的断言,用于比较双倍,并且指定它们需要的距离。在这种情况下,δ在你认为日期相等的多少毫秒内。这个解决方案没有边界条件,测量绝对方差,可以EA。只需指定精度,不需要编写额外的库或代码

    Date dateOne = new Date();
    dateOne.setTime(61202516585000L);
    Date dateTwo = new Date();
    dateTwo.setTime(61202516585123L);
    // this line passes correctly 
    Assert.assertEquals(dateOne.getTime(), dateTwo.getTime(), 500.0);
    // this line fails correctly
    Assert.assertEquals(dateOne.getTime(), dateTwo.getTime(), 100.0);

注意它必须是100.0而不是100(或者需要转换为双精度),才能强制将它们作为双精度进行比较。

在比较日期时,您可以选择所需的精度级别,例如:

LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
// e.g. in MySQL db "timestamp" is without fractional seconds precision (just up to seconds precision)
assertEquals(myTimestamp, now);

这是一个实用函数,它为我完成了这项工作

    private boolean isEqual(Date d1, Date d2){
        return d1.toLocalDate().equals(d2.toLocalDate());
    }


使用AssertJ,您可以提供一个自定义比较器,如果您比较的是整个对象结构而不是单个值,则该比较器特别方便,这样其他方法,如
IsequalIggningMillis
isCloseTo
就不实用了

assertThat(东西)
.使用递归比较()
.withComparatorForType(
(a,b)->a.truncatedTo(ChronoUnit.MILLIS)。与(b.truncatedTo)相比