问题 java.time.DateTimeFormatter:需要始终呈现毫秒的ISO_INSTANT


我正在尝试将日期时间管理的各种代码混合清理为Java 8 java.time 命名空间。现在我的默认问题很小 DateTimeFormatter 对于 Instant。该 DateTimeFormatter.ISO_INSTANT formatter仅在不等于零时显示毫秒。

时代呈现为 1970-01-01T00:00:00Z 代替 1970-01-01T00:00:00.000Z

我做了一个单元测试来解释这个问题以及我们需要如何将最终日期相互比较。

@Test
public void java8Date() {
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
    String epoch, almostEpoch, afterEpoch;

    { // before epoch
        java.time.Instant instant = java.time.Instant.ofEpochMilli(-1);
        almostEpoch = formatter.format(instant);
        assertEquals("1969-12-31T23:59:59.999Z", almostEpoch );
    }

    { // epoch
        java.time.Instant instant = java.time.Instant.ofEpochMilli(0);
        epoch = formatter.format(instant);
        // This fails, I get 1970-01-01T00:00:00Z instead
        assertEquals("1970-01-01T00:00:00.000Z", epoch );
    }

    { // after epoch
        java.time.Instant instant = java.time.Instant.ofEpochMilli(1);
        afterEpoch = formatter.format(instant);
        assertEquals("1970-01-01T00:00:00.001Z", afterEpoch );
    }

    // The end game is to make sure this rule is respected (this is how we order things in dynamo):
    assertTrue(epoch.compareTo(almostEpoch) > 0);
    assertTrue(afterEpoch.compareTo(epoch) > 0); // <-- This assert would also fail if the second assert fails

    { // to confirm we're not showing nanos
        assertEquals("1970-01-01T00:00:00.000Z", formatter.format(Instant.EPOCH.plusNanos(1)));
        assertEquals("1970-01-01T00:00:00.001Z", formatter.format(Instant.EPOCH.plusNanos(1000000)));
    }
}

4582
2018-06-26 19:02


起源

如果默认格式与零不同,则默认格式也只会呈现纳秒。如果你总是想要毫秒(而不是纳秒),为什么不简单地使用自定义格式化器,比如 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX").withZone(ZoneId.of("UTC"));? - mihi
我希望尽可能接近格式化程序 Instant::toString()。也许我应该刚刚使用了一个字符串模式。 - Florent


答案:


好的,我查看了源代码,它非常简单:

DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendInstant(3).toFormatter();

我希望它适用于所有场景,它可以帮助其他人。不要犹豫,添加更好/更清洁的答案。

只是为了解释它的来源, 在JDK的代码中

ISO_INSTANT 定义如下:

public static final DateTimeFormatter ISO_INSTANT;
static {
    ISO_INSTANT = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendInstant()
            .toFormatter(ResolverStyle.STRICT, null);
}

DateTimeFormatterBuilder::appendInstant 声明为:

public DateTimeFormatterBuilder appendInstant() {
    appendInternal(new InstantPrinterParser(-2));
    return this;
}

和构造函数 InstantPrinterParser 签名是:

InstantPrinterParser(int fractionalDigits)

11
2018-06-26 19:39



如果你链接到检查的源代码会很好。 - Basil Bourque
那么我们如何将其改为仅包括ms而不是nano - user1172490
@BasilBourque我添加了对源代码的引用 - Florent
@ user1172490我在示例代码中添加了一些代码来确认此格式化程序没有渲染nanos - Florent


接受Florent的回答 是正确和好的。

我只想补充一些说明。

提到的格式化程序, DateTimeFormatter.ISO_INSTANT,默认只适用于 Instant 类。其他课程如 OffsetDateTime 和 ZonedDateTime 可以默认使用其他格式化程序。

java.time类提供最高分辨率 纳秒,比粒度更细 毫秒。这意味着小数部分最多9位数而不仅仅是3位数。

的行为 DateTimeFormatter.ISO_INSTANT 取决于小数部分中的位数。正如医生所说(强调我的):

格式化时,始终输出秒的分钟。纳秒秒 输出零,三,六或九位数 有必要的。

所以取决于其中包含的数据值 Instant 对象,您可以看到以下任何输出:

2011-12-03T10:15:30Z

2011-12-03T10:15:30.100Z

2011-12-03T10:15:30.120Z

2011-12-03T10:15:30.123Z

2011-12-03T10:15:30.123400Z

2011-12-03T10:15:30.123456Z

2011-12-03T10:15:30.123456780Z

2011-12-03T10:15:30.123456789Z

Instant class是java.time的基本构建块。经常用于数据传递,数据存储和数据交换。生成用于呈现给用户的数据的字符串表示时,请使用 OffsetDateTime 要么 ZonedDateTime


2
2017-07-27 23:57