Sia*_*Sia 1 java datetime java-11 offsetdatetime java-17
在 Java 11 中,时钟系统使用毫秒精度,但显然在 Java 13 及更高版本中,它使用微秒精度,这导致我的测试失败。例如,OffsetDateTime.now()当我从数据库“2021-12-10T10:58:05.309595+01:00”读取此日期时,给我这个日期“2021-12-10T10:58:05.309594500+01:00”。我正在寻找一种方法,可以以它们应该相等的方式格式化第一个日期。我确实希望将其设置为 OffsetDateTime 类型而不是字符串。
更新:我意识到这个问题是在我将java版本从11升级到17时出现的,而不是在本地,当gitlab运行测试时我遇到了这个问题。
这是测试:
@Test
fun `can store, find and delete a failed-message`() {
// given: a failed-message
val failedMessage = FailedMessage(
failedMessageId = FailedMessageId("the-subcription", "the-message-id"),
messageAttributes = mapOf("one" to "een", "two" to "twee"),
messagePayload = "message-payload",
exception = "exception",
dateTime = OffsetDateTime.now(),
stackTrace = "stackey tracey"
)
failedMessageRepository.store(failedMessage)
assertEquals(failedMessage, failedMessageRepository.find(failedMessage.failedMessageId))
}
Run Code Online (Sandbox Code Playgroud)
由于日期时间不相等,该测试失败。这是日志:
<FailedMessage(failedMessageId=the-subcription-the-message-id, messageAttributes={one=een, two=twee}, messagePayload=message-payload, exception=exception, dateTime=2021-12-10T10:58:05.309594500+01:00, stackTrace=stackey tracey)>
but was:
<FailedMessage(failedMessageId=the-subcription-the-message-id, messageAttributes={one=een, two=twee}, messagePayload=message-payload, exception=exception, dateTime=2021-12-10T10:58:05.309595+01:00, stackTrace=stackey tracey)>
Run Code Online (Sandbox Code Playgroud)
请你帮助我好吗?
AnOffsetDateTime 始终具有纳秒精度。It\xe2\x80\x99snow方法可能没有,具体取决于平台和 Java 版本。对象内部有。OffsetDateTime所以秒数不可能少于9 位小数。
打印多少位小数是另一个问题。当您打印 an 时,真正打印的内容OffsetDateTime是 a String。如果您只是打印该对象,toString则会隐式调用其方法来生成我们在打印中看到的字符串。OffsetDateTime.toString()总是为我们提供渲染完整精度所需的尽可能多的 3 位小数组。因此,如果小数为.100000000,则.100打印。如果它们是.309594500,正如您已经看到的,则所有 9 位小数都出现在字符串中。您无法改变这种行为。
如果您想打印不同的字符串,您可以使用DateTimeFormatter带有您喜欢的小数位数的 a 。
那么你可以做什么:
\nOffsetDateTime,或者实际上,您可以像旧的那样创建一个新的,只是更多的小数为零,这反过来可能会导致 打印更少的小数toString()。OffsetDateTime为您喜欢的字符串。以下代码片段结合了这两个选项。为了仅格式化小数点到最后一个非零小数点,我使用以下格式化程序:
\nprivate static final DateTimeFormatter ODT_FORMATTER = new DateTimeFormatterBuilder()\n .appendPattern("uuuu-MM-dd'T'HH:mm.ss")\n .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)\n .appendOffsetId()\n .toFormatter(Locale.ROOT);\nRun Code Online (Sandbox Code Playgroud)\n示范:
\n OffsetDateTime dateTime = OffsetDateTime.parse("2021-12-10T10:58:05.309594500+01:00");\n\n int nanos = dateTime.getNano();\n OffsetDateTime with7Decimals = dateTime.withNano(nanos / 100 * 100);\n System.out.println("toString(): " + with7Decimals);\n System.out.println("Formatted: " + with7Decimals.format(ODT_FORMATTER));\n OffsetDateTime with5Decimals = dateTime.withNano(nanos / 10000 * 10000);\n System.out.println("toString(): " + with5Decimals);\n System.out.println("Formatted: " + with5Decimals.format(ODT_FORMATTER));\nRun Code Online (Sandbox Code Playgroud)\n输出:
\n\n\nRun Code Online (Sandbox Code Playgroud)\ntoString(): 2021-12-10T10:58:05.309594500+01:00\nFormatted: 2021-12-10T10:58.05.3095945+01:00\ntoString(): 2021-12-10T10:58:05.309590+01:00\nFormatted: 2021-12-10T10:58.05.30959+01:00\n
对于您的测试,我认为没有理由使用字符串进行比较。OffsetDateTime只需将这些小数设置为零后比较对象即可,无论如何,这些小数都会在数据库中丢失。
编辑:测试失败的解决方法?在下面的行中,您可以获取当前时间,其精度比数据库可以存储的精度更高。
\n dateTime = OffsetDateTime.now(),\nRun Code Online (Sandbox Code Playgroud)\n因此,将其更改为仅提供数据库支持的精度:
\n dateTime = OffsetDateTime.now().truncatedTo(ChronoUnit.MICROS),\nRun Code Online (Sandbox Code Playgroud)\n这应该会导致时间被保存和检索而无需任何进一步舍入。
\n为什么会出现这个问题呢? OffsetDateTime.now()在不同平台和Java版本上有不同的精度。我不知道任何平台上的 Java 11 和 13 之间存在差异,但没有理由认为 xe2x80x99 应该是其中之一,并且在即将推出的版本中可能会有另一个。此外,如果您在 Linux、Windows 和 Mac 之间移动,即使使用相同的 Java 版本,您也可能会遇到差异。问题的另一部分是数据库的精度有限,或者更准确地说,是数据库中用于存储时间的数据类型。如果您使用timestamp with time zone,您可能会获得数据库可以提供的最佳精度。显然,您的数据库具有微秒精度(秒为 6 位小数),而 Java 13 上OffsetDateTime.now()至少具有半微秒(500 纳秒)精度。因此,从 Java 13 开始,您将获得数据库无法存储的值。显然,您的数据库或数据库驱动程序随后将您的情况的值向上舍入。这导致从数据库检索回的值与您尝试保存的值不完全相同。