0

I have a date, and I want to see if that date is further/less than 1 year away from today. For example, the the date is 13th May 2017, it would return true, if it was 13th May 2018, it would return false.

The bug at the moment in my code is that it would return true for December 2014 (which should return false).

Any ideas?

private boolean checkIfDateIsInRange() {
        Calendar today = DateTimeHelper.dateToCalendar(new Date());
        if (currentDate.get(Calendar.YEAR) > today.get(Calendar.YEAR) + 1 || currentDate.get(Calendar.YEAR) < today.get(Calendar.YEAR) - 1){
            return false;
        }
        return true;
    } 

The dateToCalendar method is as follows:

public static Calendar dateToCalendar(Date date){
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    return cal;
}
spogebob92
  • 1,474
  • 4
  • 23
  • 32
  • 1
    I strongly recommend you don’t use `Calendar` and `Date`. Those classes are poorly designed and long outdated. Instead use classes from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Sep 29 '22 at 04:56

8 Answers8

2

I suggest you to use Joda Times and you can find Years Between

public static Years yearsBetween(ReadableInstant start,
                                 ReadableInstant end)
Aditya Vyas-Lakhan
  • 13,409
  • 16
  • 61
  • 96
  • Joda time is too bloated for the functions we need, but thanks. – spogebob92 May 11 '16 at 11:50
  • It's a pretty heavy, but complete and mature solution. Working with dates with custom code can be frustrating. There are numerous little things which can break the final result in your custom code. – Zielony May 11 '16 at 11:52
  • 1
    @spogebob92 then i Think Sir Titus gave nice solution,try with that – Aditya Vyas-Lakhan May 11 '16 at 11:52
  • 1
    @spogebob92 If I understand the word *bloated*, then the `Calendar` class was bloated, way too many responsibilities in one single class. Joda-Time is not. Another thing is: Joda-Time has been replaced with java.time, the modern Java date and time API. See the answers [by Basil Bourque](https://stackoverflow.com/a/48449221/5772882) and [by klabe](https://stackoverflow.com/a/73889564/5772882). – Ole V.V. Sep 29 '22 at 08:41
1

tl;dr

see if that date is further/less than 1 year away from today

Use LocalDate.isAfter for comparison.

LocalDate.of( 2020 , Month.JANUARY , 23 ).isAfter(
    LocalDate.now().plusYears( 1 )  // Better to pass an explicit time zone than rely on the JVM’s current default: LocalDate.now( ZoneId.of( “Pacific/Auckland” ) )
)

Details

The modern approach uses the java.time classes. For earlier Android see the back-port project, ThreeTenABP.

Determine today’s date requires a time zone. For any given moment the date varies around the globe by zone. For example, today in Paris France early morning is still “yesterday” in Montréal Québec.

ZoneId z = ZoneId.of( “Africa/Tunis” ) ;

The LocalDate class represents a date-only value without time of day and without time zone.

LocalDate today = LocalDate.now( z ) ;

Calculate a year into future.

LocalDate yearFromToday = today.plusYears( 1 ) ;

Instantiate your date.

LocalDate x = LocalDate.of( 2020 , Month.JANUARY , 23 ) ;

Compare.

Boolean isOverYearInFuture = x.isAfter( yearFromToday ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

startDate.plusYears(1).isBefore(endDate);

I find the code pretty self explanatory. Rather than measuring the difference between the two dates, adding a year to the first date and comparing is simpler. The code works if startDate and endDate have type LocalDate or ZonedDateTime or OffsetDateTime or LocalDateTime.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
klabe
  • 455
  • 1
  • 5
  • 13
0

You can do something like this:

if(TimeUnit.MILLISECONDS.toDays((long)Math.abs(today.getTimeInMillis() - currentDate.getTimeInMillis())) >= 365){
    // Is one or more years after or before.
}

If you want to account for leap years, you can do this:

final GregorianCalendar gc = new GregorianCalendar();
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
}
Titus
  • 22,031
  • 1
  • 23
  • 33
  • this is a better solution IMO – Pooya May 11 '16 at 11:48
  • 2
    Would not work with leap years. It is easier to get the date of today and add one year with `Calendar.add(Calendar.YEAR, 1)`. – Arnaud Denoyelle May 11 '16 at 11:49
  • @ArnaudDenoyelle you're right, I haven't considered that. – Titus May 11 '16 at 11:50
  • @ArnaudDenoyelle yeah that is the downside – Pooya May 11 '16 at 11:50
  • The issue with this solution is that (using today as example), it will only return true up to May 2015/May 2017, when I require January 2015/June 2017 to return true as well (if that makes sense). – spogebob92 May 11 '16 at 11:57
  • @spogebob92 It should return `true` for all the dates that you've mentioned because I've used `>=` (more then or equal to 365 days). – Titus May 11 '16 at 12:00
  • might be a bit pedantic, but don't forget that there are also leap seconds (last one added in 2015) – OH GOD SPIDERS May 11 '16 at 12:39
  • The code is both flawed and confusing. Your way of counting days assumes there are always 24 hours in a day, which is not the case because of summer time (DST) and other time anomalies. Your logic for determining whether you should count 365 or 366 days because of a leap year is wrong too. – Ole V.V. Aug 26 '23 at 20:01
0

Your solution is only checking if the year part of the dates has a greater difference then 1, which of course will produce incorrect results for values like 2015-1-1 and 2016-12-31.

I would recomment adding a year to the lower of the two dates (after cloning it ofc.) and then checking if it is still before the later date, in which case the difference is more than a year.

public static boolean isMoreThanAYearDifference(Calendar c1, Calendar c2) {
    final Calendar earlierDate;
    final Calendar laterDate;
    if(c1.before(c2)) {
        earlierDate = (Calendar) c1.clone();
        laterDate = c2;
    } else {
        earlierDate = (Calendar) c2.clone();
        laterDate = c1;
    }

    earlierDate.add(Calendar.YEAR, 1);
    return earlierDate.before(laterDate);
}
OH GOD SPIDERS
  • 3,091
  • 2
  • 13
  • 16
  • 1
    no need to clone the 2nd one, you only modify `earlierDate`. – zapl May 11 '16 at 12:05
  • Yeah, after a dozen times hunting bugs that were the result of not cloning calendar objects before modifying them I made it a habbit to allways clone all calendar objects passed to the function. ;-) But you're right of course...i changed it so that only earlierDate gets set to a cloned object. – OH GOD SPIDERS May 11 '16 at 12:15
