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):