Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.time.DateTimeFormatter : Need ISO_INSTANT that always renders milliseconds

I'm trying to cleanup a mix of various code around datetime management to only Java 8 java.time namespace. Right now I have a small issue with the default DateTimeFormatter for Instant. The DateTimeFormatter.ISO_INSTANT formatter only shows milliseconds when they are not equal to zero.

The epoch is rendered as 1970-01-01T00:00:00Z instead of 1970-01-01T00:00:00.000Z.

I made a unit test to explain the problem and how we need to final dates to be compared one to each other.

@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)));     } } 
like image 410
Florent Avatar asked Jun 26 '16 19:06

Florent


People also ask

Which time format is followed in Java 8?

For time patterns, use mm for two-digit minute and ss for two-digit second. The hh pattern generates the two-digit hour for a 12-hour clock (e.g., 6PM is "06") and HH generates the two-digit hour for a 24-hour clock (e.g., 6PM is "18").

Is Java time format DateTimeFormatter thread-safe?

Yes, it is: DateTimeFormat is thread-safe and immutable, and the formatters it returns are as well.

What is the role of the DateTimeFormatter type in Java?

The DateTimeFormatter class is used to both parse and format dates according to specified Date and Time Patterns. Use parse(...) method to convert from String to Date/Time classes, use format(...) method to convert from Date/Time into String.

What is iso_instant?

The ISO_INSTANT formatter is a special case formatter designed to work with Instant . If you are using a ZonedDateTime you should use a different formatter, such as ISO_DATE_TIME or ISO_ZONED_DATE_TIME .


2 Answers

OK, I looked at the the source code and it's pretty straightforward:

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

I hope it works for all scenarios, and it can help someone else. Don't hesitate to add a better/cleaner answer.

Just to explain where it comes from, in the JDK's code,

ISO_INSTANT is defined like this:

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

And DateTimeFormatterBuilder::appendInstant is declared as:

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

And the constructor InstantPrinterParser signature is:

InstantPrinterParser(int fractionalDigits) 
like image 191
Florent Avatar answered Sep 18 '22 18:09

Florent


The accepted Answer by Florent is correct and good.

I just want to add some clarification.

The mentioned formatter, DateTimeFormatter.ISO_INSTANT, is default only for the Instant class. Other classes such as OffsetDateTime and ZonedDateTime may use other formatters by default.

The java.time classes offer a resolution up to nanosecond, much finer granularity than milliseconds. That means up to 9 digits in the decimal fraction rather than merely 3 digits.

The behavior of DateTimeFormatter.ISO_INSTANT varies by the number of digits in the decimal fraction. As the doc says (emphasis mine):

When formatting, the second-of-minute is always output. The nano-of-second outputs zero, three, six or nine digits as necessary.

So depending on the data value contained within the Instant object, you may see any of these outputs:

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

The Instant class is meant to be the basic building block of java.time. Use it frequently for data passing, data storage, and data exchange. When generating String representations of the data for presentation to users, use OffsetDateTime or ZonedDateTime.

like image 41
Basil Bourque Avatar answered Sep 20 '22 18:09

Basil Bourque