Java compareTo 日期不能相等

Java compareTo Dates cannot be equal

我正在尝试比较 2 个日期,第一个日期来自 MySQL 数据库,第二个日期来自当前日期。

如下所示,数据库中有不同的日期

但问题是我得到了 3 个 if 语句,它们应该告诉我的程序数据库日期是早于、晚于还是等于当前日期。 Before 和 After 语句应该有效,但它可以看到日期 2018-06-12 应该等于当前日期,因此它以 "before statement".

结尾

希望你能看到我做错了什么。

private static void Resetter() throws ParseException, SQLException {
    String host = "****";
    String username = "root";
    String mysqlpassword = "";

    //Querys
    String query = "select * from accounts";
    String queryy = "update accounts set daily_search_count = 0 where id = ?";
    Connection con = DriverManager.getConnection(host, username, mysqlpassword);
    Statement st = con.createStatement();
    ResultSet rs = st.executeQuery(query);

    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
    dateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ));
    Date currentDate = new Date();

    while(rs.next()){
        System.out.println(dateFormat.format(currentDate));
        if (rs.getDate(5).compareTo(currentDate) > 0) {
           // System.out.println("Database-date is after currentDate");
        } else if (rs.getDate(5).compareTo(currentDate) < 0) {
           // System.out.println("Database-date is before currentDate");
            PreparedStatement updatexdd = con.prepareStatement(queryy);
            updatexdd.setInt(1, rs.getInt(1));
            int updatexdd_done = updatexdd.executeUpdate();
        } else if (rs.getDate(5).compareTo(currentDate) == 0) {
           // System.out.println("Database-date is equal to currentDate");
        } else {
            System.out.println("Any other");
        }
    }
}

ResultSet#getDate returns 删除了时间部分的日期。实例化一个新的 Date 对象 确实 包含时间,所以你必须自己删除它。例如:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date currentDate = cal.getTime();

这完全取决于您使用的 Java 版本。如果您使用 Java 6 或 Java 7 那么这种方法将是最简单的:

无论如何,当您处理 java.sql.Date 时,您可以使用这种快速方法获取不带时间部分的日期,并将 java.sql.Date 与 java.sql.Date 进行比较:

Date currentDate = new java.sql.Date(System.currentTimeMillis());

您还可以使用 java.util.Date#beforejava.util.Date#after 方法以获得更好的代码可读性:

while(rs.next()){
    System.out.println(dateFormat.format(currentDate));
    if (rs.getDate(5).after(currentDate)) {
       // System.out.println("Database-date is after currentDate");
    } else if (rs.getDate(5).before(currentDate)) {
       // System.out.println("Database-date is before currentDate");
        PreparedStatement updatexdd = con.prepareStatement(queryy);
        updatexdd.setInt(1, rs.getInt(1));
        int updatexdd_done = updatexdd.executeUpdate();
    } else {
       // System.out.println("Database-date is equal to currentDate");
    }
}

如果您使用的是 Java 8,那么您可以使用新的 Java 时间 API。

tl;博士

LocalDate today = LocalDate.now( ZoneOffset.UTC ) ;
Instant instant = myResultSet.getObject( … , Instant.class ) ;  // Retrieve a `TIMESTAMP WITH TIME ZONE` value in database as an `Instant` for a date with time-of-day in UTC with a resolution as fine as nanoseconds.
LocalDate ld = instant.atOffset( ZoneOffset.UTC ).toLocalDate() ;  // Extract a date-only value without time-of-day and without time zone.

if ( ld.isBefore( today ) ) { … }       // Compare `LocalDate` objects.
else if ( ld.isEqual( today ) ) { … }
else if ( ld.isAfter( today ) ) { … }
else { … handle error }

java.time

您正在使用和滥用麻烦的旧日期时间 classes。

