As you have already guessed, the root cause of the error is a mismatch between the pattern the date-time string in and the one that you have used in the DateTimeFormatter. If you already know all the date-time patterns in which you are getting the date-time strings, you can create DateTimeFormatter with multiple optional patterns (by enclosing the patterns in square bracket). If you receive a date-time in an unknown pattern (i.e. the pattern which you have not put in DateTimeFormatter), you can throw the exception or handle it as per your requirement.
I need to parse any incoming date time string with a user-specified
locale and timezone to the sole pattern to properly store it in the
database later:
There are two parts of this requirement: A. Parse and convert the date-time in the user-specified locale and timezone into the equivalent date-time at UTC (not only recommended but also required by some databases e.g. PostgreSQL) B. Save it into the database.
The steps to meet the first part of the requirements are:
- Since the received date-time is in the user-specified timezone, ignore the timezone contained in the date-time string and parse it to
LocalDateTime.
- Convert the
LocalDateTime to ZonedDateTime at the user-specified timezone.
- Convert this
ZonedDateTime to ZonedDateTime at UTC.
- Finally, convert the
ZonedDateTime to OffsetDateTime.
Once you have OffsetDateTime, you can store it into the database as follows:
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, odt);// odt is the instance of OffsetDateTime
st.executeUpdate();
st.close();
You can use the following test harness to test the first part of the requirement:
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// Test
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Enter the date-time string (press Enter without entering anything to quit): ");
String strDateTime = scanner.nextLine();
if (strDateTime.isBlank()) {
break;
}
boolean valid;
// Create Locale
Locale locale = null;
do {
valid = true;
System.out.print("Enter language code e.g. en, fr, in: ");
String languageTag = scanner.nextLine();
if (!isValidForLocale(languageTag)) {
System.out.println("Invalid code. Please try again.");
valid = false;
} else {
locale = Locale.forLanguageTag(languageTag);
}
} while (!valid);
// Create ZoneId
ZoneId zoneId = null;
do {
valid = true;
System.out.print("Enter timezone in the format Continent/City e.g. Asia/Calcutta: ");
String timezone = scanner.nextLine();
try {
zoneId = ZoneId.of(timezone);
} catch (Exception e) {
System.out.println("Invalid timezone. Please try again.");
valid = false;
}
} while (!valid);
try {
System.out.println(getDateTimeInUTC(strDateTime, locale, zoneId));
} catch (DateTimeParseException e) {
System.out.println("The date-time string has the following problem:\n" + e.getMessage());
System.out.println("Please try again.");
}
}
}
static OffsetDateTime getDateTimeInUTC(String strDateTime, Locale locale, ZoneId zoneId)
throws DateTimeParseException {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("[uuuu-M-d H:m:s][EEE MMM d H:m:s zzz uuuu]", locale);
// Ignore the timezone contained in strDateTime and parse strDateTime to
// LocalDateTime. Then, convert the LocalDateTime to ZonedDateTime at zoneId.
// Then, convert this ZonedDateTime to ZonedDateTime at UTC. Finally, convert
// the ZonedDateTime to OffsetDateTime and return the same.
ZonedDateTime zdt = LocalDateTime.parse(strDateTime, dtf).atZone(zoneId).withZoneSameInstant(ZoneOffset.UTC);
return zdt.toOffsetDateTime();
}
static boolean isValidForLocale(String languageTag) {
return Arrays.stream(Locale.getISOLanguages()).anyMatch(l -> Objects.equals(l, languageTag));
}
}
A sample run:
Enter the date-time string (press Enter without entering anything to quit): Mon Dec 21 21:18:37 GMT 2020
Enter language code e.g. en, fr, in: en
Enter timezone in the format Continent/City e.g. Asia/Calcutta: Asia/Calcutta
2020-12-21T15:48:37Z
Enter the date-time string (press Enter without entering anything to quit): 2020-1-23 5:15:8
Enter language code e.g. en, fr, in: en
Enter timezone in the format Continent/City e.g. Asia/Calcutta: Asia/Calcutta
2020-01-22T23:45:08Z
Enter the date-time string (press Enter without entering anything to quit):