Parsing Instant using OpenCsv

1k views Asked by At

I am trying to parse Instant from a CSV using OpenCsv this way:

@CsvDate("yyyy-MM-dd hh:mm:ss")
@CsvBindByName(column = "date")
private Instant date;

I know that OpenCsv is supposed to support java.time.

But when trying to use it I am getting the following exception:

Error parsing CSV line: 8.

...

Caused by: java.time.format.DateTimeParseException: Text '2022-04-21 00:00:00' could not be parsed: Unable to obtain Instant from TemporalAccessor: {HourOfAmPm=0, MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, MinuteOfHour=0, SecondOfMinute=0},ISO resolved to 2022-04-21 of type java.time.format.Parsed

...

Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {HourOfAmPm=0, MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, MinuteOfHour=0, SecondOfMinute=0},ISO resolved to 2022-04-21 of type java.time.format.Parsed

...

Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds

What comes from my investigation, is that the root cause is the lack of a time zone, but my question is how do I specify the time zone using the annotation only?

2

There are 2 answers

4
Joop Eggen On BEST ANSWER

See DateTimeFormatter. It should be HH for 24 hours time (0-23) i.o. hh for 12 hours time (1-12). The error is caused by hour "00". Fortunately, otherwise you would have only the first half of the day.

See h, k, H and K.

The class Instant does not use units like seconds, is more a long internally. Use LocalDateTime for your format.

0
nyg On

What comes from my investigation, is that the root cause is the lack of a time zone, but my question is how do I specify the time zone using the annotation only?

I haven't find a way to change the timezone of the DateTimeFormatter used by OpenCSV. You could, in theory, use @CsvCustomBindByName and provide a custom converter in which you can use your own DateTimeFormatter with the timezone you want.

However, if you also control how your CSV is generated, you can simply use the following datetime patterns to make it work with Instant:

@CsvBindByName(column = "date")
@CsvDate(
        value = "yyyy-MM-dd'T'HH:mm:ssX",
        writeFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'",
        writeFormatEqualsReadFormat = false)
private Instant date;

value is the pattern used when reading a bean from a CSV file. X is a pattern letter which will corresponds to the zone offset, i.e. when reading the 'Z' character, it will assume the time to be in UTC+00:00. See doc.

When writing a bean to a CSV file, the writeFormat pattern will be used and needs to be different, you need to print the 'Z' char manually at the end, hence the escaped 'Z'. The reason is that internally OpenCSV does the following when writing an Instant:

LocalDateTime ldt = LocalDateTime.ofInstant((Instant) value, ZoneId.of("UTC"));
return writeDtf.format(ldt);

LocalDateTime does not have a timezone, so the X cannot be used. Arguably, they could instead do the following:

return writeDtf.withZone(ZoneOffset.UTC).format(value);

Which would then allow us to use the X. They could also do the same when parsing a date to an Instant, in order to be consistent.