JSR-310 - parsing seconds fraction with variable length
Asked Answered
F

4

40

Is there a way how to create JSR-310 formatter that is able to parse both following date/times with variable length of seconds fraction?

2015-05-07 13:20:22.276052

or

2015-05-07 13:20:22.276

Example code:

DateTimeFormatter formatter
= new java.time.format.DateTimeFormatterBuilder()
        .append( java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") )
        .appendOptional( java.time.format.DateTimeFormatter.ofPattern(".SSSSSS") )
        .toFormatter();
formatter.parse("2015-05-07 13:20:22.276052", LocalDateTime::from);
Fibroblast answered 7/5, 2015 at 13:52 Comment(0)
M
63

This solves the problem:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendPattern("yyyy-MM-dd HH:mm:ss")
    .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
    .toFormatter();

System.out.println(LocalDateTime.parse("2015-05-07 13:20:22.276052", formatter));
System.out.println(LocalDateTime.parse("2015-05-07 13:20:22.276", formatter));
System.out.println(LocalDateTime.parse("2015-05-07 13:20:22", formatter));

// output
2015-05-07T13:20:22.276052
2015-05-07T13:20:22.276
2015-05-07T13:20:22

The answer by JiriS is incorrect, as it uses appendValue whereas the correct way is to use DateTimeFormatterBuilder.appendFraction (which also handles the decimal point). The difference can be seen in the second system out, where appendValue incorrectly parses "2015-05-07T13:20:22.000276".

When parsing, LocalDateTime.parse(str, formatter) is a neater approach than using the formatter directly in most cases.

When using the builder, take advantage of appendPattern() and optionalStart() to keep things neat.

Mattins answered 7/5, 2015 at 17:5 Comment(5)
Here you recommend using LocalDateTime.parse rather than using the formatter directly. Yet the doc for DateTimeFormatter.parse says just the opposite: “Most applications should use this method for parsing.” … LocalDateTime dt = parser.parse(str, LocalDateTime::from);Thermomagnetic
I see the confusion. The phrase "Most applications should use this method for parsing" is wrt the other parse methods in DateTimeFormatter. It wasn't intended as a comparison against the parse() methods on classes like LocalDate. Thus my answer is correct, users should prefer LocalDate.parse to DateTimeFormatter.parse.Mattins
Is there a way to create such a formatter with only a pattern string?Vinaigrette
No. Patterns are a subset of the possible formatters.Mattins
FYI to people seeing this years after it was written - that appendFraction signature does not appear to be in the latest couple of releases (at least on Maven Central, 2.9.9+). I couldn't find any alternative that worked for microseconds.Snip
L
2

The accepted answer is written by the architect of modern date-time API and is the most elegant way to solve this problem. My answer shows an alternative way to solve this problem.

This solution leverages a feature of DateTimeFormatter that allows specifying optional units using square brackets.

Demo:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
                "yyyy-MM-dd HH:mm:ss[.[SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]]",
                Locale.ENGLISH);

        // Test
        Stream.of(
                "2015-05-07 13:20:22.123456789",
                "2015-05-07 13:20:22.276052",
                "2015-05-07 13:20:22.276",
                "2015-05-07 13:20:22"
        ).forEach(s -> System.out.println(LocalDateTime.parse(s, formatter)));
    }
}

Output:

2015-05-07T13:20:22.123456789
2015-05-07T13:20:22.276052
2015-05-07T13:20:22.276
2015-05-07T13:20:22

Learn more about the modern date-time API from Trail: Date Time.

Lovely answered 15/10, 2022 at 18:12 Comment(0)
F
1

And this one works

DateTimeFormatter formatter
= new java.time.format.DateTimeFormatterBuilder()
        .append( java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") )
        .appendOptional(
                new java.time.format.DateTimeFormatterBuilder()
                    .appendLiteral('.')
                    .appendValue( ChronoField.MICRO_OF_SECOND, 1, 6, SignStyle.NOT_NEGATIVE).toFormatter())
        .toFormatter();
Fibroblast answered 7/5, 2015 at 14:11 Comment(1)
This Answer’s use of appendValue does not work in all cases. See the Answer by JodaStephen for details.Thermomagnetic
B
0

Parsing fails with a datetime without any ":" characters:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMddHHmmss")
            .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 9, false)
            .toFormatter();
        System.out.println(LocalDateTime.parse("20150507132022123", formatter));

Edit: it was appendFraction() which expects "." character.

Baboon answered 4/1, 2022 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.