Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 DateTimeFormatter parsing for optional fractional seconds of varying significance

Tags:

My MCVE (as a TestNG unit test):

public class MyDateTimeFormatterTest {      private static final String BASE_PATTERN = "yyyy/MM/dd HH:mm:ss";     private static final DateTimeFormatter FORMATTER =             DateTimeFormatter.ofPattern(BASE_PATTERN + "[.SSSSSSSSS]");     private static final LocalDateTime TEST_INPUT =             LocalDateTime.of(2015, 5, 4, 12, 34, 56, 123456789);      @DataProvider(name = "test-cases")     public Iterator<Object[]> getTestCases() {         return Arrays.asList(testFor("", ChronoUnit.SECONDS),                 testFor(".SSS", ChronoUnit.MILLIS),                 testFor(".SSSSSS", ChronoUnit.MICROS),                 testFor(".SSSSSSSSS", ChronoUnit.NANOS)).iterator();     }      @Test(dataProvider = "test-cases")     public void testWithDefaultResolution(String input, LocalDateTime output) {         assertThat(FORMATTER.parse(input, LocalDateTime::from), equalTo(output));     }      private Object[] testFor(String patternSuffix, TemporalUnit truncatedTo) {         return new Object[] { DateTimeFormatter.ofPattern(BASE_PATTERN + patternSuffix)                 .format(TEST_INPUT), TEST_INPUT.truncatedTo(truncatedTo) };     } } 

I am trying to test the parsing of a date-time String with optional fractional seconds of varying significance using DateTimeFormatter. The relevant part of the Javadoc reads:

Fraction: Outputs the nano-of-second field as a fraction-of-second. The nano-of-second value has nine digits, thus the count of pattern letters is from 1 to 9. If it is less than 9, then the nano-of-second value is truncated, with only the most significant digits being output.

Based on my limited understanding, I used [...] to mark the fractional seconds as optional, and since I'm interested in varying significance, I thought I should stick to SSSSSSSSS.

However, the unit test fails at parsing up to milliseconds and microseconds, i.e. the second and third cases. Changing the ResolverStyle to LENIENT does not help here as it fails at the parsing stage, not the resolution.

May I know which approaches should I consider to resolve my problem? Should I be using DateTimeFormatterBuilder to optionally specify each fractional digit (9 times), or is there a 'smarter' way with my pattern?

edit I found my own answer in the end... will still leave this as unanswered for a day and see if there are other approaches or not.

like image 623
h.j.k. Avatar asked May 07 '15 02:05

h.j.k.


People also ask

What is datetimeformatter in Java?

java.time.format.DateTimeFormatter public final class DateTimeFormatter extends Object Formatter for printing and parsing date-time objects. This class provides the main application entry point for printing and parsing and provides common implementations of DateTimeFormatter :

How to parse a date with an offset in datetimeformatter?

DateTimeFormatter With Predefined Instances DateTimeFormatter comes with multiple predefined date/time formats that follow ISO and RFC standards. For example, we can use the ISO_LOCAL_DATE instance to parse a date such as ‘2018-03-09': To parse a date with an offset, we can use ISO_OFFSET_DATE to get an output like ‘2018-03-09-03:00':

Which time format does not use adjacent value parsing?

The second format does not use adjacent value parsing, because the fields are separated by the dash (they are not adjacent!). See docs.oracle.com/javase/8/docs/api/java/time/format/…

Why can't datetimeformatter handle two numeric fields without separation?

Please refer to Michael’s answer. DateTimeFormatter based on patterns are not smart enough to handle both an optional section and the possibility to have two numeric fields without separation.


1 Answers

Oh cool, another 15 minutes of troubleshooting yielded this:

private static final DateTimeFormatter FORMATTER =      new DateTimeFormatterBuilder().appendPattern(BASE_PATTERN) // .parseLenient()         .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter(); 

edit parseLenient() is optional.

like image 158
h.j.k. Avatar answered Oct 13 '22 01:10

h.j.k.