Java:如何检查Date是否在一定范围内?

Java: how do I check if a Date is within a certain range?

我有一系列有开始日期和结束日期的范围。 我想检查日期是否在该范围内。

Date.before()和Date.after()似乎有点尴尬。 我真正需要的是像这样的伪代码:

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

不确定它是否相关,但我从数据库中提取的日期有时间戳。


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

对我来说似乎不那么尴尬。请注意,我这样写而不是

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

所以即使testDate完全等于其中一个最终案例,它也会起作用。


TL;博士

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ZoneId z = ZoneId.of("America/Montreal" );  // A date only has meaning within a specific time zone. For 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*.
)

半开

日期时间工作通常采用"半开"方法来定义时间跨度。开头是包容性的,而结尾是独家的。所以从星期一开始的一周到达,但不包括,在接下来的星期一。

java.time

Java 8及更高版本内置了java.time框架。补充了旧的麻烦类,包括java.util.Date/.Calendar和SimpleDateFormat。灵感来自成功的Joda-Time图书馆。由JSR 310定义。由ThreeTen-Extra项目扩展。

Instant是UTC时间轴上具有纳秒分辨率的时刻。

Instant

将java.util.Date对象转换为Instant对象。

1
2
Instant start = myJUDateStart.toInstant();
Instant stop =

如果从数据库通过JDBC获取java.sql.Timestamp对象,则以类似的方式转换为java.time.Instant。 java.sql.Timestamp已经是UTC,因此无需担心时区。

1
2
Instant start = mySqlTimestamp.toInstant() ;
Instant stop =

获取当前时刻进行比较。

1
Instant now = Instant.now();

比较使用方法isBefore,isAfter和equals。

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

LocalDate

也许您只想使用日期,而不是时间。

LocalDate类表示仅限日期的值,没有时间和没有时区。

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

要获取当前日期,请指定时区。在任何特定时刻,今天的日期因时区而异。例如,巴黎的新日早些时候比蒙特利尔早。

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

我们可以使用isEqualisBeforeisAfter方法进行比较。在日期时间工作中,我们通常使用半开放方法,其中一段时间??的开始是包容性的,而结尾是排他性的。

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

Interval

如果您选择将ThreeTen-Extra库添加到项目中,则可以使用Interval类来定义时间跨度。该类提供了测试间隔是否包含,邻接,封闭或重叠其他日期时间/间隔的方法。

Interval类适用于Instant对象。 Instant类表示UTC时间轴上的时刻,分辨率为纳秒(小数部分最多九(9)位)。

我们可以通过指定时区来获得ZonedDateTime,将LocalDate调整为特定时刻,即当天的第一时刻。从那里我们可以通过提取Instant返回到UTC。

1
2
3
4
5
6
7
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 );

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。

从哪里获取java.time类?

  • Java SE 8和SE 9及更高版本

    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小功能和修复。
  • Java SE 6和SE 7

    • 许多java.time功能都被反向移植到Java 6&amp; 7在ThreeTen-Backport。
  • Android的

    • ThreeTenABP项目特别适用于Android的ThreeTen-Backport(如上所述)。
    • 请参阅如何使用....

ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter等。


这是正确的方法。日历的工作方式相同。我能给你的最好的(根据你的例子)是这样的:

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

Date.getTime()返回自格林尼治标准时间1/1/1970 00:00:00以来的毫秒数,并且很长,因此很容易比较。


考虑使用Joda Time。我喜欢这个库,并希望它能取代现有的Java Date和Calendar类当前可怕的混乱。这是正确的日期处理。

编辑:现在不再是2009了,Java 8已经出了很多年了。使用基于Joda Time的Java 8内置java.time类,正如Basil Bourque在下面提到的那样。在这种情况下,您将需要Period类,以及Oracle的如何使用它的教程。


一种简单的方法是在1970年1月1日之后将日期转换为毫秒(使用Date.getTime()),然后比较这些值。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  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;
   }
 }


这对我来说更清楚,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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

}


你可以这样使用

1
2
3
4
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

不关心哪个日期边界是哪个。

1
2
Math.abs(date1.getTime() - date2.getTime()) ==
    Math.abs(date1.getTime() - dateBetween.getTime()) + Math.abs(dateBetween.getTime() - date2.getTime());

你的逻辑会正常工作。正如你所提到的,你从数据库获取的日期是时间戳,你只需要先将时间戳转换为日期然后使用这个逻辑。

也别忘了检查空日期。

这里共享一点来从Timestamp转换为date。

public static Date convertTimeStamptoDate(String val)throws Exception {

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    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;
    }
}