首页 > 解决方案 > Java OffsetDateTime 不同环境测试中的精度问题

问题描述

我在 Spock 中运行测试,我尝试比较两个 OffsetDateTime 对象。测试与 H2 数据库集成。我的实体名为 Beacon,它具有 creation_time 属性。这是实体:

@Entity
@Table(name = "beacons")
@Getter
@Setter
public class Beacon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    private OffsetDateTime creationTime;
}

然后,Spock 测试,我做了以下事情: 创建新的 Beacon 对象:

def beacon = new Beacon(
        id: id,
        creationTime: OffsetDateTime.now()
)

然后我将 Beacon 对象保存在数据库中,获取它并比较创建时间:

beaconRepository.save(beacon)
def savedBeacon = beaconRepository.findById(id)
beacon.getCreationTime() == savedBeacon.getCreationTime()

现在,在 Unix 和 MacOS 上一切正常,而在 Windows 上,这个测试失败了,因为 savedBeacon.getCreationTime() 返回的时间比 beacon.getCreationTime() 更精确。为什么会这样?当然,我知道几种解决方法,例如将 creationTime 定义为OffsetDateTime.now(Clock.tickMillis(ZoneId.systemDefault())),但是有更好的解决方案吗?

标签: javah2spock

解决方案


您的列在数据库中究竟是如何定义的?它应该具有TIMESTAMP(9) WITH TIME ZONE数据类型,否则值将四舍五入到微秒。

SQL 标准要求默认的小数秒精度TIMESTAMPTIMESTAMP WITH TIME ZONE数据类型为 6。

Java 8 中的各种.now()方法最多返回 3 个小数位。由于 Java 9,它们通常返回 6 或 7 位,具体取决于操作系统,并且将来可能会返回更多位(例如,在 Linux 上,9 位可用,但 Java 目前只需要 6 位)。

JSR-310 数据类型最多支持 9 位。包括 H2 在内的一些 DBMS 也支持多达 9 位(很少支持更多)。但其他一些 DBMS 最多只支持 6 位数字。


推荐阅读