Java日期解析具有微秒或纳秒精度

PNS*_*PNS 21 java milliseconds simpledateformat

根据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实现提供模式来获得正确的解决方案,而无需任何其他代码修改或字符串操作?

Bas*_*que 27

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.
Run Code Online (Sandbox Code Playgroud)

不,你不能使用SimpleDateFormat来处理纳秒.

但你的前提是......

Java 在其日期模式中不支持超过毫秒的时间粒度

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

java.time

SimpleDateFormat,以及相关的java.util.Date/ .Calendar班现在由新过时java.time在Java中8(发现包教程).

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

Instant

例如,Instant该类表示UTC中的时刻.其toString方法String使用标准ISO 8601格式生成对象.在Z上月底表示UTC,发音为"祖鲁".

instant.toString()  // Generate a `String` representing this moment, using standard ISO 8601 format.
Run Code Online (Sandbox Code Playgroud)

2013-08-20T12:34:56.123456789Z

请注意,捕获Java 8中的当前时刻仅限于毫秒级分辨率.该java.time类可以保持在纳秒的值,但只能确定当前时间毫秒.这种限制是由于实施Clock.在Java 9及更高版本中,新的Clock实现可以以更精细的分辨率获取当前时刻,具体取决于主机硬件和操作系统的限制,通常是我的经验中的微秒.

Instant instant = Instant.now() ;  // Capture the current moment. May be in milliseconds or microseconds rather than the maximum resolution of nanoseconds.
Run Code Online (Sandbox Code Playgroud)

LocalDateTime

您的示例输入字符串2015-05-09 00:10:23.999750900缺少时区指示符或UTC的偏移量.这意味着它并没有代表了一下,是不是在时间轴上的一个点.相反,它代表了全球约26-27小时范围内的潜在时刻.

将此类输入视为LocalDateTime对象.首先,将a中间的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.
        ) 
;
Run Code Online (Sandbox Code Playgroud)

java.sql.Timestamp

java.sql.Timestamp班也处理纳秒级的分辨率,但在一个尴尬的方式.通常最好在java.time类中完成你的工作.Timestamp从JDBC 4.2及更高版本开始,无需再次使用.

myPreparedStatement.setObject( … , instant ) ;
Run Code Online (Sandbox Code Playgroud)

并检索.

Instant instant = myResultSet.getObject( … , Instant.class ) ;
Run Code Online (Sandbox Code Playgroud)

OffsetDateTime

InstantJDBC规范没有强制要求支持,但是OffsetDateTime.因此,如果上述代码因JDBC驱动程序而失败,请使用以下代码.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 
myPreparedStatement.setObject( … , odt ) ;
Run Code Online (Sandbox Code Playgroud)

并检索.

Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;
Run Code Online (Sandbox Code Playgroud)

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


关于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-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


flo*_*rad 16

我发现涵盖日期时间格式的多种变体非常棒,如下所示:

final DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"))
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
Run Code Online (Sandbox Code Playgroud)