将日期与Arraylist中的下一个对象日期进行比较无法正常工作

时间:2018-11-14 10:17:22

标签: java date

我有一个记录列表,每个记录中都有日期对象。我正在检查第一条记录的日期应早于第二条记录,第二条记录的日期应少于第三条记录,依此类推。这是我的代码。

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 :(得分:3)

您过于复杂了。检查成对条目就足够了,因为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()处替换正确的getter调用。

如果您还需要检查每台机器的开始日期和结束日期的顺序正确(使用增强的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替换为LocalDateDate类存在设计问题,并且已经过时了。尽管它的名称不代表日期,但代表时间点。 LocalDate是现代Java日期和时间API java.time的一部分,使用起来非常好。

您需要做的另一件事是让数据库查询按开始日期对条目进行排序,然后知道您以正确的顺序获得了它们。

您的代码出了什么问题?

首先,您的代码在不可读的边界上很复杂。

接下来,当两个条目ij的开始日期顺序错误时,即j日期早于i日期,则{ {1}}为负,machines.get(j).getStrt_Dt().compareTo(machines.get(i).getStrt_Dt())为正。确实是一样的比较,只是相反。因此乘积将为负,您的machines.get(i).getStrt_Dt().compareTo(machines.get(j).getStrt_Dt()条件将为false,并且不会引发异常。

仅当两个条目具有完全相同的日期(以毫秒为单位)时,>= 0返回0,乘积为0,compareTo为true,并且将引发您的异常。

答案 1 :(得分:2)

tl; dr

使用现代的java.time.LocalDateorg.threeten.extra.LocalDateRange类将您的业务逻辑简化为基本上一行,询问列表中的上一个项目是否不在当前项目之前。

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

另请参阅correct Answer by Ole V.V.

避免使用旧的日期时间类

切勿使用设计不良的旧类。 java.util.Date类(尽管命名)代表一个时刻,一个带有UTC时间的日期。 java.sql.Date类伪装为仅表示日期,但实际上还涉及时间和偏移量。

java.time

现代方法使用 java.time 类。

LocalDate

LocalDate类表示没有日期,没有time zoneoffset-from-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对象,而不是仅使用整数,可以使您的代码更具自文档性,确保有效值并提供type-safety

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

您可以直接解析标准ISO 8601格式的字符串:YYYY-MM-DD

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

要解析其他格式,请搜索堆栈溢出以了解DateTimeFormatter

LocalDateRange

您似乎在Machine类中使用了一对日期,即开始日期和结束日期。

有一类。将ThreeTen-Extra库添加到您的项目中以访问LocalDateRange类。此类具有一些非常有用的比较方法。使用此类大大简化了代码,并使业务逻辑的意图更加明显。

将您的停止/开始日期存储为LocalDateRange对象上的Machine对象。

private LocalDateRange dateRange; 

半开

通常,跟踪时间跨度的最佳方法是半开放式方法。开头是包含,结尾是包含

因此,一周的定义是从一天(例如星期一)开始,一直到(但不包括)下周的同一天,例如下一个星期一。一年从一年的1月1日开始,一直到第二年的1月1日,但确实包括在内。

使用半开放式方法将意味着定义您的示例日期,如下所示。请注意,将第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及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

您是否正在使用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没有任何公共构造函数,您可以通过静态工厂方法创建一个对象。 通过使用枚举ChronoUnit,您将获得一个长值,表示从一个日期到第二个日期的天,周或年。如果你想计算星期之间的时间,只需用WEEKS代替DAYS。

现在比较日期很简单,如果值是负数,则第一个日期超过第二个日期。

LocalDate的文档:https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html