1

I am receiving timestamp in format : HHmmss followed by milleseconds and microseconds.Microseconds after the '.' are optional

For example: "timestamp ":"152656375.489991" is 15:26:56:375.489991. Below code is throwing exceptions:

final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
      .appendPattern("HHmmssSSS")
      .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
      .toFormatter();
LocalTime.parse(dateTime,FORMATTER);

Can someone please help me with DateTimeformatter to get LocalTime in java.

Here is the stacktrace from the exception from the code above:

java.time.format.DateTimeParseException: Text '152656375.489991' could not be parsed: Conflict found: NanoOfSecond 375000000 differs from NanoOfSecond 489991000 while resolving  MicroOfSecond
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1959)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1894)
    at java.base/java.time.LocalTime.parse(LocalTime.java:463)
    at com.ajax.so.Test.main(Test.java:31)
Caused by: java.time.DateTimeException: Conflict found: NanoOfSecond 375000000 differs from NanoOfSecond 489991000 while resolving  MicroOfSecond
    at java.base/java.time.format.Parsed.updateCheckConflict(Parsed.java:329)
    at java.base/java.time.format.Parsed.resolveTimeFields(Parsed.java:462)
    at java.base/java.time.format.Parsed.resolveFields(Parsed.java:267)
    at java.base/java.time.format.Parsed.resolve(Parsed.java:253)
    at java.base/java.time.format.DateTimeParseContext.toResolved(DateTimeParseContext.java:331)
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1994)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1890)
    ... 3 more
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
RGoyal
  • 165
  • 3
  • 16
  • try https://stackoverflow.com/questions/30103167/jsr-310-parsing-seconds-fraction-with-variable-length – Vivek Jan 09 '20 at 05:59
  • Please share the stack trace of the exception you are getting there, as the code looks correct to me. Also share the exact string value dateTime is initialised with. – AurA Jan 09 '20 at 11:23
  • Are there always exactly 6 digits after the point if there are any? Or could the number of digits vary? – Ole V.V. Jan 09 '20 at 23:08

1 Answers1

0

There are many options, depending on the possible variations in the strings you need to parse.

1. Modify the string so you need no formatter

    String timestampString = "152656375.489991";
    timestampString = timestampString.replaceFirst(
            "^(\\d{2})(\\d{2})(\\d{2})(\\d{3})(?:\\.(\\d*))?$", "$1:$2:$3.$4$5");
    System.out.println(timestampString);
    LocalTime time = LocalTime.parse(timestampString);
    System.out.println(time);

The output from this snippet is:

15:26:56.375489991

The replaceFirst() call modifies your string into 15:26:56.375489991, the default format for LocalTime (ISO 8601) so it can be parsed without any explicit formatter. For this I am using a regular expression that may not be too readable. (…) enclose groups that I use as $1, $2, etc., in the replacement string. (?:…) denotes a non-capturing group, that is, cannot be used in the replacement string. I put a ? after it to specify that this group is optional in the original string.

This solution accepts from 1 through 6 decimals after the point and also no fractional part at all.

2. Use a simpler string modification and a formatter

I want to modify the string so I can use this formatter:

private static DateTimeFormatter fullParser
        = DateTimeFormatter.ofPattern("HHmmss.[SSSSSSSSS][SSS]");

This requires the point to be after the seconds rather than after the milliseoncds. So move it three places to the left:

    timestampString = timestampString.replaceFirst("(\\d{3})(?:\\.|$)", ".$1");
    LocalTime time = LocalTime.parse(timestampString, fullParser);

15:26:56.375489991

Again I am using a non-capturing group, this time to say that after the (captured) group of three digits must come either a dot or the end of the string.

3. The same with a more flexible parser

The formatter above specifies that there must be either 9 or 3 digits after the decimal point, which may be too rigid. If you want to accept something in between too, a builder can build a more flexible formatter:

private static DateTimeFormatter fullParser = new DateTimeFormatterBuilder()
        .appendPattern("HHmmss")
        .appendFraction(ChronoField.NANO_OF_SECOND, 3, 9, true)
        .toFormatter();

I think that this would be my favourite approach, again depending on the exact requirements.

4. Parse only a part of the string

There is no problem so big and awful that it cannot simply be run away from (Linus in Peanuts, from memory)

If you can live without the microseconds, ignore them:

private static DateTimeFormatter partialParser
        = DateTimeFormatter.ofPattern("HHmmssSSS");

To parse only a the part of the string up to the point using this formatter:

    TemporalAccessor parsed
            = partialParser.parse(timestampString, new ParsePosition(0));
    LocalTime time = LocalTime.from(parsed);

15:26:56.375

As you can see it has ignored the part from the decimal point, which I wouldn’t find too satisfactory.

What went wrong in your code?

Your 6 digits after the decimal point denote nanoseconds. Microseconds would have been only 3 decimals after the milliseconds. To use appendFraction() to parse these you would have needed a TemporalUnit of nano of millisecond. The ChronoUnit enum offers nano of day and nano of second, but not nano of milli. TemporalUnit is an interface, so in theory we could develop our own nano of milli class for the purpose. I tried to develop a class implementing TemporalUnit once, but gave up, I couldn’t get it to work.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161