Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSR-310 - parsing seconds fraction with variable length

Tags:

java

java-time

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);
like image 296
JiriS Avatar asked May 07 '15 13:05

JiriS


3 Answers

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.

like image 115
JodaStephen Avatar answered Nov 20 '22 19:11

JodaStephen


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();
like image 30
JiriS Avatar answered Nov 20 '22 21:11

JiriS


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.

like image 1
Arvind Kumar Avinash Avatar answered Nov 20 '22 19:11

Arvind Kumar Avinash