I have timecodes with this structure hh:mm:ss.SSS
for which i have a own Class, implementing the Temporal Interface.
It has the custom Field TimecodeHour Field allowing values greater than 23 for hour.
I want to parse with DateTimeFormatter. The hour value is optional (can be omitted, and hours can be greater than 24); as RegEx (d*dd:)?dd:dd.ddd
For the purpose of this Question my custom Field can be replaced with the normal HOUR_OF_DAY Field.
My current Formatter
DateTimeFormatter UNLIMITED_HOURS = new DateTimeFormatterBuilder() .appendValue(ChronoField.HOUR_OF_DAY, 2, 2,SignStyle.NEVER) .appendLiteral(':') .parseDefaulting(TimecodeHour.HOUR, 0) .toFormatter(Locale.ENGLISH); DateTimeFormatter TIMECODE = new DateTimeFormatterBuilder() .appendOptional(UNLIMITED_HOURS) .appendValue(MINUTE_OF_HOUR, 2) .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2) .appendFraction(MILLI_OF_SECOND, 3, 3, true) .toFormatter(Locale.ENGLISH);
Timecodes with a hour value parse as expected, but values with hours omittet throw an Exception
java.time.format.DateTimeParseException: Text '20:33.123' could not be parsed at index 5
I assume, as hour and minute have the same pattern, the parser starts at front and captures the minute value for the optional section. Is this right, and how can solve this?
Advertisement
Answer
Try with two optional parts (one with hours, other without) like in:
var formatter = new DateTimeFormatterBuilder() .optionalStart() .appendValue(HOUR_OF_DAY, 2, 4, SignStyle.NEVER).appendLiteral(":") .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(":") .appendValue(SECOND_OF_MINUTE, 2) .optionalEnd() .optionalStart() .parseDefaulting(HOUR_OF_DAY, 0) .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(":") .appendValue(SECOND_OF_MINUTE, 2) .optionalEnd() .toFormatter(Locale.ENGLISH);
I do not know about TimecodeHour
, so I used HOUR_OF_DAY
to test
(also too lazy to include fractions)