根据SimpleDateFormat class documentation , Java
不支持在其日期模式中超过毫秒的时间粒度。
所以,像这样的日期字符串
当通过模式解析时
实际上解释了
.
之后的整数符号为(近 10 亿!)毫秒而不是纳秒,导致日期即提前 11 天。令人惊讶的是,使用较少数量的
S
符号仍然会导致解析所有 9 位数字(而不是最左边的 3 位 .SSS
)。有两种方法可以正确处理此问题:
是否有任何其他方法可以通过向标准提供模式来获得正确的解决方案
SimpleDateFormat
实现,没有任何其他代码修改或字符串操作?
最佳答案
tl;博士
LocalDateTime.parse( // With resolution of nanoseconds, represent the idea of a date and time somewhere, unspecified. Does *not* represent a moment, is *not* a point on the timeline. To determine an actual moment, place this date+time into context of a time zone (apply a `ZoneId` to get a `ZonedDateTime`).
"2015-05-09 00:10:23.999750900" // A `String` nearly in standard ISO 8601 format.
.replace( " " , "T" ) // Replace SPACE in middle with `T` to comply with ISO 8601 standard format.
) // Returns a `LocalDateTime` object.
不
不,您不能使用 SimpleDateFormat 来处理 nanoseconds 。
但你的前提是……
Java does not support time granularity above milliseconds in its date patterns
从 Java 8 、 9 、 10 及更高版本开始,内置 java.time 类不再如此。 Java 6 和 Java 7 也不是真的,因为大多数 java.time functionality is back-ported 。
时间
SimpleDateFormat
和相关的 java.util.Date
/.Calendar
类现在已被 Java 8 ( java.time ) 中的新 Tutorial 包过时了。新的 java.time 类支持 nanosecond 解析。该支持包括解析和生成九位小数秒。例如,当您使用
java.time.format
DateTimeFormatter
API 时,S
模式字母表示“秒的分数”而不是“毫秒”,它可以处理纳秒值。Instant
例如,
Instant
类代表 UTC 中的一个时刻。它的 toString
方法使用标准的 ISO 8601 格式生成一个 String
对象。末尾的 Z
表示 UTC,发音为“Zulu”。instant.toString() // Generate a `String` representing this moment, using standard ISO 8601 format.
2013-08-20T12:34:56.123456789Z
请注意,在 Java 8 中捕获当前时刻仅限于毫秒分辨率。 java.time 类可以以纳秒为单位保存一个值,但只能以毫秒为单位确定当前时间。这个限制是由于
Clock
的实现。在 Java 9 及更高版本中,新的 Clock
实现可以更精细地捕捉当前时刻,具体取决于主机硬件和操作系统的限制,根据我的经验,通常是 microseconds。Instant instant = Instant.now() ; // Capture the current moment. May be in milliseconds or microseconds rather than the maximum resolution of nanoseconds.
LocalDateTime
您的
2015-05-09 00:10:23.999750900
示例输入字符串缺少时区或 UTC 偏移量的指示符。这意味着它不代表一个时刻,不是时间线上的一个点。相反,它代表了大约 26-27 小时范围内的潜在时刻,即全局时区的范围。将此类输入解析为
LocalDateTime
对象。首先,将中间的 SPACE 替换为 T
以符合 ISO 8601 格式,在解析/生成字符串时默认使用。所以不需要指定格式模式。LocalDateTime ldt =
LocalDateTime.parse(
"2015-05-09 00:10:23.999750900".replace( " " , "T" ) // Replace SPACE in middle with `T` to comply with ISO 8601 standard format.
)
;
java.sql.Timestamp
java.sql.Timestamp
类也处理纳秒级分辨率,但以一种笨拙的方式。通常最好在 java.time 类中完成您的工作。从 JDBC 4.2 及更高版本开始,无需再次使用 Timestamp
。myPreparedStatement.setObject( … , instant ) ;
和检索。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
OffsetDateTime
JDBC 规范没有强制要求支持
Instant
,但 OffsetDateTime
是。因此,如果上述代码与您的 JDBC 驱动程序失败,请使用以下代码。OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
和检索。
Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;
如果使用旧的 4.2 之前的 JDBC 驱动程序,您可以使用
toInstant
和 from
方法在 java.sql.Timestamp
和 java.time 之间来回切换。这些新的转换方法被添加到旧的遗留类中。关于 java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如
java.util.Date
、 Calendar
和 SimpleDateFormat
。Joda-Time 项目,现在在 maintenance mode 中,建议迁移到 java.time 类。
要了解更多信息,请参阅 Oracle Tutorial 。并在 Stack Overflow 上搜索许多示例和解释。规范是 JSR 310 。
您可以直接与您的数据库交换 java.time 对象。使用符合 JDBC driver 或更高版本的 JDBC 4.2。不需要字符串,不需要
java.sql.*
类。从哪里获得 java.time 类?
ThreeTen-Extra 项目使用附加类扩展了 java.time。该项目是 future 可能添加到 java.time 的试验场。您可能会在这里找到一些有用的类,例如
Interval
、 YearWeek
、 YearQuarter
和 more 。
关于具有微秒或纳秒精度的 Java 日期解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30135025/