122

I have a series of ranges with start dates and end dates. I want to check to see if a date is within that range.

Date.before() and Date.after() seem to be a little awkward to use. What I really need is something like this pseudocode:

boolean isWithinRange(Date testDate) {
    return testDate >= startDate && testDate <= endDate;
}

Not sure if it's relevant, but the dates I'm pulling from the database have timestamps.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Mike Sickler
  • 33,662
  • 21
  • 64
  • 90
  • 1
    Similar Question with multiple Answers: [How can I determine if a date is between two dates in Java?](http://stackoverflow.com/q/883060/642706) – Basil Bourque Feb 09 '16 at 18:47
  • 2
    FYI, the troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/8/docs/api/java/util/Date.html) and [`java.util.Calendar`](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html) are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) classes. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Sep 04 '17 at 22:56

17 Answers17

194
boolean isWithinRange(Date testDate) {
   return !(testDate.before(startDate) || testDate.after(endDate));
}

Doesn't seem that awkward to me. Note that I wrote it that way instead of

return testDate.after(startDate) && testDate.before(endDate);

so it would work even if testDate was exactly equal to one of the end cases.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • 5
    with this code the following is invalid: `date is 01-01-2014 and the range is from 01-01-2014 to 31-01-2014` how can you make it valid? – Shahe Jan 31 '14 at 09:36
  • 4
    @Shahe the first thing I would do is check the time portions of your dates. Chances are your `date` is early in the morning and `startDate` is after it. – Paul Tomblin Jan 31 '14 at 13:20
  • 2
    this trick with negating or is still nice after seven years. : ) – Tosz Sep 16 '16 at 11:33
  • It's not working when testDate is today's current date.. How to handle this scenario in above suggested solution. – Swift Dec 08 '16 at 12:14
  • @Swift `Date` contains time (hours, minutes, seconds and milliseconds). So if you want to test if something is "today", then you to make a Date object that is "today" at time 0:00:00.000 and one that is "tomorrow" at time 0:00:00.000. I use the `Calendar` class for that, but I'm sure there is a better way if you use JodaTime or the newer base Java classes. – Paul Tomblin Dec 08 '16 at 15:35
42

tl;dr

ZoneId z = ZoneId.of( "America/Montreal" );  // A date only has meaning within a specific time zone. At any given moment, the date varies around the globe by zone.
LocalDate ld = 
    givenJavaUtilDate.toInstant()  // Convert from legacy class `Date` to modern class `Instant` using new methods added to old classes.
                     .atZone( z )  // Adjust into the time zone in order to determine date.
                     .toLocalDate();  // Extract date-only value.

LocalDate today = LocalDate.now( z );  // Get today’s date for specific time zone.
LocalDate kwanzaaStart = today.withMonth( Month.DECEMBER ).withDayOfMonth( 26 );  // Kwanzaa starts on Boxing Day, day after Christmas.
LocalDate kwanzaaStop = kwanzaaStart.plusWeeks( 1 );  // Kwanzaa lasts one week.
Boolean isDateInKwanzaaThisYear = (
    ( ! today.isBefore( kwanzaaStart ) ) // Short way to say "is equal to or is after".
    &&
    today.isBefore( kwanzaaStop )  // Half-Open span of time, beginning inclusive, ending is *exclusive*.
)

Half-Open

Date-time work commonly employs the "Half-Open" approach to defining a span of time. The beginning is inclusive while the ending is exclusive. So a week starting on a Monday runs up to, but does not include, the following Monday.

java.time

Java 8 and later comes with the java.time framework built-in. Supplants the old troublesome classes including java.util.Date/.Calendar and SimpleDateFormat. Inspired by the successful Joda-Time library. Defined by JSR 310. Extended by the ThreeTen-Extra project.

An Instant is a moment on the timeline in UTC with nanosecond resolution.

Instant

Convert your java.util.Date objects to Instant objects.

Instant start = myJUDateStart.toInstant();
Instant stop = …

If getting java.sql.Timestamp objects through JDBC from a database, convert to java.time.Instant in a similar way. A java.sql.Timestamp is already in UTC so no need to worry about time zones.

Instant start = mySqlTimestamp.toInstant() ;
Instant stop = …

Get the current moment for comparison.

Instant now = Instant.now();

Compare using the methods isBefore, isAfter, and equals.

Boolean containsNow = ( ! now.isBefore( start ) ) && ( now.isBefore( stop ) ) ;

