I'm pulling a date string from a SQL table. The date string being pulled from Sql is...
2020-07-28 15:35:45.9375232 -05:00
and when I try and parse and format, it's returning a different time than what was passed in.
Method1:
String record = data.getValue(4, 1)
// returns "2020-07-28 15:43:16.5168174 -05:00" from SQL table
def formattedDate = Date.parse("yyyy-MM-dd HH:mm:ss.SSSSSSS XXX", record).format("yyyy-MM-dd'T'HH:mm:ss.SSS")
log.logInfo("queried date = " + formattedDate)
Returns 2020-07-28T17:09:24.174
and Method2:
def queriedDate2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSS XXX").parse(record)
log.logInfo("queriedDate2= " + queriedDate2)
Returns Tue Jul 28 17:09:24 CDT 2020
Can anyone help me understand what's going on?
Probably SimpleDateFormat is ignoring the zone-offset (-5 hours) in the give date-time string and applying your JVM's time-zone while parsing it. I suggest you switch from the outdated and error-prone java.util.Date and SimpleDateFormat to the modern date-time API to get rid of such problems.
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
// Given date-time string
String dateTimeStr = "2020-07-28 15:35:45.9375232 -05:00";
// OffsetDateTime from the given date-time string
OffsetDateTime odt = OffsetDateTime.parse(dateTimeStr,
DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSS XXX"));
System.out.println(odt);
// You can print it in different forms using DateTimeFormatter patterns e.g.
System.out.println(DateTimeFormatter.ofPattern("MMM dd uuuu").format(odt));
System.out.println(DateTimeFormatter.ofPattern("MMM dd uuuu HH:mm:ss.SSSSSSS ZZZZZ").format(odt));
System.out.println(DateTimeFormatter.ofPattern("MM/uuuu/dd hh:mm:ss a").format(odt));
}
}
Output:
2020-07-28T15:35:45.937523200-05:00
Jul 28 2020
Jul 28 2020 15:35:45.9375232 -05:00
07/2020/28 03:35:45 pm
Your
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSS XXX")
expects a timezone, e.g. GMT for the XXX. Your returned value
"2020-07-28 15:43:16.5168174 -05:00"
seems to have a GMT offset: -05:00
I would guess that the .parse() is erroneous and returns some sort of default format.
Because of that, it is suggested to use Arvind's suggested OffsetDateTime.
This question already has an answer here:
Why pattern of datetimeformat is needed to avoid this causing a DateTimeParseException? [duplicate]
Java 8 DateTimeFormatter for month in all CAPS not working [duplicate]
(1 answer)
Closed 2 years ago.
I'm being passed a date in ths format - "JAN01/2020" but I can't seem to find the DateTimeFormatter pattern for it. I tried these (as well as several others) but they're resulting in DateTimeParseException -
DateTimeFormatter.ofPattern("MMMd/YYYY").parse("JAN01/2020")
DateTimeFormatter.ofPattern("MMMdd/YYYY").parse("JAN01/2020")
I also considered this post's solution and the three lines below also result in DateTimeParseException. It does not appear to be a case-sensitivity issue
Java 8 DateTimeFormatter for month in all CAPS not working
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("MMMdd/yyyy");
LocalDateTime dateTime = LocalDateTime.parse("JAN14/2020", formatter);
System.out.println(dateTime.getYear());
I appreciate any suggestions!
You may
handle the case with use of DateTimeFormatterBuilder
.parseCaseInsensitive() for the case of the month, MMM is for Jan and you have JAN
.appendPattern("MMMdd/yyyy") for the pattern
parse into a LocalDate as there is no time composant
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("MMMdd/yyyy");
DateTimeFormatter dateFormat = builder.toFormatter();
LocalDate dateTime = LocalDate.parse("JAN14/2020", dateFormat);
System.out.println(dateTime); // 2020-01-14
CODE DEMO
Case-sensitive
The main problem with your format is, you are using YYYY instead of yyyy. Do it as follows:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
class Main {
public static void main(String args[]) {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("MMMd/yyyy")
.toFormatter();
System.out.println(LocalDate.parse("JAN01/2020", formatter));
}
}
Output:
2020-01-01
Demo of using DateTimeFormatter::parse with it:
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
class Main {
public static void main(String args[]) {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("MMMd/yyyy")
.toFormatter();
System.out.println(formatter.parse("JAN01/2020"));
}
}
Output:
{},ISO resolved to 2020-01-01
As the question you are linking to says, you need to use a DateTimeFormatterBuilder for case insensitive parsing of your month abbreviation in upper case.
Beware of the case of format pattern letters. yyyy and YYYY are different. See for example this question: Can't parse String to LocalDate (Java 8).
Supply a locale for your formatter. If JAN is English, then supply an English-speaking locale. For example:
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("MMMdd/yyyy")
.toFormatter(Locale.ENGLISH)
As azro said, since you haven’t got a time of day, parse into a LocalDate, not a LocalDateTime (or specify a default hour of day on the builder to be able to parse into a LocalDateTime, but I don’t see the advantage).
I am simply trying to convert a date string into a DateTime object in Java 8. Upon running the following lines:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDateTime dt = LocalDateTime.parse("20140218", formatter);
I get the following error:
Exception in thread "main" java.time.format.DateTimeParseException:
Text '20140218' could not be parsed:
Unable to obtain LocalDateTime from TemporalAccessor:
{},ISO resolved to 2014-02-18 of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1918)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1853)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
The syntax is identical to what has been suggested here, yet I am served with an exception. I am using JDK-8u25.
It turns out Java does not accept a bare Date value as DateTime. Using LocalDate instead of LocalDateTime solves the issue:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate dt = LocalDate.parse("20140218", formatter);
If you really need to transform a date to a LocalDateTime object, you could use the LocalDate.atStartOfDay(). This will give you a LocalDateTime object at the specified date, having the hour, minute and second fields set to 0:
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDateTime time = LocalDate.parse("20140218", formatter).atStartOfDay();
For what is worth if anyone should read again this topic(like me) the correct answer would be in DateTimeFormatter definition, e.g.:
private static DateTimeFormatter DATE_FORMAT =
new DateTimeFormatterBuilder().appendPattern("dd/MM/yyyy[ [HH][:mm][:ss][.SSS]]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter();
One should set the optional fields if they will appear. And the rest of code should be exactly the same.
Edit : usefull thing from wittyameta comment :
Remember to add the parseDefaulting AFTER you have called appendPattern. Otherwise it'll give DateTimeParseException
For anyone who landed here with this error, like I did:
Unable to obtain LocalDateTime from TemporalAccessor: {HourOfAmPm=0, MinuteOfHour=0}
It came from a the following line:
LocalDateTime.parse(date, DateTimeFormatter.ofPattern("M/d/yy h:mm"));
It turned out that it was because I was using a 12hr Hour pattern on a 0 hour, instead of a 24hr pattern.
Changing the hour to 24hr pattern by using a capital H fixes it:
LocalDateTime.parse(date, DateTimeFormatter.ofPattern("M/d/yy H:mm"));
This is a really unclear and unhelpful error message. After much trial and error I found that LocalDateTime will give the above error if you do not attempt to parse a time. By using LocalDate instead, it works without erroring.
This is poorly documented and the related exception is very unhelpful.
Expanding on retrography's answer..: I had this same problem even when using LocalDate and not LocalDateTime. The issue was that I had created my DateTimeFormatter using .withResolverStyle(ResolverStyle.STRICT);, so I had to use date pattern uuuuMMdd instead of yyyyMMdd (i.e. "year" instead of "year-of-era")!
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseStrict()
.appendPattern("uuuuMMdd")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
LocalDate dt = LocalDate.parse("20140218", formatter);
(This solution was originally a comment to retrography's answer, but I was encouraged to post it as a stand-alone answer because it apparently works really well for many people.)
If the date String does not include any value for hours, minutes and etc you cannot directly convert this to a LocalDateTime. You can only convert it to a LocalDate, because the string only represent the year,month and date components it would be the correct thing to do.
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate ld = LocalDate.parse("20180306", dtf); // 2018-03-06
Anyway you can convert this to LocalDateTime.
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate ld = LocalDate.parse("20180306", dtf);
LocalDateTime ldt = LocalDateTime.of(ld, LocalTime.of(0,0)); // 2018-03-06T00:00
You do not need to define a DateTimeFormatter
You do not need to define a DateTimeFormatter to parse the given date string. You can use the OOTB (Out-Of-The-Box), DateTimeFormatter.BASIC_ISO_DATE to parse it.
Demo:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.parse("20140218", DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(date);
// In case you need an instance of LocalDateTime
LocalDateTime ldt = date.atTime(LocalTime.MIN);
System.out.println(ldt);
}
}
Output:
2014-02-18
2014-02-18T00:00
ONLINE DEMO
Learn more about the modern Date-Time API* from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time. Check this answer and this answer to learn how to use java.time API with JDBC.
DateTimeFormatter format = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.toFormatter();
Works for me
In cases where you simply want to take a format (whether or not it has time) and want to parse to a LocalDateTime, you can do the following.
LocalDateTime parseDateTime(String dateTime, DateTimeFormatter fmt) {
return fmt.parse(dateTime, t -> {
LocalDate date = t.query(TemporalQueries.localDate());
LocalTime time = t.query(TemporalQueries.localTime());
return LocalDateTime.of(date, time != null ? time : LocalTime.MIDNIGHT);
});
}
I needed this because I was getting the date/time pattern as a parameter for a custom Spark UDF.
This works fine
public class DateDemo {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm");
String date = "16-08-2018 12:10";
LocalDate localDate = LocalDate.parse(date, formatter);
System.out.println("VALUE="+localDate);
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
LocalDateTime parse = LocalDateTime.parse(date, formatter1);
System.out.println("VALUE1="+parse);
}
}
output:
VALUE=2018-08-16
VALUE1=2018-08-16T12:10
Try this one:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy");
LocalDate fromLocalDate = LocalDate.parse(fromdstrong textate, dateTimeFormatter);
You can add any format you want. That works for me!
I arrived at this problem because my input string didn't have a year in it:
input string: Tuesday, June 8 at 10:00 PM
formatter: DateTimeFormatter.ofPattern("EEEE, MMMM d 'at' h:mm a", Locale.US);
I knew the year so I just appended it to get:
input string: Tuesday, June 8 at 6:30 PM 2021
formatter: DateTimeFormatter.ofPattern("EEEE, MMMM d 'at' h:mm a uuuu", Locale.US);
I want to get the format of a given date string.
Example: I have a string like 2011-09-27T07:04:21.97-05:00 and the date format of this string is yyyy-MM-dd'T'HH:mm:ss.SSS.
Here I want to find out this date format when I pass string(2011-09-27T07:04:21.97-05:00) to a method which will return the format(yyyy-MM-dd'T'HH:mm:ss.SSS), then later I will format my given date string according to my requirement(like yy-mm--dd or mm/dd/yyyy).
Can any one tell me how can I get it achieved?
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NewClass {
private static final String[] formats = {
"yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd HH:mm:ss",
"MM/dd/yyyy HH:mm:ss", "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'",
"MM/dd/yyyy'T'HH:mm:ss.SSSZ", "MM/dd/yyyy'T'HH:mm:ss.SSS",
"MM/dd/yyyy'T'HH:mm:ssZ", "MM/dd/yyyy'T'HH:mm:ss",
"yyyy:MM:dd HH:mm:ss", "yyyyMMdd", };
/*
* #param args
*/
public static void main(String[] args) {
String yyyyMMdd = "20110917";
parse(yyyyMMdd);
}
public static void parse(String d) {
if (d != null) {
for (String parse : formats) {
SimpleDateFormat sdf = new SimpleDateFormat(parse);
try {
sdf.parse(d);
System.out.println("Printing the value of " + parse);
} catch (ParseException e) {
}
}
}
}
}
you can do like this way, I don't know good way or not but try this
first create the SimpleDateFormat object
SimpleDateFormt sdf = new SimpleDateFormat("yyyy-MM-dd 'T' HH:mm:ss.SSS");
now when check the date if this will parse in this format then change as per your format
try{
Date date = sdf.parse(yourdate);
sdf.applyPattern("yy-mm--dd or mm/dd/yyyy");
String dateformat = sdf.format(date);
}catch(Exception ex) { // here forgot the exact exception class Parse exception was used
// do something here
}
updated post:
Returning a date format from an unknown format of date string in java
How to convert String to Date without knowing the format?
Parse any date in Java
I think you should try to parse input string with some predefine patterns. The one that works is the one you need. Remember that some patterns are quite tricky.
01.12.12 is 01 December 2012 in Europe but 12 January 2012 in USA. It could be 12 December 2001 too.
If I understand you correctly, you want to parse arbitrary strings (that is, string of a format you don't know) as dates by using DateFormat.parse()? Then you have to deal with issues like how to handle 01-02-03 (2 Jan 2003? 1 Feb 2003? etc.)
You should know at least something about the expected format, like a choice of several predefined formats for your input.
Madhu's code is can workout, but some performance problem will arise because every failure case will raise the exception.
i think we need to find the reguler expression solution to find the pattern form the given date String.
you can find all most all reg expressions to date and time format in the following link
http://regexlib.com/DisplayPatterns.aspx?cattabindex=4&categoryId=5&AspxAutoDetectCookieSupport=1
Here is a generic solution the determine the pattern without knowing the date pattern in advance and without calling the parse method of SimpleDateFormat for all formats. You can get any date pattern from date string value by using the regex.
package com.utility.utils.modelmapper.datetime;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DateParser {
private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {
{
put("^\\d{8}$", "yyyyMMdd");
put("^\\d{12}$", "yyyyMMddHHmm");
put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
put("^\\d{14}$", "yyyyMMddHHmmss");
put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
put("^\\d{4}-\\d{1,2}-\\d{1,2}T\\d{1,2}:\\d{2}:\\d{2}\\.\\d{2}[-+]\\d{2}:\\d{2}$", "yyyy-MM-dd'T'HH:mm:ss.SSS");
}
};
/**
* To Determine the pattern by the string date value
*
* #param dateString
* #return The matching SimpleDateFormat pattern, or null if format is unknown.
*/
public static String determineDateFormat(String dateString) {
for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
if (dateString.matches(regexp) || dateString.toLowerCase().matches(regexp)) {
return DATE_FORMAT_REGEXPS.get(regexp);
}
}
return null;
}
public static void main(String[] args) {
parse("2011-09-27T07:04:21.97-05:00"); //here is your value
parse("20110917");
parse("01/02/2018");
parse("02-01-2018 06:07:59");
parse("02 January 2018");
}
public static void parse(String value) {
if (value != null) {
String format = determineDateFormat(value);
if (format != null) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date date = sdf.parse(value);
System.out.println(String.format("Format : %s | Value : %s | Parsed Date : %s", value, date, format));
} catch (ParseException e) {
// Failed the execution
}
}
}
}
}
Console output of the class:
Format : 2011-09-27T07:04:21.97-05:00 | Value : Tue Sep 27 07:04:21 LINT 2011 | Parsed Date : yyyy-MM-dd'T'HH:mm:ss.SSS
Format : 20110917 | Value : Sat Sep 17 00:00:00 LINT 2011 | Parsed Date : yyyyMMdd
Format : 01/02/2018 | Value : Tue Jan 02 00:00:00 LINT 2018 | Parsed Date : MM/dd/yyyy
Format : 02-01-2018 06:07:59 | Value : Tue Jan 02 06:07:59 LINT 2018 | Parsed Date : dd-MM-yyyy HH:mm:ss
Format : 02 January 2018 | Value : Tue Jan 02 00:00:00 LINT 2018 | Parsed Date : dd MMMM yyyy
Maybe I missed some of the date-time patterns here but for that the correct regex pattern should be added in the map.
You will need to take the inital date string and covert it to a date object and pass that converted date object and format it to your required string.
You could try dateparser.
It can recognize any String automatically, and parse it into Date, Calendar, LocalDateTime, OffsetDateTime correctly and quickly(1us~1.5us).
It doesn't based on any natural language analyzer or SimpleDateFormat or regex.Pattern.
With it, you don't have to prepare any appropriate patterns like yyyy-MM-dd'T'HH:mm:ss'Z' or MM/dd/yyyy HH:mm:ss etc:
Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");
And it has better performance than loop-try multiple SimpleDateFormat.
Please enjoy it.
java.time and its predefined formatters
We cannot do this for just any date-time format. There are thousands of them, we cannot know them all (someone will invent a new one tomorrow), and some look alike so much we can’t tell which we’ve got.
I suggest that for the majority of purposes you need to parse the string, but you don’t need to know a format pattern for doing so. In very many cases, including the example from your question, 2011-09-27T07:04:21.97-05:00, we don’t need to specify a pattern (your string matches DateTimeFormatter.ISO_OFFSET_DATE_TIME).
Since Java 8 came out in 2014 (and even if still using Java 6 or 7), use java.time, the modern Java date and time API, for your date and time work.
I am defining an array of formatters for the formats we want to cater for. Please substitute your own set.
private static final DateTimeFormatter[] formatters = {
DateTimeFormatter.ISO_OFFSET_DATE_TIME,
DateTimeFormatter.RFC_1123_DATE_TIME,
new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter(),
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withLocale(Locale.US),
DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm")
.withZone(ZoneId.of("America/Los_Angeles"))
};
The following method tries the formatters in turn until one works:
private static OffsetDateTime parse(String dateTimeString) {
for (DateTimeFormatter formatter : formatters) {
try {
return ZonedDateTime.parse(dateTimeString, formatter)
.toOffsetDateTime();
} catch (DateTimeParseException dtpe) {
// Ignore, try next formatter
}
}
throw new IllegalArgumentException("String " + dateTimeString + " could not be parsed");
}
Let’s try it out with some different strings:
String[] dateTimeStrings = {
"2011-09-27T07:04:21.97-05:00",
"20110917",
"2012-07-04",
"12/27/2014 23:45",
"Mon, 12 Nov 2018 01:32:10 GMT",
"July 29, 2015 at 10:19:36 AM EDT",
};
for (String dts : dateTimeStrings) {
try {
System.out.format("%32s -> %s%n", dts, parse(dts));
} catch (IllegalArgumentException iae) {
System.out.format("%32s -> %s%n", dts, iae);
}
}
Output is:
2011-09-27T07:04:21.97-05:00 -> 2011-09-27T07:04:21.970-05:00
20110917 -> java.lang.IllegalArgumentException: String 20110917 could not be parsed
2012-07-04 -> 2012-07-04T00:00Z
12/27/2014 23:45 -> 2014-12-27T23:45-08:00
Mon, 12 Nov 2018 01:32:10 GMT -> 2018-11-12T01:32:10Z
July 29, 2015 at 10:19:36 AM EDT -> 2015-07-29T10:19:36-04:00
Other options
Techniques for parsing dates and times in multiple formats include:
Take a taste of the string to decide its format and use an appropriate formatter based on that. It’s best suited if you have just a few formats, though the answer by Vinit Solanki shows an elaborate version for quite many formats.
Use optional parts in a format pattern string. For example [uuuu][uu] will parse either four digit or two digit year (2021 or just 21).
Try several formatters in turn as shown in my code above. If you do need to know the pattern, use an array of patterns instead of an array of formatters.
Requiring the supplier of the string to supply a format patterns string too. This is not always as simple as it may sound, though.
Beware of ambiguity. The classical example is the two formats MM-dd-yyyy and dd-MM-yyyy. If we get a string of 03-09-2020, there’s no way to tell whether it means March 9 or 3rd September. Even worse, 02-05-07 might be yy-MM-dd, dd-MM-yy, MM-dd-yy and even more possibilities. As a consequence, make sure you don’t include two (or more) formatters that may parse the same string into different results.
Links
Oracle tutorial: Date Time explaining how to use java.time.
A fine answer by Arvind Kumar Avinash showing the use of optional parts in the format pattern string for parsing different formats.
HH:mm:ss.SSS => ([0-2]{1,}[0-9]{1,})(:)([0-5]{1,}[0-9]{1,})(:)([0-5]{1,}[0-9]{1,})(.)([0-9]{1,3})
yyyy-mm-dd => ([0-9]{4})(-)([0-1]{1,}[0-9]{1,})(-)([0-3]{1,}[0-9]{1,})