如何使用Java Streams API正确过滤开始日期和结束日期之间的日期?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Java Streams API正确过滤开始日期和结束日期之间的日期?相关的知识,希望对你有一定的参考价值。

所以我一直在使用Java Streams API,我正在尝试过滤数据库中的日期,其中某些内容的开始日期在当前日期之前,结束日期在当前日期之后。我尝试了很多东西,但到目前为止我没有尝试过任何东西似乎都有效。

这是我到目前为止所尝试的:

我已经尝试实现ChronoLocalDate接口,并使用.isBefore() and.isAfter()`,但日期没有正确过滤

在这一点上,我现在尝试坚持使用LocalDate,并且我尝试使用以下代码将LocalDate.now()转换为yyyy-MM-dd格式(这是我的数据库中的日期格式):

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 streamsLocalDate做类似的事情。

    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元素的列表,这个类有两个字段:startDateendDate

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实例。也许使用注释。

另一答案

Filter in SQL

所以我一直在使用Java Streams API,我正在尝试过滤数据库中的日期,

这是一个矛盾。如果您在数据库中过滤日期作为查询的一部分(通常是最佳方法),那么您不需要Java流。

SQL中的半开查询

写下你的疑问。我假设我们正在查询类似于SQL标准DATE类型的数据类型的一对列,仅用于没有时间和没有时区的仅日期值。

在此查询代码中,我们使用半开放方法,其中开头是包含的,而结尾是独占的。这通常是处理时间跨度的最佳方法。

String sql = "SELECT * FROM event_ WHERE start_ <= ? AND stop_ > ? ;" ;

今天

我们需要今天的日期传递到?占位符。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜过后几分钟是新的一天,而在Montréal Québec仍然是“昨天”。

如果未指定时区,则JVM会隐式应用其当前的默认时区。在运行时(!)期间,该默认值可能是change at any moment,因此您的结果可能会有所不同。最好明确指定您期望/预期的时区作为参数。如果关键,请与您的用户确认该区域。

proper time zone name的格式指定Continent/Region,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4字母缩写,如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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 ) ;

Filter in Java

通常最好在SQL中执行尽可能多的过滤,以在数据库中的服务器端执行。功能强大的企业级数据库引擎(如Postgres)针对此类工作进行了高度优化。

但是,也许你有理由在Java而不是数据库中这样做。

你必须在Java,pulling from a result set in JDBC中逐行检索。所以你不妨过滤一下。这意味着不需要Java流。只需将LocalDate的值与开始值和停止值进行比较即可。

我已经尝试实现了ChronoLocalDate接口,并使用了.isBefore()和.isAfter()`,但日期没有正确过滤

您似乎对java.time在这方面的工作方式感到困惑。 ChronoLocalDate界面不适合您实施。该界面已经在LocalDate类中实现,用于世界其他大部分地区西部常用的ISO 8601日历系统。这个界面适用于实现各种日历系统的作者,不适合你和我。至少有十几个这样的日历系统已经为Java实现,包括一些与Java捆绑在一起的日历系统,如伊斯兰,日本和泰国日历。

顺便说一句,java.time JavaDoc解释说你通常应该避免使用更通用的接口。它们的存在主要是为了实现日历系统的那些,如上所述。在日常业务编程中,您通常应该使用更具体的类。例如,LocalDate而不是ChronoLocalDate

要比较LocalDate对象,请调用LocalDate::isBeforeisAfterisEqualequalscompareTo方法

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类添加了功能。

根据您的具体情况,您可能会发现LocalDateRange课程很有帮助。此类表示作为一对LocalDate对象的时间跨度。有用的比较方法包括abutscontainsoverlaps等。默认情况下,这些工作根据半开放方法工作,但您可以选择指定完全关闭(包括开头和结尾)。

让我们使用这个类修改上面示例代码中的比较。

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.
    }
}

Smart objects, not dumb strings

LocalDate rightNow = LocalDate.now(); String formatString = rightNow.format(DateTimeFormatter.ISO_LOCAL_DATE);

你为什么要生成字符串?鉴于您的问题陈述,我认为不需要字符串。使用日期时间值时,请使用日期时间对象。不要将字符串用于日期时间业务逻辑。

Tutorial

提示:通过Oracle免费提供的java.time读取the tutorial

另一答案

所以我发现了什么是错的。我从LocalDate而不是java.joda导入了错误的java.time

所以,从这里开始,我不再使用ChronoLocalDate<

以上是关于如何使用Java Streams API正确过滤开始日期和结束日期之间的日期?的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 Streams过滤器延迟评估的意图[重复]

使用 Kafka 的 Streams API 处理错误消息

用于事件过滤的 Kafka Consumer API 与 Streams API

如何使用 Java Streams API 添加和更新地图条目

Java 8 Streams 中的过滤器映射

Java 8 中的 Streams API 详解