सटीक के विभिन्न स्तरों के साथ दिनांक वस्तुओं की तुलना करें


81

मेरे पास एक JUnit परीक्षण है जो विफल हो जाता है क्योंकि मिलीसेकंड अलग हैं। इस मामले में मुझे मिलीसेकंड की परवाह नहीं है। मिलीसेकंड को अनदेखा करने के लिए (या मैं इसे कैसे सेट करना चाहूंगा) किसी भी सटीकता को कैसे बदल सकता है?

एक असफल दावे का उदाहरण जो मैं पारित करना चाहता हूं:

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

जवाबों:


22

DateFormatएक प्रारूप के साथ एक ऑब्जेक्ट का उपयोग करें जो केवल उन हिस्सों को दिखाता है जिन्हें आप मिलान करना चाहते हैं और assertEquals()परिणामी स्ट्रिंग्स पर करते हैं। आप इसे आसानी से अपनी assertDatesAlmostEqual()विधि में भी लपेट सकते हैं ।


15
एक दूसरी सीमा पर मिलीसेकंड अंतर के मामले को संभालना नहीं है, 10.000 और 09.999 अलग होगा।
scarba05

61

अभी तक एक और समाधान, मैं इसे इस तरह करना होगा:

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

4
विचरण की तुलना करने के लिए +1, लेकिन पूर्ण विचरण के लिए खाता नहीं है (जैसे कि date1 तारीख 2 के बाद क्या है?)
Ophidian

13
मैं आमतौर पर सिर्फ रैपिंग के लिए ऐसा ही तरीका
अपनाता हूं। मैथ.ब्स

60

इसमें मदद के लिए पुस्तकालय हैं:

अपाचे कॉमन-लैंग

यदि आपके पास अपाचे कॉमन-लैंग है , तो आप अपने क्लासपैथ पर ले जा सकते हैं, आप DateUtils.truncateतिथियों का उपयोग किसी क्षेत्र में करने के लिए कर सकते हैं ।

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

असरज

यदि आप Java 8 Date / Time API का उपयोग कर रहे हैं, तो संस्करण 3.7.0 से शुरू होकर, 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));

यह विरासत जावा तिथियों के साथ भी काम करता है:

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

यह वह उपाय है जिसकी मुझे तलाश थी :)
geoaxis

1
धन्यवाद इसने मुझे एक टन बचा लिया!
रॉबर्ट बेल्ट्रान

क्यों DateUtils.round का इस्तेमाल नहीं करते?
डोमिन

1
राउंड भी काम करेगा। यह ऊपर या नीचे गोल होगा, जबकि ट्रंकट हमेशा नीचे जाएगा। प्रति डॉक्स , गोल भी डेलाइट सेविंग टाइम संभालती है।
दान वाट

मेरे पास एक ही मुद्दा था java.sql.Timestampsऔर DateUtils.truncate(...)जावा 8 में मेरे लिए काम किया। मेरे विशेष मामले में एक डेटाबेस तकनीक शामिल थी जो एक सेकंड की तुलना में किसी भी महीन अनाज को बचाने में मदद नहीं करती थी, इसलिए मैं एक इन-मेमोरी टाइमस्टैम्प की तुलना कर रहा था, जिसे बचाया गया था और डेटाबेस से पुनर्प्राप्त किया गया। इन-मेमोरी टाइमस्टैम्प में डेटाबेस से पढ़े गए टाइमस्टैम्प की तुलना में अधिक सटीकता थी।
केंट बुल

6

आप ऐसा कुछ कर सकते हैं:

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

कोई स्ट्रिंग तुलना की जरूरत है।


मुझे लगता है कि आपका मतलब "/" बनाम "%" था? यह मनमानी परिशुद्धता, IMHO के बारे में गड़बड़ हो जाता है। हालांकि अच्छी बात है।
माइकल ईस्टर

वूप्स! अच्छी पकड़। मुझे नहीं लगता कि परिशुद्धता एक मुद्दा है। Date.getTime () हमेशा युग के बाद से एमएस का एक लंबा रिटर्न देता है।
सेठ

1
यह विफल हो जाएगा यदि एक मान 3.999 सेकंड और दूसरा 4.000 है। आसान शब्द, कभी-कभी यह एक सेकंड तक अंतर को सहन करेगा, कभी-कभी यह 2 एमएस के अंतर के लिए विफल हो जाएगा।
डेविड बालैसिक

6

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

4

मुझे पता नहीं है कि 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!"); }
    }
}

यदि आप विधि कहते हैं, assertEqualDatesतो मैं इसका रिटर्न प्रकार बनाऊंगा voidऔर अंतिम पंक्ति बनाऊंगा assertEquals(d1, d2)। इस तरह यह JUnit के सभी assert*तरीकों के समान होगा ।
जोआचिम सॉउर

माना। मैं कोड चलाना चाहता था और हाथ में JUnit नहीं था।
माइकल ईस्टर

1
ग्लोबल डेट फॉर्मेटर्स से सावधान रहें। वे धागा-सुरक्षित नहीं हैं। यह इस कोड के साथ कोई समस्या नहीं है, लेकिन यह एक बुरी आदत है।
इसका नाडोक

1
यह उस मामले को नहीं संभालता है जहाँ दो दिनांक ऑब्जेक्ट में एक उप-दूसरा अंतर होता है, लेकिन वे दूसरी सीमा पार करते हैं।
ओफिडियन

3

