【问题标题】:Create actual date in string format from microseconds with precision of microseconds从微秒以微秒的精度创建字符串格式的实际日期
【发布时间】:2020-04-25 05:00:46
【问题描述】:

我想从微秒创建一个 java.util.Date 对象。但我不想失去精确到毫秒的微秒。目前我正在通过以下方式将微秒转换为 Date 对象。 如果我将微秒传递给 Date 构造函数,那么它会给出错误的日期,例如 56521-12-01

long microSeconds = value.getTimestampValue();// This function returns a microseconds.
DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS);
long millis = TimeUnit.MILLISECONDS.convert(microSeconds, TimeUnit.MICROSECONDS);
String startDate = FORMAT.format(new Date(millis));

我不想将微秒转换为毫秒。是否有 API 或 java-8 实用程序的方法,我可以直接将微秒转换为 Date 对象?

这种方法的问题是它删除最后三位微秒使其变为毫秒,然后从毫秒创建一个日期对象,解析器只需将其毫秒部分与1000 相乘以将其转换为微秒。这样我就失去了一个实际的微发送。

【问题讨论】:

标签: java java-8 java.util.date


【解决方案1】:

您无法使用java.util.Date 执行此操作。 From the API:

Date 类表示特定的时间瞬间,精度为毫秒。

【讨论】:

  • 我的用例只是将微秒转换为“实际时间的字符串表示”,精度为微秒。我可以使用其他类来代替 Date。
  • @KhalidShah 那么你应该编辑这个问题然后说出来。
【解决方案2】:

tl;博士

Instant                                                  // Represent a moment as a count since the epoch reference of 1970-01-01T00:00Z.
.ofEpochSecond(                                          // Instantiate a `Instant` as a count of whole seconds plus fractional second.
    TimeUnit.MICROSECONDS.toSeconds( microseconds ) ,    // Count of whole seconds since the epoch reference.
    TimeUnit.MICROSECONDS.toNanos( microseconds )        // Calculate fractional second as a count of nanoseconds.
    - 
    TimeUnit.SECONDS.toNanos( 
        TimeUnit.MICROSECONDS.toSeconds( microseconds )
    ) 
)                                                        // Returns an `Instant`.
.toString()                                              // Generate text in standard ISO 8601 format.
.replace( "T" , " " )                                    // Replace a `T` with a SPACE for desired output format.
.replace( "Z" , "" )                                     // Remove the `Z` from the end, that indicates the date and time is in the context of UTC.   

避免使用旧的日期时间类

您正在使用糟糕的日期时间类,这些类在几年前被 JSR 310 中定义的现代 java.time 类所取代。

此外,正如其他人指出的那样,旧类无法容纳微秒,只能容纳毫秒。好吧,实际上,遗留类java.sql.Timestamp 试图表示纳秒,但这样做是通过一种可怕的黑客行为来实现的,其中包括以毫秒为单位的小数秒纳秒的小数秒(真是一团糟)。

java.time

样本数据

作为演示,让我们创建自 1970 年第一刻 UTC 以来的微秒样本数。

// Convert to microseconds. Use as input value to further code below.
Instant instantPrime = Instant.now().truncatedTo( ChronoUnit.MICROS ); // Drop any nanoseconds, to keep only microseconds.
long wholeSecondsPrime = instantPrime.getEpochSecond();
long nanoAdjustmentPrime = instantPrime.getNano();
long microseconds = 
    TimeUnit.SECONDS.toMicros( wholeSecondsPrime ) + 
    TimeUnit.NANOSECONDS.toMicros( nanoAdjustmentPrime )
;

处理

让我们将微秒计数分成两部分,自 1970-01-01T00:00Z 以来的整秒数,以及微秒数的小数秒。

// Convert from microseconds.
long wholeSeconds = TimeUnit.MICROSECONDS.toSeconds( microseconds );
long nanoAdjustment = TimeUnit.MICROSECONDS.toNanos( microseconds ) - TimeUnit.SECONDS.toNanos( wholeSeconds );

将它们重新组合在一起。

