代码之家  ›  专栏  ›  技术社区  ›  Glorfindel Craig Stuntz

为什么Java的日期之后()当日期实际上更早时返回“true”?

  •  2
  • Glorfindel Craig Stuntz  · 技术社区  · 6 年前

    date1 以前是 date2 date1.after(date2) 返回 true . 时区没有影响;两个日期都是UTC。

    import java.sql.Timestamp;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.TimeZone;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            Date date1 = dateFormat.parse("2018-07-27 01:22:14.077");
            Date date2 = new Timestamp(1532654534390l);
            System.out.println(dateFormat.format(date1));
            System.out.println(dateFormat.format(date2));
            System.out.println(date1.getTime() > date2.getTime());
            System.out.println(date1.after(date2));
        }
    }
    


    2018-07-27 01:22:14.390

    是的

    这里发生了什么?


    在我真正的节目里, 日期1 从日志文件中解析 日期2 由Hibernate从数据库中检索,这会导致不同的数据类型。尽管我找到了问题的根本原因并知道如何避免这个问题,但我仍然对防止这个陷阱的解决方案非常感兴趣。

    2 回复  |  直到 6 年前
        1
  •  8
  •   Glorfindel Craig Stuntz    6 年前

    这里潜在的“问题”是 java.sql.Timestamp java.util.Date ,不在指定字段中存储毫秒( fastTime ,相当于 Unix time ),但在另一个领域 nanos . 这个 after 快速时间 字段(这是有意义的,因为它可以用于所有 Date 对象)。

    在这种情况下发生的是 快速时间 Timestamp 从1532654534 390 000 077 after() before() 在这种情况下是不可靠的;解决方法是 getTime() 时间戳

        2
  •  7
  •   Basil Bourque    6 年前

    太长,读不下去了

    使用现代的 java.time文件 类,从来没有可怕的遗留日期时间类。

    Instant
    .ofEpochMilli( 1_532_654_534_390L ) 
    .isAfter(
        LocalDateTime
        .parse( 
            "2018-07-27 01:22:14.077"
            .replace( " " , "T" ) 
        )
        .atOffset( 
            ZoneOffset.UTC
        )
        .toInstant()
    )
    

    Timestamp 对象为 Date

    您的代码:

    Date date2 = new Timestamp…  // Violates class documentation. 
    

    违反了类文档中建立的契约。

    由于Timestamp类和java.util.Date文件类,建议代码不要将时间戳值作为日期类型. 时间戳和java.util.Date文件表示实现继承,而不是类型继承。

    医生说 java.sql.Timestamp java.util.Date ,则指示您忽略继承的事实。你不能使用 时间戳 对象作为 日期 . 你的代码和医生说的一模一样 去做。

    当然,这个假装不是子类的策略是一个非常糟糕的类设计。这个黑客是 原因 永远不要使用这些类 .

    您看到的有关毫秒-分秒和纳秒不匹配的行为记录如下:

    注意:此类型是java.util.Date文件以及一个单独的纳秒值。只有整数秒存储在java.util.Date文件组件。分秒-纳米-是分开的。这个时间戳.equals(Object)方法在传递不是的实例的对象时从不返回truejava.sql.Timestamp,因为日期的纳米组成部分是未知的。因此时间戳.equals(Object)方法相对于日期类型.equals(Object)方法。另外,hashCode方法使用底层java.util.Date文件实现,因此在其计算中不包括nanos。

    你使用的是出了名的糟糕的类。你发现的问题是由于他们糟糕的设计,使用了糟糕的黑客。不要费心去理解这些课程,只要 完全避免它们 .

    班级。

    分析输入字符串。

    LocalDateTime ldt = LocalDateTime.parse( "2018-07-27 01:22:14.077".replace( " " , "T" ) ;  // Without a time zone or offset, this value has no specific meaning, is *not* a point on the timeline. 
    

    显然,您知道一个事实,即输入字符串隐式地表示UTC中的某个时刻。

    OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;  // Assign an offset-from-UTC to give the date and time a meaning as an actual point on the timeline. 
    

    Instant 阶级比阶级更基本 OffsetDateTime

    Instant instant = Instant.ofEpochMilli( 1_532_654_534_390L ) ;  // Translate a count of milliseconds from 1970-01-01T00:00:00Z into a moment on the timeline in UTC. 
    

    比较一下。

    Boolean stringIsAfterLong = odt.toInstant().isAfter( instant ) ;
    

    关于 java.time文件

    这个 java.time legacy 日期时间类,例如 java.util.Date Calendar ,& SimpleDateFormat .

    Joda-Time 项目,正在进行中 maintenance mode ,建议迁移到 java.time 班级。

    要了解更多信息,请参阅 Oracle Tutorial . 和搜索堆栈溢出的许多例子和解释。规格为 JSR 310 .

    你可以交换 java.time文件 对象直接与数据库连接。使用 JDBC driver 符合 JDBC 4.2 java.sql.* 班级。

    这个 ThreeTen-Extra 项目扩展java.time文件有额外的课程。这个项目是一个试验场,为未来可能的补充java.time文件. 您可以在这里找到一些有用的类,例如 Interval , YearWeek , YearQuarter ,和 more