यह वास्तव में एक कठिन समस्या है क्योंकि यह सीमा के मामलों के कारण प्रकट होता है, जहां आप जिस संस्करण की परवाह नहीं करते हैं, वह आपके द्वारा जांचे जा रहे मूल्य के लिए सीमा से अधिक नहीं है। उदाहरण के लिए मिलीसेकंड का अंतर एक सेकंड से कम है लेकिन दो टाइमस्टैम्प दूसरी सीमा या मिनट सीमा या घंटे की सीमा को पार करते हैं। यह किसी भी 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);
}

2

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

2

Joda-Time ( http://joel-costigliola.github.io/assertj/assertj-joda-time.html ) के लिए AssertJ मुखर का उपयोग करें

import static org.assertj.jodatime.api.Assertions.assertThat;
import org.joda.time.DateTime;

assertThat(new DateTime(dateOne.getTime())).isEqualToIgnoringMillis(new DateTime(dateTwo.getTime()));

परीक्षण विफल संदेश अधिक पठनीय है

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.

1
AssertJ java.util.date के लिए भी काम करता है:assertThat(new Date(2016 - 1900, 0, 1,12,13,14)).isEqualToIgnoringMillis("2016-01-01T12:13:14");
Dan Watt

1

यदि आप Joda का उपयोग कर रहे थे तो आप Fest Joda Time का उपयोग कर सकते थे ।


3
क्या आप अधिक जानकारी प्रदान कर सकते हैं कि इसे कैसे लागू किया जाना चाहिए? और यह एक टिप्पणी में परिवर्तित किया जाना चाहिए।
ह्यूगो डोजो

1

बस उन भागों की तुलना करें जिनकी आप तुलना करना चाहते हैं:

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

मुझे यह तरीका बहुत अच्छा लगता है फिर डेट फॉर्मेटर का उपयोग करना। केवल समस्या यह है कि दिनांक में विशिष्ट गेट्टर फ़ील्ड हटाए गए हैं। एक ही काम करने के लिए कैलेंडर का उपयोग करना बेहतर है।
kfox

आह, ध्यान देने योग्य बात यह है कि उन तरीकों को पदावनत किया जाता है। मैंने कैलेंडर ऑब्जेक्ट को बदलने और तुलना करने के लिए वैकल्पिक कोड के साथ अपना उत्तर अपडेट किया है।
ओलिवर हर्नांडेज़

1

JUnit में डबल्स की तुलना करने के लिए एक जोर-शोर से बनाया गया है, और यह निर्दिष्ट करने के लिए कि उन्हें कितना करीब होना चाहिए। इस स्थिति में, डेल्टा के भीतर आप कितने मिलिसेकंड को तारीखों के बराबर मानते हैं। इस समाधान की कोई सीमा नहीं है, पूर्ण विचरण को मापता है, आसानी से सटीक निर्दिष्ट कर सकता है, और इसके लिए कोई अतिरिक्त पुस्तकालय या कोड लिखने की आवश्यकता नहीं है।

    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 के बजाय 100.0 होना चाहिए (या डबल्स की आवश्यकता है) इसे डबल्स के रूप में तुलना करने के लिए मजबूर करना चाहिए।


1

आप तारीखों की तुलना करते समय कौन सा सटीक स्तर चुन सकते हैं, जैसे:

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


0

new Dateसीधे उपयोग करने के बजाय , आप एक छोटा सहयोगी बना सकते हैं, जिसे आप अपने परीक्षण में मॉक कर सकते हैं:

public class DateBuilder {
    public java.util.Date now() {
        return new java.util.Date();
    }
}

से एक DateBuilder सदस्य और परिवर्तन कॉल बनाएं new Dateकरने के लिएdateBuilder.now()

import java.util.Date;

public class Demo {

    DateBuilder dateBuilder = new DateBuilder();

    public void run() throws InterruptedException {
        Date dateOne = dateBuilder.now();
        Thread.sleep(10);
        Date dateTwo = dateBuilder.now();
        System.out.println("Dates are the same: " + dateOne.equals(dateTwo));
    }

    public static void main(String[] args) throws InterruptedException {
        new Demo().run();
    }
}

मुख्य विधि का उत्पादन होगा:

Dates are the same: false

परीक्षण में आप एक ठूंठ को इंजेक्ट कर सकते हैं DateBuilderऔर इसे अपनी पसंद का कोई भी मूल्य वापस कर सकते हैं। उदाहरण के लिए मॉकिटो या एक अनाम वर्ग जो ओवरराइड करता है now():

public class DemoTest {

    @org.junit.Test
    public void testMockito() throws Exception {
        DateBuilder stub = org.mockito.Mockito.mock(DateBuilder.class);
        org.mockito.Mockito.when(stub.now()).thenReturn(new java.util.Date(42));

        Demo demo = new Demo();
        demo.dateBuilder = stub;
        demo.run();
    }

    @org.junit.Test
    public void testAnonymousClass() throws Exception {
        Demo demo = new Demo();
        demo.dateBuilder = new DateBuilder() {
            @Override
            public Date now() {
                return new Date(42);
            }
        };
        demo.run();
    }
}

0

SimpleDateFromat का उपयोग करके स्ट्रिंग को तिथियों में परिवर्तित करें, निर्माता को आवश्यक दिनांक / समय फ़ील्ड निर्दिष्ट करें और स्ट्रिंग मानों की तुलना करें:

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


0

यहां एक उपयोगिता फ़ंक्शन है जिसने मेरे लिए काम किया है।

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


-1

मैंने java.util को ऑब्जेक्ट्स डाले। डेट करें और तुलना करें

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

यह सटीक होने के कारण मुखर हो जाएगा।
टेकक्रंच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.