【问题标题】:DateTime does not equal itself after unserialization反序列化后 DateTime 不等于自身
【发布时间】:2014-01-08 17:22:55
【问题描述】:

正如您在下面看到的,在序列化和反序列化之后,您会得到据称不同的 DateTime 实例:

scala> import org.joda.time.DateTime
import org.joda.time.DateTime

scala> val a = new DateTime()
a: org.joda.time.DateTime = 2014-01-08T19:00:08.883+02:00

scala> val b = DateTime.parse(a.toString())
b: org.joda.time.DateTime = 2014-01-08T19:00:08.883+02:00

scala> a == b
res0: Boolean = false

根据AbstractInstant's javadocequals,“根据毫秒瞬间、年表和时区,将此对象与指定对象进行比较是否相等。”所以这不应该发生对吗?我错过了什么?

【问题讨论】:

  • 会不会是因为你是在比较==而不是equals
  • 如果您使用的是 Scala,您是否可以相应地标记您的问题以避免混淆。
  • @gtgaxiola @RakeshKR,实际上,在 Scala 中,==Any 上调用 equals 的最终方法。

标签: scala datetime serialization deserialization jodatime


【解决方案1】:

这是唯一正确的答案,通过自己的测试找到:

DateTime a = new DateTime(); // uses default time zone
System.out.println(a); // 2014-01-08T19:38:00.696+01:00

DateTime b = DateTime.parse(a.toString());
System.out.println(b); // 2014-01-08T19:38:00.696+01:00

System.out.println(a.getChronology()); // ISOChronology[Europe/Berlin]
System.out.println(b.getChronology()); // ISOChronology[+01:00]
System.out.println(a.equals(b)); // false!!!

我假设在 Scala 中,== 的比较确实意味着 equals() 的比较,正如 @aris1348880 在他的评论中所说,因此我在翻译成 java 代码时相应地替换了运算符。

所以equals()-比较失败的原因很明显:是DateTime对象atoString()-方法中没有正确打印时区id。我认为它是 JodaTime 中的一个错误,因为 toString() 应该始终打印不可变值对象的整个状态。顺便说一句,在这个细节上,老java.util.Date 更糟糕!好吧,作为解决方法,您可以使用 JodaTime 的格式引擎来正确打印。

System.out.println(
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'['ZZZ']'").print(a));
// Output: 2014-01-08T19:38:00.696[Europe/Berlin]

为了让行为更清晰:

DateTime 构造函数使用默认时区(在我的测试欧洲/柏林)。它的 toString() 方法打印的是偏移量,而不是实际时区(这就是问题所在)。

DateTime-parse()-method 根据 JodaTime 文档 ISODateTimeFormat#dateTimeParser() 使用,它再次只能解析偏移量,但在这段代码中,它也只提供了偏移量信息,而不是实时时区,否则解析会因异常而停止。

在这两种情况下,DateTime 实例的创建方式不同,它们具有不同的时区/偏移量,因此具有不同的年表。因此,equals() 的结果乍一看令人惊讶,但可以理解。

【讨论】:

  • 在我看来,错误在于parse 没有使用默认时区......但是解析来自toString 方法的日期是相当奇怪的。
  • @JonathanDrapeau 不解析方法是正确的,因为它读取时区字符串“+01:00”并将其视为偏移量,到目前为止还可以(文本中的时区信息必须优先与格式化程序对象中的时区设置相比)。但除此之外,我同意整个过程有点奇怪。我也不会将其称为“反序列化”,就像格式化和重新解析一样。
  • 当使用像 DateTime 这样的复杂类型时,最好避免使用通用的方式来构建对象。在示例中,问题在于时区,但毫秒是相同的,因此 a.compareTo(b) == 0。复杂类型的相等并不总是一个简单的概念,因为这种类型的对象实例的状态相等。例如,BigDecimal 相等与 compareTo(值)不同,因为相等性检查值和比例,而 compareTo 仅值所以 new BigDecimal("1.0").equals(new BigDecimal("1.00") 为 false 而 new BigDecimal("1.0 ").compareTo(new BigDecimal("1.00") == 0.
  • 感谢您的洞察力。我想问题是 Joda DateTime 提供了比 ISO8601 格式更多的细节。我将继承 DateTime 并覆盖 equals 以使年表比较不那么精确。
  • 我不知道 scala 是否可以做子类魔术,但至少在纯 Java 子类中 DateTime 是不可能的,因为该类是最终的。据我所知,JodaTime 项目负责人 S. Colebourne 后来对拥有pluggable chronology 的设计决定感到遗憾。
【解决方案2】:

我在 GitHub 上打开了一个问题,here is the author's response

很久以前我选择了DateTime的toString格式,我省略了包含值类型的完整状态。结果,两个对象看起来是相等的(通过 toString),但实际上并非如此。这是一个错误,因为它会导致这种混乱。不幸的是,它无法更正。

解析行为也很不幸,因为它过于关注偏移量而没有足够关注时区(因为 ISO-8601 不处理时区 ID)。同样,在这里进行更改为时已晚。

【讨论】:

    【解决方案3】:

    我没有 org.joda.time.DateTime 但我只是尝试使用 java.util.Date 进行测试。

    你应该试试这个:

    val a = new DateTime()
    val b = new DateTime(a.getMillis())
    a == b
    

    我也试过这个:

    import org.joda.time.DateTime;
    
    public class TestDateTime {
    
        public static void main(String[] args) {
            DateTime a = new DateTime();
            System.out.println(a.toString());
            DateTime b = DateTime.parse(a.toString());
            System.out.println(b.toString());
            System.out.println(a.equals(b));
        }
    }
    

    这是输出:

    2014-01-08T19:05:52.182+01:00
    2014-01-08T19:05:52.182+01:00
    false
    

    问题是 DateTime 的 equals 因为不同的时区而失败。我认为假设以这种不同方式创建的两个 DateTime 实例等价是错误的。

    当您对 java.util.Date 进行序列化和反序列化时,传递的 long(包装的值)不是字符串。

    【讨论】:

    • 抱歉为什么是-2?是为了 == 而不是等于?你不知道使用 scala 解释器 a == b 意味着 a.equals(b) 吗?见stackoverflow.com/questions/7681161/…
    • 我赞成你证明原因不是使用 scala 运算符 ==。
    • 这种方法的缩小之处在于你不再有一个人类可读的序列化日期。
    • 对不起@SamDufel 你指的是哪种方法?我的意思是 DateTime 的哪种方法:equals 或 toString 或 parse?
    • 我认为问题在于 toString 和 parse 没有绑定,因此假设等价是错误的。只是合同的问题。我更喜欢将人类可读性与序列化分开(当序列化和反序列化一个对象时,两个实例必须相等)
    【解决方案4】:

    == 将比较参考,equals() 比较值。试试a.equals(b)

    查看这篇文章:What is the difference between == vs equals() in Java?

    【讨论】:

    • 在看了 scala 男孩对 scala 中 == 含义的评论后,这个答案似乎不正确。但即使equals() 也会产生错误,正如我在测试中所证明的那样,请参阅我的答案。
    • 这适用于 java,而在 scala 中 ==() 调用 equals() 比较值和 x.eq(y) checks whether y is a reference to x
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    相关资源
    最近更新 更多