Как правильно отфильтровать даты, которые находятся между начальной и конечной датами, используя API Java Streams? - PullRequest
0 голосов
/ 30 марта 2019

Итак, я использую API Java Streams и пытаюсь отфильтровать даты в моей БД, в которых дата начала чего-либо предшествует текущей дате, а дата окончания - после текущей.Я пробовал несколько вещей, но ничего из того, что я пробовал до сих пор, похоже, не работает.

Вот что я попробовал до сих пор:

Я пытался реализовать 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.

Любая помощь здесь будетс благодарностьюЗаранее спасибо.

Ответы [ 4 ]

0 голосов
/ 01 апреля 2019

Итак, я выяснил, что случилось.Я импортировал неправильный LocalDate из java.joda вместо java.time.

Итак, я больше не использую ChronoLocalDate, но LocalDate из java.time, и моя фильтрация теперь работает отлично.Очень глупая ошибка с моей стороны, но я понял это.

Спасибо всем, кто тоже пытался помочь.

0 голосов
/ 30 марта 2019

Ниже приведен пример применения фильтра.Я предполагаю, что у вас есть список с Foo элементами, и этот класс имеет два поля: 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 может автоматически преобразовывать даты в базе данных в соответствующиеВременные случаи.Возможно с использованием аннотаций.

0 голосов
/ 31 марта 2019

Фильтр в SQL

Итак, я использую API Java Streams и пытаюсь отфильтровать даты в моей БД,

Это противоречие,Если вы фильтруете даты в базе данных как часть вашего запроса (как правило, лучший подход), тогда вам не нужны потоки Java.

Полуоткрытый запрос в SQL

Напишите ваш запрос,Я предполагаю, что мы запрашиваем пару столбцов типа данных, аналогичного стандарту SQL DATE, для значения только для даты без времени суток и без часового пояса.

InВ этом коде запроса мы используем подход Half-Open, где начало включительно , а окончание эксклюзивно .Как правило, это лучший подход к обработке промежутков времени.

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

Today

Нам нужна сегодняшняя дата, чтобы перейти в этот ? заполнитель.

Aчасовой пояс имеет решающее значение при определении даты.В любой момент времени дата меняется по всему миру в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как "вчера" в Монреаль Квебек .

Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента.Если критично, подтвердите зону с вашим пользователем.

Укажите правильное имя часового пояса в формате Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland,Никогда не используйте 2-4 буквенные сокращения, такие как EST или IST, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

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, извлекать из набора результатов в JDBC .Таким образом, вы могли бы также фильтровать тогда.Это означает, что нет необходимости в потоках Java.Просто сравните значения LocalDate для начальных и конечных значений.

Я пытался реализовать интерфейс ChronoLocalDate и использовал .isBefore () и.isAfter () `, но даты сделалине отфильтрован должным образом

Вы, похоже, не понимаете, как java.time работает в этом отношении. Интерфейс ChronoLocalDate не предназначен для реализации . Интерфейс уже реализован в классе LocalDate для ISO 8601 календарная система, широко используемая на западе большей части остального мира.Этот интерфейс существует для авторов, реализующих различные системы составления календаря, а не для вас и для меня. По крайней мере, около десятка таких систем составления календарей уже реализованы для Java, включая некоторые в комплекте с Java, такие как исламские, японские и тайские календари.

Кстати, JavaDoc java.time объясняет, что в общем случае следует избегать использования более общих интерфейсов.Они существуют в основном для тех, кто внедряет системы календаря, как описано выше.В повседневном деловом программировании вы обычно должны использовать более конкретные классы.Например, 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 .

Для вашей конкретной ситуации вам может пригодиться класс LocalDateRange. Этот класс представляет промежуток времени в виде пары LocalDate объектов. Полезные методы сравнения включают abuts, contains, overlaps и т. Д. Они работают по умолчанию в соответствии с подходом Half-Open, хотя при желании вы можете указать полностью закрытый (начало и конец включительно ).

Давайте использовать этот класс для изменения сравнения в нашем примере кода выше.

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 , предоставляемое Oracle бесплатно.

0 голосов
/ 30 марта 2019

Ну, вы можете сделать что-то вроде следующего, используя 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());        
    }
}
...