Instant instant = Instant.ofEpochSecond( wholeSeconds , nanoAdjustment );
boolean match = instant.equals( instantPrime );

转储到控制台。

System.out.println( "instantPrime = " + instantPrime );
System.out.println( "wholeSecondsPrime = " + wholeSecondsPrime );
System.out.println( "nanoAdjustmentPrime = " + nanoAdjustmentPrime );

System.out.println( "microseconds = " + microseconds );

System.out.println( "wholeSeconds = " + wholeSeconds );
System.out.println( "nanoAdjustment = " + nanoAdjustment );
System.out.println( "instant = " + instant );

System.out.println( "match = " + match );

instantPrime = 2020-01-07T23:32:26.385565Z

wholeSecondsPrime = 1578439946

nanoAdjustmentPrime = 385565000

微秒 = 1578439946385565

wholeSeconds = 1578439946

纳米调整 = 385565000

即时 = 2020-01-07T23:32:26.385565Z

匹配 = 真

字符串

使用标准 ISO 8601 格式生成代表 Instant 值的文本。

String output = instant.toString() ;

2020-01-07T23:32:26.385565Z

您想要的格式已接近该格式。将T 替换为空格。并删除Z,但要小心。将日期时间显示为文本而不指示时区或与 UTC 的偏移可能会给读者带来歧义和困惑。我建议包括Z

String output = instant.toString().replace( "T" , " " ).replace( "Z" , "" ) ;

2020-01-07 23:32:26.385565


关于java.time

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

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

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

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 非常感谢@Basil 提供的详细信息:)
  • 我在我的机器上执行相同的上述代码,但它会以不同的方式打印时间。 instantPrime = 2020-01-08T11:29:19.065Z wholeSecondsPrime = 1578482959 nanoAdjustmentPrime = 65000000 microseconds = 1578482959065000 wholeSeconds = 1578482959 nanoAdjustment = 65000000 instant = 2020-01-08T11:29:19.065Z match = true 它只显示时间,以毫秒为单位。
  • @KhalidShah 你在运行 Java 8 吗?该版本使用Clock 的实现,以毫秒为单位捕获当前时刻。 Java 9 和更高版本的一些实现使用新的Clock,它以微秒为单位捕获当前时刻。 java.time 类可以表示纳秒,但传统的计算机硬件时钟并不那么准确。
  • 是的,我正在使用 java 8
【解决方案3】:
int seconds = (int) (microSeconds / 1_000_000);
int nanoSeconds = ((int) (microSeconds % 1_000_000)) * 1_000;
LocalDateTime t = LocalDateTime.ofEpochSecond(seconds, nanoSeconds, ZoneOffset.UTC);

String s = t.toString(); // with a T
s = t.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); // with a T
s = t.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"));

日期的分辨率为毫秒。而且新的时间课程要好得多,虽然需要更多的努力。

【讨论】:

  • 缺少()int seconds = (int) (microSeconds / 1_000_000);
  • 错误的类。 LocalDateTime 类不能代表时刻,因为它缺少时区上下文或与 UTC 的偏移量。更改为OffsetDateTime
  • 感谢@JoopEggen,它真的很有帮助:)
【解决方案4】:

如果你的字符串包含毫秒,那么首先我们需要为它选择合适的模式。

String date = "2021-11-15T07:45:28.655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
LocalDate dt = LocalDate.parse(date, formattter);

它解决了你的问题。

如果它也包含时区,那么您需要再次更改模式。

String date = "2021-11-15T07:45:28.655-08:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSz");
LocalDate dd = LocalDate.parse(date, formatter);

【讨论】:

    【解决方案5】:

    只是使用 LocalDateTime 的替代方法:

    LocalDateTime date =
    LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
    

    但是,如果您只是想要一种“格式良好的时间戳”,您可以使用:

    LocalDateTime.now()
    

    【讨论】:

    • 问题是关于微秒,而不是毫秒。
    猜你喜欢
    • 2015-07-20
    • 2014-01-11
    • 2013-02-03
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 2015-01-10
    • 1970-01-01
    • 2019-12-21
    相关资源
    最近更新 更多