Hi I am having a java program . In my java program the input will be date strings of different formats like the following
a) 2021-10-09T08:00:01.111Z ( with millisecond)
b) 2021-10-09T08:00:01Z ( without milliseconds)
c) 2021-10-09T08:00Z ( no seconds )
d) 2021-10-09T08Z ( no minutes )
Currently I am using a built in date formatter DateTimeFormatter.ISO_OFFSET_DATE_TIME . However when i run my sample snippet program it fails
the following is my sample program
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Tester2 {
public static void main(String[] args) {
OffsetDateTime t1 = OffsetDateTime.parse("2021-10-09T08:00:01.111Z");;
System.out.println(t1.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t2 = OffsetDateTime.parse("2021-10-09T08:00:01Z");;
System.out.println(t2.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t3 = OffsetDateTime.parse("2021-10-09T08:00Z");;
System.out.println(t3.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t4 = OffsetDateTime.parse("2021-10-09T08Z");;
System.out.println(t4.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
}
However when i run my progam it fails with the followign output.
2021-10-09T08:00:01.111Z
2021-10-09T08:00:01Z
2021-10-09T08:00:00Z
Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-10-09T08Z' could not be parsed at index 13
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:404)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:389)
at test.Tester2.main(Tester2.java:20)
The following is the output I want
2021-10-09T08:00:01.111Z
2021-10-09T08:00:01Z
2021-10-09T08:00:00Z
2021-10-09T08:00:00Z
Is there any java built in data formatter that will help me to achieve the desired output. If there is no such dateformatter could you help me to write a new date formatter ? Anyother solutions are welcome.
thank you
CodePudding user response:
There's probably a better solution, but SimpleDateFormat will at a bare minimum allow you to customize the format per object. For example:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.parse("2021-10-09T08:00:01Z");
Would parse into the desired date object. If you want to do the opposite and go from date to string:
sdf.format(new Date(1633780801000));
CodePudding user response:
One the one hand that might look weird that Java lacks functionality of parsing partial date formats (I'm facing such issues in every heterogeneous project), however, on the other hand when we parsing partial date formats we need somehow compensate missing fields, and the problem is such compensations/heuristics depend on either particular project or, moreover, particular interaction between systems, please check the PoC below, hope it will help:
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
public class DateTimeFormats {
public static final DateTimeFormatter ISO_LOCAL_TIME = new DateTimeFormatterBuilder()
.appendValue(HOUR_OF_DAY, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 0, 9, true)
.toFormatter();
public static final DateTimeFormatter ISO_OPTIONAL_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendLiteral('T')
.append(ISO_LOCAL_TIME)
.parseStrict()
.toFormatter();
public static final DateTimeFormatter ISO_OPTIONAL_OFFSET = new DateTimeFormatterBuilder()
.appendOffsetId()
.toFormatter();
public static final DateTimeFormatter ISO_LOCAL_DATE_OPTIONAL_TIME = new DateTimeFormatterBuilder()
.append(ISO_LOCAL_DATE)
.appendOptional(ISO_OPTIONAL_TIME)
.toFormatter();
public static final DateTimeFormatter ISO_DATE_OPTIONAL_TIME = new DateTimeFormatterBuilder()
.append(ISO_LOCAL_DATE_OPTIONAL_TIME)
.appendOptional(ISO_OPTIONAL_OFFSET)
.optionalStart()
.appendLiteral('[')
.parseCaseSensitive()
.appendZoneRegionId()
.appendLiteral(']')
.toFormatter()
// compensation
.withZone(ZoneId.systemDefault())
.withChronology(IsoChronology.INSTANCE);
public static OffsetDateTime offsetDateTime(CharSequence text) {
return offsetDateTime(text, ISO_DATE_OPTIONAL_TIME);
}
public static OffsetDateTime offsetDateTime(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, DateTimeFormats::offsetDateTime);
}
public static OffsetDateTime offsetDateTime(TemporalAccessor temporal) {
try {
LocalDate date = LocalDate.from(temporal);
Optional<LocalTime> time = Optional.ofNullable(temporal.query(TemporalQueries.localTime()));
Optional<ZoneOffset> offset = Optional.ofNullable(temporal.query(TemporalQueries.offset()));
Optional<ZoneId> zone = Optional.ofNullable(temporal.query(TemporalQueries.zoneId()));
// compensation
LocalDateTime result = LocalDateTime.of(date, time.orElse(LocalTime.MIN));
if (offset.isPresent()) {
return OffsetDateTime.of(result, offset.get());
} else if (zone.isPresent()) {
return ZonedDateTime.of(result, zone.get())
.toOffsetDateTime();
}
return result
// compensation
.atZone(ZoneId.systemDefault())
.toOffsetDateTime();
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain OffsetDateTime from TemporalAccessor: "
temporal " of type " temporal.getClass().getName(), ex);
}
}
}
System.out.println(offsetDateTime("2021-10-09T08:00:01.1111111Z"));
System.out.println(offsetDateTime("2021-10-09T08:00:01.111Z"));
System.out.println(offsetDateTime("2021-10-09T08:00:01Z"));
System.out.println(offsetDateTime("2021-10-09T08:00Z"));
System.out.println(offsetDateTime("2021-10-09T08Z"));
System.out.println(offsetDateTime("2021-10-09Z"));
System.out.println(offsetDateTime("2021-10-09[Australia/Melbourne]"));
System.out.println(offsetDateTime("2021-10-09T08:00:01.111[Australia/Melbourne]"));
System.out.println(offsetDateTime("2021-10-09T08:00:01[Australia/Melbourne]"));
System.out.println(offsetDateTime("2021-10-09T08:00[Australia/Melbourne]"));
System.out.println(offsetDateTime("2021-10-09T08[Australia/Melbourne]"));
System.out.println(offsetDateTime("2021-10-09[Australia/Melbourne]"));
2021-10-09T08:00:01.111111100Z
2021-10-09T08:00:01.111Z
2021-10-09T08:00:01Z
2021-10-09T08:00Z
2021-10-09T08:00Z
2021-10-09T00:00Z
2021-10-09T00:00 11:00
2021-10-09T08:00:01.111 11:00
2021-10-09T08:00:01 11:00
2021-10-09T08:00 11:00
2021-10-09T08:00 11:00
2021-10-09T00:00 11:00
CodePudding user response:
DataTimeFormatter
supports optional parsing patterns with [
and ]
characters.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH[:mm[:ss[.SSS]]]X");
OffsetDateTime t1 = OffsetDateTime.parse("2021-10-09T08:00:01.111Z", formatter);
System.out.println(t1.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t2 = OffsetDateTime.parse("2021-10-09T08:00:01Z", formatter);
System.out.println(t2.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t3 = OffsetDateTime.parse("2021-10-09T08:00Z", formatter);
System.out.println(t3.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
OffsetDateTime t4 = OffsetDateTime.parse("2021-10-09T08Z", formatter);
System.out.println(t4.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));