0

I have the following code and trying to figure out if the date difference is greater than one year or not. If I have the two dates like this:

  1. 2022-08-20 10:26:44.000

  2. 2023-08-25 10:26:44.000

How should I send these two dates such that it's accepted for moreTheOneYearDifference method as defined below?

public class DateOneYearDifferenceTest {

    final GregorianCalendar gc = new GregorianCalendar();

    public static void main(String[] args) {
        System.out.printf(moreTheOneYearDifference(2022-08-20 10:26:44.000, 2023-08-25 10:26:44.000));
        

    }

    public boolean moreTheOneYearDifference(Calendar c1, Calendar c2) {
        int days = 365;
        if (c1.before(c2) && gc.isLeapYear(c1.get(Calendar.YEAR))) {
            days += 1;
        } else if (gc.isLeapYear(c2.get(Calendar.YEAR))) {
            days += 1;
        }
        return TimeUnit.MICROSECONDS.toDays((long) Math.abs(c1.getTimeInMillis() - c2.getTimeInMillis())) >= days;
    }

}

The above method was copied from this stackoverflow post answer

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Tan
  • 1,433
  • 5
  • 27
  • 47
  • 4
    You should be using `LocalDateTime` instead of `Date` or `Calendar`. `LocalDateTime` has a parse method to convert a `String` to a `LocalDateTime`. – Gilbert Le Blanc Aug 25 '23 at 18:41
  • 1
    `s1 = s1.replace(" ", "T");s2 = s2.replace(" ", "T");boolean moreThanOneYearDifference = LocalDateTime.parse(s1).isBefore(LocalDateTime.parse(s2).minusYears(1L));` – g00se Aug 25 '23 at 19:24
  • 2
    Exactly how do you define a year? Usually we ignore the Leap Day, as its purpose is to slow the calendar so we can pretend a year neatly divides into whole days. So a year from August 20 is always August 20, regardless of Leap Year — at least by usual convention. In other words, we try to ignore the existence of Leap Day when counting years. You seem to have a different definition, so you should explain your definition clearly and specifically. – Basil Bourque Aug 26 '23 at 06:59
  • As @GilbertLeBlanc said, don’t use `Calendar`. It’s long outdated and was always cumbersome to work with. From the question where you copied your code instead look at [the good answer by Basil Bourque](https://stackoverflow.com/a/48449221/5772882) and [the one by klabe](https://stackoverflow.com/a/73889564/5772882). – Ole V.V. Aug 26 '23 at 18:58
  • The code you took from that answer is even confusing and flawed. Do your self a favour, throw it away now (I already directed you to where you can find something better). – Ole V.V. Aug 26 '23 at 19:49
  • @BasilBourque It looks to me like the code the OP copied from that other answer exactly tries to *compensate* for leap years so that the time from August 20 to August 20 is always exactly a year. However the code fails to do so correctly. And in addition it also doesn’t always count days correctly. – Ole V.V. Aug 26 '23 at 19:55
  • Tan, are you after the unsigned difference or absolute value of the difference, so without regard to which of the date-times is earlier? – Ole V.V. Aug 26 '23 at 20:16

3 Answers3

1

Just as an alternative, you could calculate the Period.between(…) two LocalDates, which are part of a LocalDateTime, available by LocalDateTime.toLocalDate().

See this example:

public static void main(String[] args) {
    // example datetimes
    String a = "2022-08-20 10:26:44.000";
    String b = "2023-08-25 10:26:44.000";
    // prepare a formatter that is able to parse those Strings
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
    // then actually parsen them
    LocalDateTime ldtA = LocalDateTime.parse(a, dtf);
    LocalDateTime ldtB = LocalDateTime.parse(b, dtf);
    // caclulate the period between those two dates
    Period period = Period.between(ldtA.toLocalDate(), ldtB.toLocalDate());
    // check if the resulting period is a full year or more
    if (period.getYears() >= 1) {
        System.out.println("Difference is greater than or equals one year");
    } else {
        System.err.println("Difference is less than one year");
    }
}

Resulting output:

Difference is greater than or equals one year

Have a look at the other methods of a Period and if you want to involve time of day or calulate with that exclusively, check out a Duration:

The Java™ Tutorials — Period and Duration

deHaar
  • 17,687
  • 10
  • 38
  • 51
  • 1
    Maybe not ideal, but seems to me to be the best suggestion so far. The OP may not want to throw the time of day away (from August 25 15:00 to August 25 next year 09:00, is that less than one year?) And the OP may want to know whether the difference is *strictly* more than a year. Getting this out of a `Period` is a bit more complicated. But only the OP can tell (I frankly find the question unclear). In any case a nice demonstration of java.time and some of its strong points. – Ole V.V. Aug 27 '23 at 09:19
  • Totally agree @OleV.V., I just felt like providing an alternative, even if it is not the perfect solution. There is no perfect solution to an unclear question. One would likely have to write a custom class for this, maybe using `Period` and `Duration`. – deHaar Aug 28 '23 at 06:43
  • Thanks for the alternative. What part of question is unclear? – Tan Aug 29 '23 at 15:54
  • 1
    What’s unclear: Is the second date-time always later than the first? Do you mean *strictly* greater than a year (as opposed to equal to or less than a year)? By *one year* do you mean for example from 2022-08-20 10:26:44.000 to 2023-08-20 10:26:44.000? I believe much of the confusion comes from the code you found being flawed, which leaves doubt about what exactly you want to obtain from it. – Ole V.V. Aug 30 '23 at 06:51
  • 1
    Hi! It is not clear to me what to do if the plain *date difference* of your two dates plus times is exactly one year, zero months and zero days. Should the method `return false` or should it compare the time part down to milliseconds? "One year and one millisecond" can certainly be described as "more than one year". – deHaar Aug 30 '23 at 06:55
0

How should I send these two dates such that it's accepted for moreTheOneYearDifference method as defined below?

You can send like this:

Calendar date1 = new GregorianCalendar(2022, 7, 20, 10, 26, 44); // Note: Month is 0-based
Calendar date2 = new GregorianCalendar(2023, 7, 25, 10, 26, 44); // Note: Month is 0-based

I corrected the month values to be 0-based (January is 0, February is 1, and so on), and created instances of Calendar for date1 and date2 using the GregorianCalendar class and provided the correct year, month, day, hour, minute, and second values.

Note: I think that you mean moreThanOneYearDifference and no moreTheOneYearDifference

how would I go about converting 2022-08-20 10:26:44.000 such that it fits this format Calendar date1 = new GregorianCalendar(2022, 7, 20, 10, 26, 44) ?

You can do this way:

DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");

Calendar c1 = new GregorianCalendar();
c1.setTimeInMillis(format.parse("2022-08-20 10:26:44.000").getTime());

Calendar c2 = new GregorianCalendar();
c2.setTimeInMillis(format.parse("2023-08-25 10:26:44.000").getTime());

But, like Basil Bourque said: "These terrible date-time classes were years ago supplanted by the modern java.time classes defined in JSR 310."

So, I recommend that you use the modern java.time classes if it is possible. It will be something like this:

public class DateOneYearDifferenceTest {
    public static void main(String[] args) {
        // Original Strings
        String dateString1 = "2022-08-20 10:26:44.000";
        String dateString2 = "2023-08-20 10:26:44.000";

        // Format parser
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

        // Convertion
        LocalDateTime dateTime1 = LocalDateTime.parse(dateString1, formatter);
        LocalDateTime dateTime2 = LocalDateTime.parse(dateString2, formatter);

        if (moreThanOneYearDifference(dateTime1, dateTime2)) {
            System.out.println("The time difference is more than one year.");
        } else {
            System.out.println("The time difference is not more than one year.");
        }
    }

    /*
     * Modern method
     */
    public static boolean moreThanOneYearDifference(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        long daysDifference = ChronoUnit.DAYS.between(dateTime1, dateTime2);
        return Math.abs(daysDifference) > 365;
    }
}

I don't know how much specific you need to be, so maybe you should edit it to consider hours, minutes, seconds, etc...

Diego Borba
  • 1,282
  • 8
  • 22
  • how would I go about converting `2022-08-20 10:26:44.000` such that it fits this format `Calendar date1 = new GregorianCalendar(2022, 7, 20, 10, 26, 44)` ? – Tan Aug 25 '23 at 18:50
  • So do I need to retrieve year, month, date etc from my original date and put it in this format `GregorianCalendar(2022, 7, 20, 10, 26, 44)` ? I guess I'm not following you – Tan Aug 25 '23 at 19:06
  • I improved my answer, I hope it helps! – Diego Borba Aug 25 '23 at 19:08
  • 1
    Thank you. I needed to only figure out if two dates are more than a year or not and your code does that. – Tan Aug 25 '23 at 19:24
  • Glad I can help! – Diego Borba Aug 25 '23 at 19:30
  • One question, if the ` String dateString1 = "2022-08-20 10:26:44.000";` are not a string for me as I'm getting it from the database with datetime column, I don't need to do the string conversion like this `LocalDateTime dateTime1 = LocalDateTime.parse(dateString1, formatter);`, right? – Tan Aug 25 '23 at 19:32
  • What the type of the object that comes from the database? I mean the Java class. – Diego Borba Aug 25 '23 at 19:34
  • It's like this `Date beginDate = ch.getDate(Client.CHECKLIST).beginDate;` so `beginDate` is of the form `2023-08-20 10:26:44.000` if I System.out.println it. – Tan Aug 25 '23 at 19:39
  • You can do `LocalDateTime localDateTime = beginDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();` – Diego Borba Aug 25 '23 at 19:43
  • 1
    Ok, thanks. And similarly I will do for current date as well since I would need to match both the dates format for comparision. – Tan Aug 25 '23 at 19:52
  • The line you suggested `LocalDateTime localDateTime = beginDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();` seems to be giving me this error : `java.lang.UnsupportedOperationException [INFO] [talledLocalContainer] at java.sql/java.sql.Date.toInstant(Date.java:316)` Is everything okay with the conversion you suggested? – Tan Aug 25 '23 at 20:07
  • Sorry, I thought that it was `java.util.Date`. – Diego Borba Aug 25 '23 at 20:09
  • Sorry about not providing enough info. Would there be any fix for this? – Tan Aug 25 '23 at 20:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/255060/discussion-between-diego-borba-and-tan). – Diego Borba Aug 25 '23 at 20:17
  • 1
    You should not use either of the `Date` classes for getting a `datetime` from your database. Use `LocalDateTime` for that too. Since JDBC 4.2 assuming a JDBC `ResultSet rs` use `rs.getObject("your_datetime_column", LocalDateTime.class)`. And save any and all conversions. – Ole V.V. Aug 27 '23 at 10:09
0

Maybe try something like this:

DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
    
Calendar c1 = new GregorianCalendar(), c2 = new GregorianCalendar();
c1.setTimeInMillis(format.parse("2022-08-20 10:26:44.000").getTime());
c2.setTimeInMillis(format.parse("2023-08-25 10:26:44.000").getTime());
System.out.println(moreTheOneYearDifference(c1, c2));
  • 3
    These terrible date-time classes were years ago supplanted by the modern *java.time* classes defined in JSR 310. – Basil Bourque Aug 25 '23 at 18:48
  • @BasilBourque Is there a better way to achieve with java.time package? – Tan Aug 25 '23 at 18:49
  • 1
    @Gabriel - You approach works in calling the method but that method has some issues I guess as it is not returning me correct # of days – Tan Aug 25 '23 at 19:04
  • 1
    @Tan Yes, much better and at the same time much simpler. `LocalDateTime.parse("2022-08-20T10:26:44.000").plusYears(1).isBefore(LocalDateTime.parse("2023-08-25T10:26:44.000"))`. I have replaced the spaces in the strings with `T` to conform with ISO 8601. If you don’t want this, use a `DateTimeFormatter` for parsing. Search for the details, it’s straightforward enough. – Ole V.V. Aug 26 '23 at 20:23
  • Spelled out in [the smart comment by g00se under the question](https://stackoverflow.com/questions/76979636/using-date-for-calender-as-an-input-to-figure-out-the-time-difference#comment135705771_76979636). – Ole V.V. Aug 30 '23 at 17:50