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

时间:2015-05-09 01:31:09

标签: java simpledateformat milliseconds

根据SimpleDateFormat class documentationJava在日期模式中不支持超过毫秒的时间粒度。

所以,日期字符串如

  • 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实现提供模式,没有任何其他代码修改或字符串操作,是否还有其他方法可以获得正确的解决方案?

2 个答案:

答案 0 :(得分:25)

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在日期模式

中不支持milliseconds以上的时间粒度

...不再是Java 8,9,10及更高版本的内置 java.time 类。并不像Java 6和Java 7那样真实,因为大多数 java.time functionality is back-ported

java.time

SimpleDateFormat,相关的java.util.Date / .Calendar类现在已经过时了Java 8中的新java.time包(Tutorial)。

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

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对象。首先,用T替换中间的SPACE以符合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驱动程序,则可以使用toInstantfrom方法在java.sql.Timestamp和java.time之间来回切换。这些新的转换方法已添加到旧版类中。

关于 java.time

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

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 1 :(得分:0)

我发现覆盖这样的日期时间格式的多种变体非常好:

import { AppWindow } from './AppWindow.js'
const template = `
<style>
:host {
    height: 100% ;
    display: block ;
}

button {
  position: fixed;
  bottom: 10px; left: 10px;
}
</style>

  <button>Add</buton>
`
var count = 0

class WebDesktop extends window.HTMLElement {
  constructor() {
    super()
    this.attachShadow( { mode: 'open' } ).innerHTML = template
  }

  connectedCallback() {
    this.shadowRoot.querySelector( 'button' ).onclick = () => this.shadowRoot.appendChild( new AppWindow )
    this.ondragover = ev => ev.preventDefault()
    this.ondrop = ev => {
        let origin = JSON.parse( ev.dataTransfer.getData( 'text' ) )
        console.log( origin )
        let app = this.shadowRoot.getElementById( origin.id )
        let childPos = app.getBoundingClientRect()
        app.style.left = childPos.left + ( ev.clientX - origin.x ) + 'px'
        app.style.top = childPos.top + ( ev.clientY - origin.y ) + 'px'
    } 
  }
}

window.customElements.define('web-desktop', WebDesktop)