Сравнение даты с датой следующего объекта в Arraylist не работает должным образом - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть список записей, в которых каждая запись содержит объекты даты.Я проверяю, что первая запись должна иметь более ранние даты, чем вторая, а вторая запись должна быть меньше 3-й записи и т. Д. Вот мой код ..

for(int i = 0; i < machines.size(); i++) {
        for(int j = i + 1; j < machines.size(); j++) {

            if((machines.get(j).getStrt_Dt().compareTo(machines.get(i).getStrt_Dt()) 
                    * machines.get(i).getStrt_Dt().compareTo(machines.get(j).getStrt_Dt()) >= 0)
                ||
                (machines.get(j).getStrt_Dt().compareTo(machines.get(i).getStrt_Dt()) 
                        * machines.get(i).getStrt_Dt().compareTo(machines.get(j).getStrt_Dt()) >= 0)) {

                throw new Exception("Dates are not as per criteria..");

            }

        }
    }

Мои даты, которыеиз базы данных:

  STRT_DT    END_DT
 ---------- ----------     
 2014-01-01 2014-12-31
 2013-01-01 2013-02-01 (Here the second record is having the date less than first record), this should fail 
 2016-01-01 2016-12-31
 2017-01-01 2017-12-31
 2018-01-01 2018-02-01

Я что-то упустил?Любая помощь будет принята с благодарностью ..

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Вы слишком усложняете это. Достаточно проверить парные записи, так как если a for(int i = 0; i < machines.size() - 1; i++) { if (! machines.get(i).getEnd_Dt().before(machines.get(i + 1).getStrt_Dt())) { throw new IllegalStateException("Dates are not in chronological order"); } } Я использую «не до» для обозначения «вкл или после». Или, другими словами, я требую, чтобы каждая дата начала была строгой до следующей даты начала, и если это не так, я выбрасываю исключение. Редактировать: Я не знал, как получить дату окончания для вашего типа объекта, поэтому, пожалуйста, замените правильный вызов получателя, где я написал getEnd_Dt().

Если вам дополнительно необходимо проверить правильность порядка начала и окончания каждого устройства (используя расширенный цикл for):

for (Machine m : machines) {
    if (m.getEnd_Dt().before(m.getStrt_Dt())) {
        throw new IllegalStateException("Dates are not in chronological order");
    }
}

Кроме того, соглашения об именах Java не используют подчеркивания в именах (кроме CONSTANT_NAME), поэтому предпочитайте getStrtDt над getStrt_Dt. Или еще лучше, getStartDate.

Тем не менее, Embid123 является верным, вы должны увидеть, если вы можете заменить Date на LocalDate. Класс Date имеет проблемы с дизайном и давно устарел. И, несмотря на название, это не дата, а момент времени. LocalDate является частью java.time, современного Java-API даты и времени, с которым гораздо приятнее работать.

Другая вещь, которая вам нужна, - это чтобы ваш запрос к базе данных сортировал записи по дате начала, тогда вы знаете, что вы получаете их в правильном порядке.

Что пошло не так в вашем коде?

Во-первых, ваш код сложен на границе нечитаемого.

