关于simpledateformat:具有微秒或纳秒精度的Java日期解析

Java date parsing with microsecond or nanosecond accuracy

根据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位数字(而不是例如.SSS的最左边3位)。

有两种方法可以正确处理此问题:

  • 使用字符串预处理
  • 使用自定义的SimpleDateFormat实现

是否可以通过仅向标准SimpleDateFormat实现提供模式而无需任何其他代码修改或字符串操纵的方式来获得正确的解决方案?


TL;博士

1
2
3
4
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来处理纳秒。

但是你的前提是……

Java does not support time granularity above milliseconds in its date patterns

Ok.

…从Java 8、9、10及更高版本开始,内置Java.time类不再适用。 Java 6和Java 7也并非如此,因为大多数java.time功能都是反向移植的。

java.time

SimpleDateFormat和相关的java.util.Date / .Calendar类现在已被Java 8(教程)中新的java.time包取代。

新的java.time类支持纳秒分辨率。该支持包括解析并生成9位数的小数秒。例如,当您使用java.time.format DateTimeFormatter API时,S模式字母表示"秒的分数"而不是"毫秒",它可以处理纳秒的值。

<5233>

例如,Instant类表示UTC中的时刻。其toString方法使用标准ISO 8601格式生成String对象。最后的Z表示UTC,发音为" Zulu"。

1
instant.toString()  // Generate a `String` representing this moment, using standard ISO 8601 format.

2013-08-20T12:34:56.123456789Z

Ok.

请注意,在Java 8中捕获当前时刻仅限于毫秒分辨率。 java.time类可以保存一个以纳秒为单位的值,但是只能以毫秒为单位确定当前时间。此限制归因于Clock的实现。在Java 9和更高版本中,新的Clock实现可以更好地解析当前时刻,具体取决于主机硬件和操作系统的限制,根据我的经验,通常为微秒。

1
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格式,在解析/生成字符串时默认使用该格式。因此,无需指定格式化模式。

1
2
3
4
5
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

1
myPreparedStatement.setObject( … , instant ) ;

和检索。

1
Instant instant = myResultSet.getObject( … , Instant.class ) ;

OffsetDateTime

JDBC规范未强制支持Instant,但OffsetDateTime是必需的。因此,如果以上代码在JDBC驱动程序中失败,请使用以下代码。

1
2
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;

和检索。

1
Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;

如果使用旧版4.2之前的JDBC驱动程序,则可以使用toInstantfrom方法在java.sql.Timestamp和java.time之间来回切换。这些新的转换方法已添加到旧的旧类中。

Table of date-time types in Java (both legacy and modern) and in standard SQL

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。

您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

  • Java SE 8,Java SE 9,Java SE 10和更高版本

  • 内置。
  • 标准Java API的一部分,具有捆绑的实现。
  • Java 9添加了一些次要功能和修复。
  • Java SE 6和Java SE 7

  • java.time的许多功能在ThreeTen-Backport中都被反向移植到Java 6和7。
  • Android的

  • 更高版本的Android捆绑了java.time类的实现。
  • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。请参阅如何使用ThreeTenABP…。
  • ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter等。

    好。