正如其他人指出的那样:

  • A SQL-standard DATE 类型只包含一个没有时间和时区的日期
  • 遗留 java.util.Date class 的命名有误,其中包含日期 UTC 中的时间。
  • 旧版 java.sql.Date class 假装只包含一个日期但实际上有一个时间,因为这个 class 继承自上面的那个,而文档告诉我们在我们的用法中忽略这个事实。 (是的,这很混乱,是一个糟糕的设计,一个笨拙的 hack。)

永远不要使用 java.util.Datejava.util.Calendarjava.sql.Timestampjava.sql.Date 和相关的 class。相反,只使用正常的 java.time classes。他们在干净的设计和对日期时间处理的深入理解方面处于行业领先地位,这些理解来自其前身 Joda-Time 项目的经验。

对于仅日期值,存储在 SQL-标准数据库类型 DATE 中,使用 java.time.LocalDate.

LocalDate ld = myResultSet.get( … , LocalDate.class ) ;  // Retrieving from database.
myPreparedStatement.setObject( … , ld ) ;  // Storing in database.

对于存储在 SQL 标准数据库类型 TIMESTAMP WITH TIME ZONE 中的 UTC 时间日期,使用 java.time.InstantInstant class 表示 UTC 时间轴上的一个时刻,分辨率为 纳秒 (向上到九 (9) 位小数)。

Instant instant = myResultSet.get( … , Instant.class ) ;  // Retrieving from database.
myPreparedStatement.setObject( … , instant ) ;  // Storing in database.

要在 Java 中进行比较,请使用 isEqualisBeforeisAfterequalscompare 方法。

Boolean overdue = someLocalDate.isAfter( otherLocalDate ) ;

时区

时区对于确定某一时刻的日期和时间至关重要 (Instant/TIMESTAMP WITH TIME ZONE)。

从数据库中检索您的 TIMESTAMP WITH TIME ZONE 值作为 Instant 后,调整为 time zone or offset-from-UTC 您想要用于感知日期和时间的挂钟时间-日。对于时区,应用 ZoneId 以获得 ZonedDateTime 对象。对于与 UTC 的偏移量,应用 ZoneOffset 以获得 OffsetDateTime 对象。在任何一种情况下,通过调用 toLocalDate 获取一个 LocalDate 对象来提取一个仅限日期的值。

对于您的情况,您显然希望将日期视为 UTC。所以应用常量 ZoneOffset.UTC 得到 OffsetDateTime

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;  
LocalDate ld = odt.toLocalDate() ;   // Extract a date-only value without time-of-day and without time zone.

我们想与 UTC 中的当前日期进行比较。

LocalDate today = LocalDate.now( ZoneOffset.UTC ) ;  // Specify the offset/zone by which you want to perceive the current date.

比较。

if ( ld.isBefore( today ) ) { … }
else if ( ld.isEqual( today ) ) { … }
else if ( ld.isAfter( today ) ) { … }
else { … handle error }

ISO 8601

避免不必要地使用自定义格式,例如 "yyyy/MM/dd"。尽可能使用标准 ISO 8601 格式。

对于仅限日期的值,则为 YYYY-MM-DD。

String output = LocalDate.now().toString() ;  // Ex: 2018-01-23

示例 H2

这是从 SQL-标准 DATE 类型的数据库列写入、查询和读取 LocalDate 对象的完整示例。

使用 H2 Database Engine, as I am not a MySQL 用户。创建内存数据库而不是写入存储。我假设 MySQL 的代码几乎相同。

