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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30135025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 09:12:33  来源:igfitidea点击:

Java date parsing with microsecond or nanosecond accuracy

javasimpledateformatmilliseconds

提问by PNS

According to the SimpleDateFormat class documentation, Javadoes not support time granularity above milliseconds in its date patterns.

根据SimpleDateFormat 类文档Java其日期模式不支持超过毫秒的时间粒度。

So, a date string like

所以,像这样的日期字符串

  • 2015-05-09 00:10:23.999750900 // The last 9 digits denote nanoseconds
  • 2015-05-09 00:10:23.999750900 // 最后 9 位数字表示纳秒

when parsed via the pattern

当通过模式解析时

  • yyyy-MM-dd HH:mm:ss.SSSSSSSSS // 9 'S' symbols
  • yyyy-MM-dd HH:mm:ss.SSSSSSSS // 9 个“S”符号

actually interprets the whole number after the .symbol as (nearly 1 billion!) milliseconds and not as nanoseconds, resulting in the date

实际上将.符号后面的整数解释为(近 10 亿!)毫秒而不是纳秒,从而得到日期

  • 2015-05-20 21:52:53 UTC
  • 2015-05-20 21:52:53 UTC

i.e. over 11 days ahead. Surprisingly, using a smaller number of Ssymbols still results in all 9 digits being parsed (instead of, say, the leftmost 3 for .SSS).

即提前 11 天。令人惊讶的是,使用较少数量的S符号仍然会导致所有 9 位数字都被解析(而不是最左边的 3 个.SSS)。

There are 2 ways to handle this issue correctly:

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

  • Use string preprocessing
  • Use a custom SimpleDateFormat implementation
  • 使用字符串预处理
  • 使用自定义 SimpleDateFormat 实现

Would there be any other way for getting a correct solution by just supplying a pattern to the standard SimpleDateFormatimplementation, without any other code modifications or string manipulation?

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

采纳答案by Basil Bourque

tl;dr

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.

Nope

No, you cannot use SimpleDateFormat to handle nanoseconds.

不,您不能使用 SimpleDateFormat 来处理nanoseconds

But your premise that…

但你的前提是……

Java does not support time granularity above millisecondsin its date patterns

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

…is no longer true as of Java 8, 9, 10 and later with java.timeclasses built-in. And not really true of Java 6 and Java 7 either, as most of the java.timefunctionality is back-ported.

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

java.time

时间

SimpleDateFormat, and the related java.util.Date/.Calendarclasses are now outmoded by the new java.timepackage found in Java 8 (Tutorial).

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

The new java.time classes support nanosecondresolution. That support includes parsing and generating nine digits of fractional second. For example, when you use the java.time.formatDateTimeFormatterAPI, the Spattern letter denotes a "fraction of the second" rather than "milliseconds", and it can cope with nanosecond values.

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

Instant

Instant

As an example, the Instantclass represents a moment in UTC. Its toStringmethod generates a Stringobject using the standard ISO 8601format. The Zon the end means UTC, pronounced “Zulu”.

例如,Instant该类表示UTC 中的一个时刻。它的toString方法String使用标准ISO 8601格式生成一个对象。在Z上月底手段UTC,发音为“祖鲁”。

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

2013-08-20T12:34:56.123456789Z

2013-08-20T12:34:56.123456789Z

Note that capturing the current moment in Java 8 is limited to millisecond resolution. The java.timeclasses can holda value in nanoseconds, but can only determine the current time with milliseconds. This limitation is due to the implementation of Clock. In Java 9 and later, a new Clockimplementation can grab the current moment in finer resolution, depending on the limits of your host hardware and operating system, usually microsecondsin my experience.

请注意,在 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.

LocalDateTime

LocalDateTime

Your example input string of 2015-05-09 00:10:23.999750900lacks an indicator of time zone or offset-from-UTC. That means it does notrepresent a moment, is nota point on the timeline. Instead, it represents potentialmoments along a range of about 26-27 hours, the range of time zones around the globe.

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

Pares such an input as a LocalDateTimeobject. First, replace the SPACE in the middle with a Tto comply with ISO 8601 format, used by default when parsing/generating strings. So no need to specify a formatting pattern.

将此类输入解析为LocalDateTime对象。首先,将中间的空格替换为 aT以符合 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

The java.sql.Timestampclass also handles nanosecond resolution, but in an awkward way. Generally best to do your work inside java.time classes. No need to ever use Timestampagain as of JDBC 4.2 and later.

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

myPreparedStatement.setObject( … , instant ) ;

And retrieval.

和检索。

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

OffsetDateTime

OffsetDateTime

Support for Instantis not mandated by the JDBC specification, but OffsetDateTimeis. So if the above code fails with your JDBC driver, use the following.

InstantJDBC 规范并未强制要求支持,但OffsetDateTime确实如此。因此,如果上述代码与您的 JDBC 驱动程序失败,请使用以下代码。

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

And retrieval.

和检索。

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

If using an older pre-4.2 JDBC driver, you can use toInstantand frommethods to go back and forth between java.sql.Timestampand java.time. These new conversion methods were added to the oldlegacy classes.

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

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

Java(旧版和现代版)和标准 SQL 中的日期时间类型表



About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310

You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*classes.

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

Where to obtain the java.time classes?

从哪里获得 java.time 类?

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by flowgrad

I found the it wonderful to cover multiple variants of date time format like this:

我发现像这样涵盖日期时间格式的多种变体真是太好了:

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);