LocalDate

Perhaps you want to work with only the date, not the time-of-day.

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

LocalDate start = LocalDate.of( 2016 , 1 , 1 ) ;
LocalDate stop = LocalDate.of( 2016 , 1 , 23 ) ;

To get the current date, specify a time zone. At any given moment, today’s date varies by time zone. For example, a new day dawns earlier in Paris than in Montréal.

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );

We can use the isEqual, isBefore, and isAfter methods to compare. In date-time work we commonly use the Half-Open approach where the beginning of a span of time is inclusive while the ending is exclusive.

Boolean containsToday = ( ! today.isBefore( start ) ) && ( today.isBefore( stop ) ) ;

Interval

If you chose to add the ThreeTen-Extra library to your project, you could use the Interval class to define a span of time. That class offers methods to test if the interval contains, abuts, encloses, or overlaps other date-times/intervals.

The Interval class works on Instant objects. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

We can adjust the LocalDate into a specific moment, the first moment of the day, by specifying a time zone to get a ZonedDateTime. From there we can get back to UTC by extracting a Instant.

ZoneId z = ZoneId.of( "America/Montreal" );
Interval interval = 
    Interval.of( 
        start.atStartOfDay( z ).toInstant() , 
        stop.atStartOfDay( z ).toInstant() );
Instant now = Instant.now();
Boolean containsNow = interval.contains( now );

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?

  • Java SE 8 and SE 9 and later
  • Built-in.
  • Part of the standard Java API with a bundled implementation.
  • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
  • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
  • The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
  • See How to use….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

wittich
  • 2,079
  • 2
  • 27
  • 50
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
20

That's the correct way. Calendars work the same way. The best I could offer you (based on your example) is this:

boolean isWithinRange(Date testDate) {
    return testDate.getTime() >= startDate.getTime() &&
             testDate.getTime() <= endDate.getTime();
}

Date.getTime() returns the number of milliseconds since 1/1/1970 00:00:00 GMT, and is a long so it's easily comparable.

MBCook
  • 14,424
  • 7
  • 37
  • 41
9

Consider using Joda Time. I love this library and wish it would replace the current horrible mess that are the existing Java Date and Calendar classes. It's date handling done right.

EDIT: It's not 2009 any more, and Java 8's been out for ages. Use Java 8's built in java.time classes which are based on Joda Time, as Basil Bourque mentions above. In this case you'll want the Period class, and here's Oracle's tutorial on how to use it.

Community
  • 1
  • 1
Ben Hardy
  • 1,739
  • 14
  • 16
  • 3
    don't you believe introducing Joda Time to a project is over time if all one wants to do is make a simple comparison as the questioner indicated?- – hhafez Jan 30 '09 at 03:49
  • 3
    I assume you mean overkill. In the case of most library additions I would agree with you. However Joda Time is pretty light, and helps you write more correct date handling code thanks to the immutability of its representations, so it's not a bad thing to introduce in my opinion. – Ben Hardy Jan 30 '09 at 19:26
  • 31
    And let's not forget StackOverflow rule #46: If anybody mentions dates or times in Java, it's mandatory for somebody to suggest switching to Joda Time. Don't believe me? Try to find a question that doesn't. – Paul Tomblin Jan 30 '09 at 23:03
  • 2
    Even Sun/Oracle gave up on the old date-time classes bundled with the earliest versions of Java, agreeing to supplant the old classes with the [java.time](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) framework (inspired by Joda-Time). Those old classes were poorly designed, and had proven to be troublesome and confusing. – Basil Bourque Feb 09 '16 at 18:54
  • 2
    @PaulTomblin ;-) Rule #46 has been updated. Nowadays it’s mandatory that somebody mentions and recommends [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). As also this answer has been updated to do. – Ole V.V. Apr 27 '22 at 06:55
  • 1
    I'm amazed this still gets eyeballs. – Ben Hardy May 20 '22 at 05:36
9

Since Java 8

NOTE: All, but one, Date's constructors are deprecated. Most of the Date's methods are deprecated. REF: Date: Deprecated Methods. All, but Date::from(Instant), static methods are deprecated.

So, since java 8 consider using Instant (immutable, thread-safe, leap-seconds aware) type rather than Date. REF: Instant

  static final LocalTime MARKETS_OPEN = LocalTime.of(07, 00);
  static final LocalTime MARKETS_CLOSE = LocalTime.of(20, 00);

    // Instant utcTime = testDate.toInstant();
    var bigAppleTime = ZonedDateTime.ofInstant(utcTime, ZoneId.of("America/New_York"));

