Java: how do I check if a Date is within a certain range?
我有一系列有开始日期和结束日期的范围。 我想检查日期是否在该范围内。
Date.before()和Date.after()似乎有点尴尬。 我真正需要的是像这样的伪代码:
1 2 3 |
不确定它是否相关,但我从数据库中提取的日期有时间戳。
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项目扩展。
将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 |
也许您只想使用日期,而不是时间。
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" ) ); |
我们可以使用
1 |
如果您选择将ThreeTen-Extra库添加到项目中,则可以使用
我们可以通过指定时区来获得
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及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如
现在处于维护模式的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& 7在ThreeTen-Backport。
-
Android的
- ThreeTenABP项目特别适用于Android的ThreeTen-Backport(如上所述)。
- 请参阅如何使用....
ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。您可以在这里找到一些有用的类,例如
这是正确的方法。日历的工作方式相同。我能给你的最好的(根据你的例子)是这样的:
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 |
你的逻辑会正常工作。正如你所提到的,你从数据库获取的日期是时间戳,你只需要先将时间戳转换为日期然后使用这个逻辑。
也别忘了检查空日期。
这里共享一点来从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; } } |