如何使用 Java 流 API 正确过滤介于开始日期和结束日期之间的日期?
How would I properly filter dates that are in between a start date and end date, using the Java Streams API?
所以我一直在使用 Java 流 API,并且我正在尝试过滤我的数据库中的日期,其中某些内容的开始日期早于当前日期,并且结束日期晚于当前日期。我已经尝试了很多东西,但到目前为止我尝试的任何东西似乎都不起作用。
这是我到目前为止尝试过的方法:
I've tried implementing the ChronoLocalDate
interface, and used .isBefore() and
.isAfter()`, but the dates did not filter properly
At this point I've now tried sticking with LocalDate
, and I've tried converting LocalDate.now()
to the yyyy-MM-dd
format (which is the date format in my DB) using the following code:
LocalDate rightNow = LocalDate.now();
String formatString = rightNow.format(DateTimeFormatter.ISO_LOCAL_DATE);
但是,这不起作用并导致以下错误:
The method format(DateTimeFormatter) is undefined for the type LocalDate
我从 2018 年的各种例子中得到了上面的代码。 LocalDate.format()
已经贬值了吗?
此外,我还没有找到任何使用 LocalDate 在开始日期和结束日期之间进行过滤的相关示例。
如果您能从这里得到任何帮助,我们将不胜感激。提前致谢。
好吧,您可以使用 Java8 streams
和 LocalDate
来执行以下操作。
public static void main(String[] args) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String startTextDateFromDB = "2019-03-14";
String endTextDateFromDB = "2019-05-14";
LocalDate startDate = LocalDate.parse(startTextDateFromDB, dateFormatter);
LocalDate endDate = LocalDate.parse(endTextDateFromDB, dateFormatter);
List<LocalDate> getList = getDatesInBetween(startDate, endDate);
System.out.println(Arrays.asList(getList));
}
//return a list of all localdates between two dates
public static List<LocalDate> getDatesInBetween(
LocalDate startDate, LocalDate endDate) {
//get numbers between two dates
long numbersBetween = ChronoUnit.DAYS.between(startDate, endDate);
//collect each localdate from start date until end date
return IntStream.iterate(0, i -> i + 1)
.limit(numbersBetween)
.mapToObj(i -> startDate.plusDays(i))
.collect(Collectors.toList());
}
}
下面是如何应用过滤器的示例。我假设你有一个包含 Foo
元素的列表,并且这个 class 有两个字段:startDate
和 endDate
.
LocalDate now = LocalDate.now();
List<LocalDate> dates = dbEntries.stream()
.filter(t -> t.getStartDate().compareTo(now) < 0 && t.getEndDate().compareTo(now) > 0)
.collect(Collectors.toList());
Spring Boot 可能能够自动将数据库中的日期转换为适当的 Temporal 实例。也许使用注释。
在 SQL
中过滤
So I've been using the Java Streams API, and I'm trying to filter dates in my DB,
这是矛盾的。如果您在数据库中过滤日期作为查询的一部分(通常是最好的方法),那么您就不需要 Java 流。
半开查询 SQL
写下您的查询。我假设我们正在查询一对数据类型类似于 SQL-standard DATE
类型的列,以获得没有时间和时区的仅日期值。
在此查询代码中,我们使用了半开方法,其中开头是包含,结尾是独占。这通常是处理时间跨度的最佳方法。
String sql = "SELECT * FROM event_ WHERE start_ <= ? AND stop_ > ? ;" ;
今天
我们需要将今天的日期传递给 ?
占位符。
时区对于确定日期至关重要。对于任何给定时刻,日期在全球范围内因地区而异。例如,Paris France is a new day while still “yesterday” in Montréal Québec.
午夜后几分钟
如果未指定时区,JVM 将隐式应用其当前默认时区。该默认值在运行时可能 change at any moment (!),因此您的结果可能会有所不同。最好明确指定 desired/expected 时区作为参数。如果关键,请与您的用户确认区域。
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 EST
或 IST
等 2-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,代码将变得难以阅读,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
LocalDate today = LocalDate.now( z ) ;
占位符的传递值
我们有今天的日期,请将其传递给您准备好的声明。
myPreparedStatement.setObject( 1 , today ) ;
myPreparedStatement.setObject( 2 , today ) ;
在 Java
中过滤
通常最好在您的 SQL 中执行尽可能多的过滤,以便在数据库的服务器端执行。 Postgres 等强大的企业级数据库引擎针对此类工作进行了高度优化。
但是,也许您有一些理由在 Java 而不是数据库中执行此操作。
您必须在 Java、pulling from a result set in JDBC 中逐行检索。所以你不妨过滤一下。这意味着不需要 Java 流。只需比较 LocalDate
的值作为您的起始值和终止值。
I've tried implementing the ChronoLocalDate interface, and used .isBefore() and.isAfter()`, but the dates did not filter properly
您似乎对 java.time 在这方面的工作方式感到困惑。 ChronoLocalDate
interface is not for you to implement. The interface is already implemented in the LocalDate
class for the ISO 8601 日历系统在世界其他大部分地区的西方普遍使用。该接口是为实现各种日历系统的作者而存在的,而不是为你和我而存在的。至少,已经为 Java 实现了大约十几个这样的日历系统,包括一些与 Java 捆绑在一起的系统,例如伊斯兰,日本和泰国日历。
顺便说一句,java.time Java文档解释说您通常应该避免使用更通用的接口。如上所述,它们的存在主要是为了那些实施日历系统的人的利益。在日常业务编程中,您通常应该使用更具体的 classes。例如,LocalDate
而不是 ChronoLocalDate
。
要比较 LocalDate
个对象,请调用 LocalDate::isBefore
, isAfter
, isEqual
、equals
或 compareTo
方法
List< Event > events = new ArrayList<>() ;
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
…
LocalDate start = rs.getObject( "start_" , LocalDate.class ) ;
LocalDate stop = rs.getObject( "stop_" , LocalDate.class ) ;
if( ( ! start.isAfter( today ) ) && ( stop.isBefore( today ) ) {
Event event = … ;
events.add( event ) ;
} else {
// Skip this row. Does not meet our criterion of containing today's date.
}
}
LocalDateRange
ThreeTen-Extra 库使此类工作变得更加容易。该项目向 java.time classes 添加了功能。
对于您的具体情况,您可能会发现 LocalDateRange
class 很有帮助。此 class 将时间跨度表示为一对 LocalDate
对象。有用的比较方法包括 abuts
、contains
、overlaps
等。这些默认情况下根据半开方法工作,尽管您可以选择指定完全关闭(开始和结束都 包含 )。
让我们使用这个 class 来修改上面示例代码中的比较。
List< Event > events = new ArrayList<>() ;
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
…
LocalDate start = rs.getObject( "start_" , LocalDate.class ) ;
LocalDate stop = rs.getObject( "stop_" , LocalDate.class ) ;
LocalDateRange dateRange = LocalDateRange.of( start , stop ) ; // Instantiate the date range.
if( ( dateRange.contains( today ) ) { // Call `LocalDateRange::contains` rather than write our own comparison logic.
Event event = … ;
events.add( event ) ;
} else {
// Skip this row. Does not meet our criterion of containing today's date.
}
}
智能对象,而不是哑字符串
LocalDate rightNow = LocalDate.now();
String formatString = rightNow.format(DateTimeFormatter.ISO_LOCAL_DATE);
为什么要生成字符串?鉴于您的问题陈述,我认为不需要字符串。使用日期时间值时,请使用日期时间对象。不要将字符串用于日期时间业务逻辑。
教程
提示:通读 java.time 上的 the tutorial,由 Oracle 免费提供。
所以我发现了问题所在。我从 java.joda
而不是 java.time
导入了错误的 LocalDate
。
所以从这里开始,我不再使用 ChronoLocalDate
,而是使用 java.time
中的 LocalDate
,并且我的过滤现在可以完美运行。我犯了一个非常愚蠢的错误,但我想通了。
感谢所有同样尝试提供帮助的人。
所以我一直在使用 Java 流 API,并且我正在尝试过滤我的数据库中的日期,其中某些内容的开始日期早于当前日期,并且结束日期晚于当前日期。我已经尝试了很多东西,但到目前为止我尝试的任何东西似乎都不起作用。
这是我到目前为止尝试过的方法:
I've tried implementing the
ChronoLocalDate
interface, and used.isBefore() and
.isAfter()`, but the dates did not filter properlyAt this point I've now tried sticking with
LocalDate
, and I've tried convertingLocalDate.now()
to theyyyy-MM-dd
format (which is the date format in my DB) using the following code:
LocalDate rightNow = LocalDate.now();
String formatString = rightNow.format(DateTimeFormatter.ISO_LOCAL_DATE);
但是,这不起作用并导致以下错误:
The method format(DateTimeFormatter) is undefined for the type LocalDate
我从 2018 年的各种例子中得到了上面的代码。 LocalDate.format()
已经贬值了吗?
此外,我还没有找到任何使用 LocalDate 在开始日期和结束日期之间进行过滤的相关示例。
如果您能从这里得到任何帮助,我们将不胜感激。提前致谢。
好吧,您可以使用 Java8 streams
和 LocalDate
来执行以下操作。
public static void main(String[] args) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String startTextDateFromDB = "2019-03-14";
String endTextDateFromDB = "2019-05-14";
LocalDate startDate = LocalDate.parse(startTextDateFromDB, dateFormatter);
LocalDate endDate = LocalDate.parse(endTextDateFromDB, dateFormatter);
List<LocalDate> getList = getDatesInBetween(startDate, endDate);
System.out.println(Arrays.asList(getList));
}
//return a list of all localdates between two dates
public static List<LocalDate> getDatesInBetween(
LocalDate startDate, LocalDate endDate) {
//get numbers between two dates
long numbersBetween = ChronoUnit.DAYS.between(startDate, endDate);
//collect each localdate from start date until end date
return IntStream.iterate(0, i -> i + 1)
.limit(numbersBetween)
.mapToObj(i -> startDate.plusDays(i))
.collect(Collectors.toList());
}
}
下面是如何应用过滤器的示例。我假设你有一个包含 Foo
元素的列表,并且这个 class 有两个字段:startDate
和 endDate
.
LocalDate now = LocalDate.now();
List<LocalDate> dates = dbEntries.stream()
.filter(t -> t.getStartDate().compareTo(now) < 0 && t.getEndDate().compareTo(now) > 0)
.collect(Collectors.toList());
Spring Boot 可能能够自动将数据库中的日期转换为适当的 Temporal 实例。也许使用注释。
在 SQL
中过滤So I've been using the Java Streams API, and I'm trying to filter dates in my DB,
这是矛盾的。如果您在数据库中过滤日期作为查询的一部分(通常是最好的方法),那么您就不需要 Java 流。
半开查询 SQL
写下您的查询。我假设我们正在查询一对数据类型类似于 SQL-standard DATE
类型的列,以获得没有时间和时区的仅日期值。
在此查询代码中,我们使用了半开方法,其中开头是包含,结尾是独占。这通常是处理时间跨度的最佳方法。
String sql = "SELECT * FROM event_ WHERE start_ <= ? AND stop_ > ? ;" ;
今天
我们需要将今天的日期传递给 ?
占位符。
时区对于确定日期至关重要。对于任何给定时刻,日期在全球范围内因地区而异。例如,Paris France is a new day while still “yesterday” in Montréal Québec.
午夜后几分钟如果未指定时区,JVM 将隐式应用其当前默认时区。该默认值在运行时可能 change at any moment (!),因此您的结果可能会有所不同。最好明确指定 desired/expected 时区作为参数。如果关键,请与您的用户确认区域。
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 EST
或 IST
等 2-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,代码将变得难以阅读,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
LocalDate today = LocalDate.now( z ) ;
占位符的传递值
我们有今天的日期,请将其传递给您准备好的声明。
myPreparedStatement.setObject( 1 , today ) ;
myPreparedStatement.setObject( 2 , today ) ;
在 Java
中过滤通常最好在您的 SQL 中执行尽可能多的过滤,以便在数据库的服务器端执行。 Postgres 等强大的企业级数据库引擎针对此类工作进行了高度优化。
但是,也许您有一些理由在 Java 而不是数据库中执行此操作。
您必须在 Java、pulling from a result set in JDBC 中逐行检索。所以你不妨过滤一下。这意味着不需要 Java 流。只需比较 LocalDate
的值作为您的起始值和终止值。
I've tried implementing the ChronoLocalDate interface, and used .isBefore() and.isAfter()`, but the dates did not filter properly
您似乎对 java.time 在这方面的工作方式感到困惑。 ChronoLocalDate
interface is not for you to implement. The interface is already implemented in the LocalDate
class for the ISO 8601 日历系统在世界其他大部分地区的西方普遍使用。该接口是为实现各种日历系统的作者而存在的,而不是为你和我而存在的。至少,已经为 Java 实现了大约十几个这样的日历系统,包括一些与 Java 捆绑在一起的系统,例如伊斯兰,日本和泰国日历。
顺便说一句,java.time Java文档解释说您通常应该避免使用更通用的接口。如上所述,它们的存在主要是为了那些实施日历系统的人的利益。在日常业务编程中,您通常应该使用更具体的 classes。例如,LocalDate
而不是 ChronoLocalDate
。
要比较 LocalDate
个对象,请调用 LocalDate::isBefore
, isAfter
, isEqual
、equals
或 compareTo
方法
List< Event > events = new ArrayList<>() ;
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
…
LocalDate start = rs.getObject( "start_" , LocalDate.class ) ;
LocalDate stop = rs.getObject( "stop_" , LocalDate.class ) ;
if( ( ! start.isAfter( today ) ) && ( stop.isBefore( today ) ) {
Event event = … ;
events.add( event ) ;
} else {
// Skip this row. Does not meet our criterion of containing today's date.
}
}
LocalDateRange
ThreeTen-Extra 库使此类工作变得更加容易。该项目向 java.time classes 添加了功能。
对于您的具体情况,您可能会发现 LocalDateRange
class 很有帮助。此 class 将时间跨度表示为一对 LocalDate
对象。有用的比较方法包括 abuts
、contains
、overlaps
等。这些默认情况下根据半开方法工作,尽管您可以选择指定完全关闭(开始和结束都 包含 )。
让我们使用这个 class 来修改上面示例代码中的比较。
List< Event > events = new ArrayList<>() ;
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
…
LocalDate start = rs.getObject( "start_" , LocalDate.class ) ;
LocalDate stop = rs.getObject( "stop_" , LocalDate.class ) ;
LocalDateRange dateRange = LocalDateRange.of( start , stop ) ; // Instantiate the date range.
if( ( dateRange.contains( today ) ) { // Call `LocalDateRange::contains` rather than write our own comparison logic.
Event event = … ;
events.add( event ) ;
} else {
// Skip this row. Does not meet our criterion of containing today's date.
}
}
智能对象,而不是哑字符串
LocalDate rightNow = LocalDate.now();
String formatString = rightNow.format(DateTimeFormatter.ISO_LOCAL_DATE);
为什么要生成字符串?鉴于您的问题陈述,我认为不需要字符串。使用日期时间值时,请使用日期时间对象。不要将字符串用于日期时间业务逻辑。
教程
提示:通读 java.time 上的 the tutorial,由 Oracle 免费提供。
所以我发现了问题所在。我从 java.joda
而不是 java.time
导入了错误的 LocalDate
。
所以从这里开始,我不再使用 ChronoLocalDate
,而是使用 java.time
中的 LocalDate
,并且我的过滤现在可以完美运行。我犯了一个非常愚蠢的错误,但我想通了。
感谢所有同样尝试提供帮助的人。