Далее, когда две записи i и j имеют даты начала в неправильном порядке, то есть дата j более ранняя, чем дата i, тогда machines.get(j).getStrt_Dt().compareTo(machines.get(i).getStrt_Dt()) отрицательна и machines.get(i).getStrt_Dt().compareTo(machines.get(j).getStrt_Dt() положительно. Это действительно то же самое сравнение, только обратное. Таким образом, продукт будет отрицательным, ваше >= 0 условие будет ложным, и вы не выбросите исключение.

Только если две записи имеют одинаковую дату (с точностью до миллисекунды), compareTo возвращает 0, произведение равно 0, >= 0 будет истинным, и ваше исключение будет выдано.

0 голосов
/ 15 ноября 2018

tl; dr

Используйте современные классы java.time.LocalDate и org.threeten.extra.LocalDateRange, чтобы упростить вашу бизнес-логику до одной строки, спрашивающей, находится ли предыдущий элемент всписок не перед текущим элементом.

if ( 
    ! 
    prior
    .getDateRange()
    .isBefore( 
       current.getDateRange() 
    ) 
) { … handle rule violation … }

См. также правильный ответ Ole VV

Избегайте устаревших классов даты и времени

Никогда не используйте плохо разработанные унаследованные классы.Класс java.util.Date, несмотря на свое название, представляет момент, дату с временем суток в UTC.Класс java.sql.Date претендует на то, чтобы представлять только дату, но фактически включает в себя время суток и смещение.

java.time

Современный подход использует классы java.time .

LocalDate

Класс LocalDate представляет значение только для даты без времени суток и без часовой пояс или смещение от UTC .

Вы можете установить месяц по номеру, с вменяемой цифрой 1-12 для января-декабря.

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

Или, лучше, использовать предварительно определенные объекты перечисления Month, по одному на каждый месяц года.Совет: используйте эти Month объекты по всей вашей кодовой базе, а не просто целое число, чтобы сделать ваш код более самодокументируемым, обеспечить допустимые значения и обеспечить безопасность типов .

LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

Вы можете напрямую анализировать строки в стандартном формате ISO 8601 : ГГГГ-ММ-ДД

LocalDate ld = LocalDate.parse( "2018-01-23" ) ; 

Для анализа других форматов выполните поиск переполнения стека, чтобы узнать о DateTimeFormatter.

LocalDateRange

В вашем классе Machine вы, кажется, используете пару дат, даты начала и окончания.

Для этого есть класс.Добавьте библиотеку ThreeTen-Extra в свой проект, чтобы получить доступ к классу LocalDateRange.Этот класс имеет несколько очень полезных методов для сравнения.Использование этого класса значительно упрощает ваш код и делает цель вашей бизнес-логики более очевидной.

Сохраните даты остановки / начала в виде объекта LocalDateRange на вашем объекте Machine.

private LocalDateRange dateRange; 

Half-Open

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

Таким образом, неделя определяется как начинающаяся с одного дня, например, с понедельника, и продолжающаяся до, но не включая, того же дня следующей недели, например, следующего понедельника.Год начинается 1 января одного года и продолжается до 1 января следующего года.

Использование подхода Half-Open будет означать определение даты в вашем примере, как показано ниже.Обратите внимание на то, как изменение конечной даты на значения в годах в строках 1, 3 и 4 совпадает с вашим собственным определением видимых значений в месяцах для строк 2 и 5.

  STRT_DT    END_DT
 ---------- ----------     
 2014-01-01 2015-01-01
 2013-01-01 2013-02-01 (Here the second record is having the date less than first record), this should fail 
 2016-01-01 2017-01-01
 2017-01-01 2018-01-01
 2018-01-01 2018-02-01

Пример класса: Machine

Вот пример кода для вашего класса Machine.

package com.basilbourque.example;

import org.threeten.extra.LocalDateRange;

import java.time.LocalDate;
import java.util.List;
import java.util.Objects;

public class Machine {
    private String name;
    private LocalDateRange dateRange;

    // Constructor
    public Machine ( String name , LocalDate start , LocalDate stop ) {
        Objects.requireNonNull( name );
        Objects.requireNonNull( start );
        Objects.requireNonNull( stop );
        // TODO: Add checks to validate data, such as dates being not too far into the past or future, and name being non-empty.
        this.name = name;
        this.dateRange = LocalDateRange.of( start , stop );
    }

    // -------|  Accessors  |--------------------------

    public String getName ( ) {
        return this.name;
    }

    public LocalDateRange getDateRange ( ) {
        return this.dateRange;
    }


    // -------|  Object  |--------------------------

    @Override
    public String toString ( ) {
        return "Machine{ " +
                "name='" + name + '\'' +
                " | dateRange=" + dateRange +
                " }";
    }

}

Давайте попробуем этот класс, написав метод main.

    public static void main ( String[] args ) {
        List < Machine > machines =
                List.of(
                        new Machine( "one" , LocalDate.parse( "2014-01-01" ) , LocalDate.parse( "2015-01-01" ) ) ,
                        new Machine( "two" , LocalDate.parse( "2013-01-01" ) , LocalDate.parse( "2013-02-01" ) ) ,  // Violates rule, where date-range should be *after* the prior one.
                        new Machine( "three" , LocalDate.parse( "2016-01-01" ) , LocalDate.parse( "2017-01-01" ) ) ,
                        new Machine( "four" , LocalDate.parse( "2017-01-01" ) , LocalDate.parse( "2018-01-01" ) ) ,
                        new Machine( "five" , LocalDate.parse( "2018-01-01" ) , LocalDate.parse( "2018-02-01" ) )
                );
        // Compare
        for ( int i = 1 ; i < machines.size() ; i++ ) { // Using annoying zero-based index counting.
            Machine prior = machines.get( i - 1 );
            Machine current = machines.get( i );
            if ( ! prior.getDateRange().isBefore( current.getDateRange() ) ) {
                System.out.println( "BAD: Machine at index " + ( i - 1 ) + " is not before Machine at index " + i + " ➙ " + prior.getDateRange().toString() + " versus " + current.getDateRange().toString() );
            }
        }
    }

При запуске.

ПЛОХО: машина с индексом 0 не раньше, чем машина с индексом 1 ➙ 2014-01-01 / 2015-01-01 против 2013-01-01 / 2013-02-01

JDBC 4.2

Начиная с JDBC 4.2 и более поздних версий мы можем напрямую обмениваться java.time объектами с базой данных.

LocalDate start = myResultSet.getObject( "start_date" , LocalDate.class ) ;

О java.time

Фреймворк java.time встроен в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 14 ноября 2018

Вы используете LocalDate?Я думаю, что это хороший выбор для сравнения дат.Это начиная с Java 8.

    package sample;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class LocalDateTest {

    public static void main(String[] args) {


        //of(year, month, day)
        LocalDate date1 = LocalDate.of(2018, 11, 11);

        LocalDate date2 = LocalDate.of(2018, 11, 14);

        long daysBetween = ChronoUnit.DAYS.between(date1, date2);

        System.out.println(daysBetween);
    }
}

LocalDate не имеет никакого открытого конструктора, вы создаете объект с помощью статического метода фабрики.Используя enum ChronoUnit, вы получаете длинное значение, представляющее дни, недели или годы от одной даты до второй даты.Если вы хотите, например, подсчитать недели между, просто замените DAYS на WEEKS

Теперь сравнивать даты просто, если значение отрицательное, первая дата превышает вторую.

Документация LocalDate: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...