within the range INCLUSIVE:

    return !bigAppleTime.toLocalTime().isBefore(MARKETS_OPEN)
        && !bigAppleTime.toLocalTime().isAfter(MARKETS_CLOSE);

within the range EXCLUSIVE:

    return bigAppleTime.toLocalTime().isAfter(MARKETS_OPEN)
        && bigAppleTime.toLocalTime().isBefore(MARKETS_CLOSE);
epox
  • 9,236
  • 1
  • 55
  • 38
4

An easy way is to convert the dates into milliseconds after January 1, 1970 (use Date.getTime()) and then compare these values.

Rahul
  • 12,886
  • 13
  • 57
  • 62
2

For covering my case -> I've got a range start & end date, and dates list that can be as partly in provided range, as fully (overlapping).

Solution covered with tests:

/**
 * Check has any of quote work days in provided range.
 *
 * @param startDate inclusively
 * @param endDate   inclusively
 *
 * @return true if any in provided range inclusively
 */
public boolean hasAnyWorkdaysInRange(LocalDate startDate, LocalDate endDate) {
    if (CollectionUtils.isEmpty(workdays)) {
        return false;
    }

    LocalDate firstWorkDay = getFirstWorkDay().getDate();
    LocalDate lastWorkDay = getLastWorkDay().getDate();

    return (firstWorkDay.isBefore(endDate) || firstWorkDay.equals(endDate))
            && (lastWorkDay.isAfter(startDate) || lastWorkDay.equals(startDate));
}
Oleksandr Yefymov
  • 6,081
  • 2
  • 22
  • 32
2

if you want inclusive comparison with LocalDate then use this code.

LocalDate from = LocalDate.of(2021,8,1);
LocalDate to = LocalDate.of(2021,8,1);
LocalDate current = LocalDate.of(2021,8,1);

boolean isDateInRage = ( ! (current.isBefore(from.minusDays(1)) && current.isBefore(to.plusDays(1))) );
  • A bit complicated, and perhaps because of that also not quite correct. I changed `current`to `LocalDate.of(2021, 7, 31);` and still got `true`, which can’t be right. Using `LocalDate` and `isBefore()` is a good idea. – Ole V.V. Jan 08 '21 at 21:08
2
    public boolean isBetween(LocalDate target, LocalDate min, LocalDate max) {
    if (target == null || min == null || max == null) {
        throw new IllegalArgumentException("paramettres can't be null");
    }

    return target.compareTo(min) >= 0 && target.compareTo(max) <= 0;
}
1

This was clearer to me,

// declare calendar outside the scope of isWithinRange() so that we initialize it only once
private Calendar calendar = Calendar.getInstance();