0

Given today is a Calendar of the actual date and dateInQuestion is a Calendar of the date to be checked:

Calendar oneYearPlus  = (today.clone()).add(Calendar.YEAR,1);
Calendar oneYearAgo   = (today.clone()).add(Calendar.YEAR,-1);
return oneYearPlus.after(dateInQuestion) || oneYearAgo.before(dateInQuestion);
Fildor
  • 14,510
  • 4
  • 35
  • 67
0

Just adding another way to do it:

public boolean checkIfInRange(Date date){

    Calendar cal= Calendar.getInstance();

    cal.add(Calendar.YEAR, -1);
    Date yearBeforeToday = cal.getTime();
    cal.add(Calendar.YEAR, 2);
    Date yearAfterToday = cal.getTime();

    return date.after(yearBeforeToday) && date.before(yearAfterToday);
}

Try it online!

Narmer
  • 1,414
  • 7
  • 16
0

I have the same need and found the official jdk answer. It gives the indication to use Period for such kind of check on dates:

I found the functionality so useful!

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

int numYear = 1;
//checking if the period is more than the specified number of years (last parameter)
if(checkForPeriodLimit(sdf.parse("12/04/1977"), sdf.parse("11/04/1978"), numYear)){
     System.out.println("less than"+numYear+" year ago");
} else {
    System.out.println("more than"+numYear+" year ago");
}

//period checking function
public boolean checkForPeriodLimit(Date dateFrom, Date dateTo, int periodInYear){

    Period p = Period.between(dateFrom.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
            dateTo.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());

    return ((p.getYears() < periodInYear)?true:false);
}

Print the result:

**less than 1 years ago**
OPMendeavor
  • 430
  • 6
  • 14
  • Why are you mixing the legacy date-time classes with the modern *java.time* classes? The troublesome legacy classes were entirely supplanted by the *java.time* framework. – Basil Bourque Jan 25 '18 at 17:44
  • Right observation! It is just that I don't fall into the concurrency issues related to `java.util.Date and SimpleDateFormatter` and they become intuitive for me :). Effectively, `checkForPeriodLimit` trasform the input `Date (dateFrom and dateTo)` into `java.time.LocalDate` with the `toLocalDate()` function. To be more safe, as you suggested, the parameters `dateFrom` and `dateTo` have to be of type `LocalDate` – OPMendeavor Jan 25 '18 at 18:23