try (
    Connection conn = DriverManager.getConnection( "jdbc:h2:mem:trashme" )
) {
    String sql = "CREATE TABLE " + "tbl_" + " (\n" +
                     "  uuid_ UUID DEFAULT random_uuid() , \n" +  // Every table should have a primary key.
                     "  when_ DATE \n" +                          // Three columns per the Question.
                     " );";
    try (
        Statement stmt = conn.createStatement() ;
    ) {
        stmt.execute( sql );
    }

    sql = "INSERT INTO tbl_ ( when_ ) VALUES ( ? ) ;";
    LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 23 );
    LocalDate ld = start;  // Keep the `start` object for use later.
    try (
        PreparedStatement ps = conn.prepareStatement( sql )
    ) {
        for ( int i = 1 ; i <= 10 ; i++ ) {
            ps.setObject( 1 , ld );
            ps.executeUpdate();
            // Prepare for next loop.
            int randomNumber = ThreadLocalRandom.current().nextInt( 1 , 5 + 1 ); // Pass minimum & ( maximum + 1 ).
            ld = ld.plusDays( randomNumber ); // Add a few days, an arbitrary number.
        }
    }

    // Dump all rows, to verify our populating of table.
    System.out.println( "Dumping all rows: uuid_ & when_ columns." );
    sql = "SELECT uuid_ , when_ FROM tbl_ ; ";
    int rowCount = 0;
    try (
        Statement stmt = conn.createStatement() ;
        ResultSet rs = stmt.executeQuery( sql ) ;
    ) {
        while ( rs.next() ) {
            rowCount++;
            UUID uuid = rs.getObject( 1 , UUID.class );
            LocalDate localDate = rs.getObject( 2 , LocalDate.class );
            System.out.println( uuid + " " + localDate );
        }
    }
    System.out.println( "Done dumping " + rowCount + " rows." );


    // Dump all rows, to verify our populating of table.
    System.out.println( "Dumping rows where `when_` is after " + start + ": uuid_ & when_ columns." );
    sql = "SELECT uuid_ , when_ FROM tbl_ WHERE when_ > ? ; ";
    rowCount = 0; // Reset count.
    final PreparedStatement ps = conn.prepareStatement( sql );
    ps.setObject( 1 , start );
    try (
        ps ;
        ResultSet rs = ps.executeQuery() ;
    ) {
        while ( rs.next() ) {
            rowCount++;
            UUID uuid = rs.getObject( 1 , UUID.class );
            LocalDate localDate = rs.getObject( 2 , LocalDate.class );
            System.out.println( uuid + " " + localDate );
        }
    }
    System.out.println( "Done dumping " + rowCount + " rows." );

} catch ( SQLException eArg ) {
    eArg.printStackTrace();
}

当运行.

Dumping all rows: uuid_ & when_ columns.
e9c75998-cd67-4ef9-9dce-6c1eed170387 2018-01-23
741c1452-e224-4e5e-95bc-904d8db58b39 2018-01-27
413de43c-a1be-40b6-9ccf-a9b7d9ba873c 2018-01-31
e2aa148f-48b6-4be6-a0fe-f2881b6b5a63 2018-02-03
f498003c-2d8b-446e-ac55-6d7568ce61c3 2018-02-06
c41606d7-8c05-4bba-9f8e-2a0d1f1bb31a 2018-02-09
3df3abe3-1865-4632-99c4-6cd74883c1ee 2018-02-10
914153fe-34f2-4e4f-a91b-944314994839 2018-02-13
96436bdf-80ee-4afe-b55d-f240140ace6a 2018-02-16
82b43f7b-077d-45c1-8c4f-f5b30dfdd44a 2018-02-19
Done dumping 10 rows.
Dumping rows where `when_` is after 2018-01-23: uuid_ & when_ columns.
741c1452-e224-4e5e-95bc-904d8db58b39 2018-01-27
413de43c-a1be-40b6-9ccf-a9b7d9ba873c 2018-01-31
e2aa148f-48b6-4be6-a0fe-f2881b6b5a63 2018-02-03
f498003c-2d8b-446e-ac55-6d7568ce61c3 2018-02-06
c41606d7-8c05-4bba-9f8e-2a0d1f1bb31a 2018-02-09
3df3abe3-1865-4632-99c4-6cd74883c1ee 2018-02-10
914153fe-34f2-4e4f-a91b-944314994839 2018-02-13
96436bdf-80ee-4afe-b55d-f240140ace6a 2018-02-16
82b43f7b-077d-45c1-8c4f-f5b30dfdd44a 2018-02-19
Done dumping 9 rows.

关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time项目,现在处于维护模式,建议迁移到java.timeclasses.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

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

从哪里获得java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.