根据 SimpleDateFormat 类文档, Java
在其日期模式中不支持毫秒以上的时间粒度。
所以,像这样的日期字符串
- 2015-05-09 00:10:23.999750900 // 最后9位表示纳秒
当通过模式解析时
- yyyy-MM-dd HH:mm:ss.SSSSSSSSS // 9 个“S”符号
实际上将 .
符号后的整数解释为(将近 10 亿!)毫秒而不是纳秒,从而得到日期
- 2015-05-20 21:52:53 UTC
即提前超过 11 天。令人惊讶的是,使用较少数量的 S
符号仍然会导致所有 9 位数字被解析(而不是最左边的 3 位代表 .SSS
)。
有两种方法可以正确处理这个问题:
- 使用字符串预处理
- 使用自定义 SimpleDateFormat 实现
是否有任何其他方法可以通过仅向标准 SimpleDateFormat
实现提供模式来获得正确的解决方案,而无需任何其他代码修改或字符串操作?
原文由 PNS 发布,翻译遵循 CC BY-SA 4.0 许可协议
tl;博士
没有
不,您不能使用 SimpleDateFormat 来处理 纳秒。
但是你的前提是……
…从 Java 8 、 9 、 10 和更高版本的内置 java.time 类开始不再适用。 Java 6 和 Java 7 也不是真的,因为大多数 java.time 功能都是向后移植的。
java.time
SimpleDateFormat
和相关的java.util.Date
/.Calendar
类现在已被新的 Java.time 包中的 教程 淘汰。新的 java.time 类支持 纳秒 分辨率。该支持包括解析和生成九位小数秒。例如,当您使用
java.time.format
DateTimeFormatter
API 时,S
模式字母表示秒的“分数”而不是“秒”处理纳秒值。Instant
例如,
Instant
类表示 UTC 中的时刻。它的toString
方法使用标准 ISO 8601 格式生成一个String
对象。最后的Z
表示 UTC,发音为“Zulu”。请注意,在 Java 8 中捕获当前时刻仅限于毫秒分辨率。 java.time 类可以 保存 以纳秒为单位的值,但只能确定以毫秒为单位的当前时间。此限制是由于
Clock
的实施。在 Java 9 及更高版本中,一个新的Clock
实现可以更精细的分辨率抓取当前时刻,这取决于你的主机硬件和操作系统的限制,根据我的经验通常是 微秒。LocalDateTime
您的示例输入字符串
2015-05-09 00:10:23.999750900
缺少时区指示符或与 UTC 的偏移量。这意味着它 不代表 一个时刻, 不是 时间轴上的一个点。相反,它代表了大约 26-27 小时范围内的 潜在 时刻,即全球时区范围。将这样的输入分解为
LocalDateTime
对象。首先,将中间的 SPACE 替换为T
以符合 ISO 8601 格式,在解析/生成字符串时默认使用。因此无需指定格式化模式。java.sql.Timestamp
java.sql.Timestamp
类也处理纳秒分辨率,但方式很尴尬。通常最好在 java.time 类中完成工作。从 JDBC 4.2 及更高版本开始,无需再次使用Timestamp
。和检索。
OffsetDateTime
对
Instant
的支持不是 JDBC 规范强制要求的,但是OffsetDateTime
是。因此,如果上面的代码在您的 JDBC 驱动程序中失败,请使用以下代码。和检索。
如果使用较早的 4.2 之前的 JDBC 驱动程序,您可以使用
toInstant
和from
方法在java.sql.Timestamp
之间来回切换这些新的转换方法被添加到 旧 的遗留类中。关于 java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 旧 日期时间类,例如
java.util.Date
、Calendar
和SimpleDateFormat
现在处于 维护模式 的 Joda-Time 项目建议迁移到 java.time 类。
要了解更多信息,请参阅 Oracle 教程。并在 Stack Overflow 中搜索许多示例和解释。规范是 JSR 310 。
您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,不需要
java.sql.*
类。在哪里获取 java.time 类?
ThreeTen-Extra 项目用附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可能会在这里找到一些有用的类,例如
Interval
、YearWeek
、YearQuarter
等等。