public boolean isWithinRange(Date date, Date startDate, Date endDate) {

    calendar.setTime(startDate);
    int startDayOfYear = calendar.get(Calendar.DAY_OF_YEAR); // first day is 1, last day is 365
    int startYear = calendar.get(Calendar.YEAR);

    calendar.setTime(endDate);
    int endDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
    int endYear = calendar.get(Calendar.YEAR);

    calendar.setTime(date);
    int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
    int year = calendar.get(Calendar.YEAR);

    return (year > startYear && year < endYear) // year is within the range
            || (year == startYear && dayOfYear >= startDayOfYear) // year is same as start year, check day as well
            || (year == endYear && dayOfYear < endDayOfYear); // year is same as end year, check day as well

}
Dhunju_likes_to_Learn
  • 1,201
  • 1
  • 16
  • 25
  • 2
    (A) `Calendar` class is *not* thread-safe. So maintaining an instance that may be accessed by more than one thread is risky. (B) The troublesome old classes such as [`java.util.Date`](https://docs.oracle.com/javase/8/docs/api/java/util/Date.html) and [`java.util.Calendar`](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html) are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) classes. See [Oracle Tutorial](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Sep 04 '17 at 22:59
  • @BasilBourque, thanks for pointing out the issues. Looks like Android doesn't yet support new DateTime class. :/ – Dhunju_likes_to_Learn Sep 04 '17 at 23:06
  • 1
    Much of the java.time functionality is back-ported to Java 6 & 7 in [ThreeTen-Backport](http://www.threeten.org/threetenbp/) and further adapted to [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) in [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) (see [*How to use…*](http://stackoverflow.com/q/38922754/642706)). And, [this Question](https://stackoverflow.com/q/494180/642706) is not about Android. – Basil Bourque Sep 04 '17 at 23:07
0

Doesnn't care which date boundry is which.

Math.abs(date1.getTime() - date2.getTime()) == 
    Math.abs(date1.getTime() - dateBetween.getTime()) + Math.abs(dateBetween.getTime() - date2.getTime());
Radu
  • 2,022
  • 3
  • 17
  • 28
0

you can use like this

Interval interval = new Interval(date1.getTime(),date2.getTime());
Interval interval2 = new Interval(date3.getTime(), date4.getTime());
Interval overlap = interval.overlap(interval2);
boolean isOverlap = overlap == null ? false : true
0
  public class TestDate {

    public static void main(String[] args) {
    // TODO Auto-generated method stub

    String fromDate = "18-FEB-2018";
    String toDate = "20-FEB-2018";

    String requestDate = "19/02/2018";  
    System.out.println(checkBetween(requestDate,fromDate, toDate));
}

public static boolean checkBetween(String dateToCheck, String startDate, String endDate) {
    boolean res = false;
    SimpleDateFormat fmt1 = new SimpleDateFormat("dd-MMM-yyyy"); //22-05-2013
    SimpleDateFormat fmt2 = new SimpleDateFormat("dd/MM/yyyy"); //22-05-2013
    try {
     Date requestDate = fmt2.parse(dateToCheck);
     Date fromDate = fmt1.parse(startDate);
     Date toDate = fmt1.parse(endDate);
     res = requestDate.compareTo(fromDate) >= 0 && requestDate.compareTo(toDate) <=0;
    }catch(ParseException pex){
        pex.printStackTrace();
    }
    return res;
   }
 }
  • 2
    Please add some words to briefly describe/explain why and how this code answers the question. – Yannis Jun 12 '18 at 09:50
  • 1
    Nice that you want to contribute, thanks. (1) Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. (2) Code-only answers are very seldom helpful. It’s from the accompanying explanation that we all learn. – Ole V.V. Jun 12 '18 at 13:09
  • 1
    Suggesting in 2018 the use of these terribly troublesome classes that were supplanted years ago is poor advice. Furthermore, your code ignores the crucial issue of time zone. – Basil Bourque Jun 12 '18 at 20:45
0

Adding new solution if the date is equals to start date:

boolean isWithinRange(Date testDate) {
   return !(testDate.before(startDate) || testDate.after(endDate));
}

Solution

return testDate.equals(startDate) || (testDate.after(startDate) && 
testDate.before(endDate)) || testDate.equals(endDate);
0

A solution will be to check date with after and before method. If is required to consider inclusive endDate, testDate.before(endDate) returns false when testDate and endDate are the same day, we can use "apache commons lang" package to verify this (org.apache.commons.lang.time.DateUtils)

import org.apache.commons.lang.time.DateUtils;

boolean isWithinRange(Date testDate) {
    return   testDate.after(startDate) && (testDate.before(endDate) || DateUtils.isSameDay(testDate, endDate));
}
Maxim Z
  • 1
  • 2
  • 1
    It’s 2022 and you are still using `Date`? Why? [Still using java.util.Date? Don’t!](https://programminghints.com/2017/05/still-using-java-util-date-dont/) (article is from 2017). – Ole V.V. May 20 '22 at 09:19
-1

your logic would work fine . As u mentioned the dates ur getting from the database are in timestamp , You just need to convert timestamp to date first and then use this logic.

Also dont forget to check for null dates.

here m sharing a bit to convert from Timestamp to date.

public static Date convertTimeStamptoDate(String val) throws Exception {

    DateFormat df = null;
    Date date = null;

    try {
        df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        date = df.parse(val);
        // System.out.println("Date Converted..");
        return date;
    } catch (Exception ex) {
        System.out.println(ex);
        return convertDate2(val);
    } finally {
        df = null;
        date = null;
    }
}
-1

google guava

Range.closed(start, end)
            .isConnected(Range.closed(now, now));
取一个好的名字
  • 1,677
  • 14
  • 16
  • Wouldn’t it be simpler and yet clearer with [`Range.contains()`](https://guava.dev/releases/19.0/api/docs/com/google/common/collect/Range.html#contains(C))? – Ole V.V. Apr 27 '22 at 09:01