Java 8 DateTime API parsing date string when both pattern and value are unknown

benw :

I have a requirement to parse an unknown date time string using an also unknown but supplied date time format. I want to parse to a ZonedDateTime. If there is no zone contained in the pattern string, I want to assume UTC timezone. The problem is, I'm not sure how to determine if the pattern string contains timezone information so that I can append .withZone(ZoneOffset.UTC) when I create the DateTimeFormatter. How should this be done?

String dateTimeFormat = "yyyy-MM-dd HH:mm:ss Z"; // this will be unknown (could be any possible valid pattern)
String dateTimeValue = "2001-01-01 00:00:00 -0800"; // this will also be unknown

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat);
if(????) { // how to determine if the pattern contains timezone information
    dateTimeFormatter = dateTimeFormatter.withZone(ZoneOffset.UTC);
}

ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeValue, dateTimeFormatter);
Basil Bourque :

Your example formatting pattern is not correct for the DateTimeFormatter class. For example uppercase YYYY means a week-based year, not likely the intention of the sender. The DD means day-of-year (1-366), again not likely the intention of the sender.

DateTimeFormatter formatting codes

You will need to learn the codes being passed to you in such strings. Then you will need to translate to the proper formatting pattern codes used by the DateTimeFormatter class.

DateTimeFormatterBuilder

You will likely benefit from using the very flexible and powerful DateTimeFormatterBuilder class where you can build up a format by multiple calls. After all the necessary calls, you finally generate a DateTimeFormatter by calling DateTimeFormatterBuilder::toFormatter.

Search Stack Overflow for examples using this class.

Other issues

how to determine if the pattern string contains timezone information

The Z in the passed formatting code tells you.

I can append .withZone(ZoneOffset.UTC)

If you are trying to adjust to UTC, do that after you have parsed the input string into a date-time object. To get to UTC, merely call toInstant, as an Instant is always in UTC, by definition.

I'm not adjusting to UTC, I want to set the zone as UTC if there is no zone specified in the pattern.

If your input string lacks any indicator of time zone or offset-from-UTC, then parse as a LocalDateTime. That class purposely lacks any concept of time zone or offset-from-UTC. As such, a LocalDateTime does not represent a moment, is not a point on the timeline.

If you know for certain the string without zone/offset was indeed actually intended for a particular zone/offset, then:

With a OffsetDateTime or ZonedDateTime, you now have a moment, a point in time.

What is the difference between zone and offset? An offset is merely a number of hours-minutes-seconds, nothing more. A zone, in contrast, is much more. A zone is a history of the past, present, and future changes to the offset used by the people of a particular region.

LocalDateTime ldt = LocalDateTime.parse( "2018-01-23T01:23:45.123" ) ;  // *Not* a moment, *not* a point on the timeline.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; // Assign an offset to determine a moment.

You seem to be overly focused on parsing as a way of applying your business logic. Instead, make your parsing as simple and direct as possible to get a java.time object matching the input. Only after parsing should you be making adjustments or transformations on your date-time value. Lastly separate both this parsing and transforming code from your other business logic work, so you have three distinct phases: (1) simply/directly parse, (2) adjust/transform value, (3) utilize date-time value for business purpose.

ISO 8601

This project of yours is fraught with risks. Taking on responsibility for somebody else’s arbitrary string inputs with their own invented formatting codes is something I would simply refuse to do. The success or failure is out of your control, as you may never know completely what will arrive.

The wiser approach is to shift the burden to the source of this incoming data. They should be passing your strings in standard ISO 8601 format. That standard was devised for exactly this purpose, exchanging date-time values as text. The standard formats are cleverly designed to be unambiguous, and easy to parse by machine as well as easy to read by humans across cultures.

The java.time classes use the standard ISO 8601 formats by default when parsing/generating strings. The ZonedDateTime class wisely extends the standard by appending name of the time zone in square brackets. I would advise the source of your data to do the same if sending a zoned value — generally best to exchange only UTC values.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=94191&siteId=1