java.time.LocalDate allow invalid value in date [duplicate] - java

I wanted to validate date in client side so I wrote the following code. But instead of getting an exception I am getting a proper date object for 31st of February date string, which is clearly an invalid date.
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/yyyy";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (Exception e) {
// Throw invalid date message
}
}
}
Output : 2015-02-28T11:30:59
Does anyone know why LocalDateTime is parsing this date instead of throwing an exception.

You just need a strict ResolverStyle.
Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.
Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):
import java.time.*;
import java.time.format.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/uuuu";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (DateTimeParseException e) {
// Throw invalid date message
System.out.println("Exception was thrown");
}
}
}

The Java 8 DateTimeFormatter uses yyyy to mean YEAR_OF_ERA, and uuuu to mean YEAR. You need to modify your pattern string as follows:
String dateFormat = "HH:mm:ss MM/dd/uuuu";
The DateTimeFormatter defaults to using the SMART resolver style, but you want it to use the STRICT resolver style. Modify your dateTimeFormatter initialization code as follows:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);

It is not rounding down. February has never had 31 days, and it is impossible to use a validating date / time object to represent a day that doesn't exist.
As a result, it takes the invalid input and gives you the best approximation to the correct date (the last date of February that year).
SimpleDateFormat inherits from DateFormat which has a setLenient(boolean value) method on it. I would expect that if you called setLenient(true) prior to parsing, it would probably complain more, as detailed in the javadocs.

try {
SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
df.setLenient(false);
System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
System.out.println(e);
}
I found one solution to recognize date as a valid date with DateFormat.setLenient(boolean). If you try to parse any invalid date it will throws parse exception.
Edit:
Java 8, but this will raise exception if a month is not between 1 and 12, if a day is more than 32. Exactly not working. But for month its working.
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}
Output:
java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32

LocalDateTime.parse will only throw an error if the String passed in contains invalid characters, a number of days exceeding 31 or a month exceeding 12.
For example, if you modified your code as such:
String dateString = "11:30:59 0zz2/31/2015";
an exception would be thrown caused by the invalid 'zz' characters within your given date. As to why it's 'rounding-down' the date so to speak, that I don't know.
Source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-

Related

Date in String to Date in Date conversion

I'm having date in String format as "2019-10-30 12:17:47". I want to convert this to an instance of Date along with the time so that I can compare two date obejcts.
This is what I've tried:
String dateString = "2019-10-30 12:17:47" //Date in String format
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); //sdf
Date d1 = format.parse(dateString);
But here I'm getting exception as "Unparseble date exception".
Kindly help...
What went wrong in your code?
In your format pattern string, yyyy-MM-dd HH-mm-ss, you have got two spaces between the date and the time. Since your date string, 2019-10-30 12:17:47, has got only one space there, your formatter objects by throwing the exception. This was also what Tim Biegeleisen said in a comment. The comment by deHaar is true too: The hyphens between hour, minute and second don’t match the colons in your date string either.
What to do instead?
See the good answer by deHaar
You should really switch to java.time (as already suggested in one of the comments below your question). It isn't more difficult than the outdated temporal classes from java.util but less error-prone and more powerful concerning offsets, time zones, daylight saving time and the multitude of different calendars the world has.
See this little example:
public static void main(String[] args) {
String dateString = "2019-10-30 12:17:47";
// define your pattern, should match the one of the String ;-)
String datePattern = "yyyy-MM-dd HH:mm:ss";
// parse the datetime using the pattern
LocalDateTime ldt = LocalDateTime.parse(dateString,
DateTimeFormatter.ofPattern(datePattern));
// print it using a different (here a built-in) formatting pattern
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// or you just use the one defined by you
System.out.println(ldt.format(DateTimeFormatter.ofPattern(datePattern)));
// or you define another one for the output
System.out.println(ldt.format(DateTimeFormatter.ofPattern("MMM dd yyyy HH-mm-ss")));
}
The output on my system looks like this:
2019-10-30T12:17:47
2019-10-30 12:17:47
Okt 30 2019 12-17-47
The date in string you want to format does not match the formatter. See more detail here,
https://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html
#Test
public void test2() {
String dateString = "2019-10-30 12:17:47"; //Date in String format
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //sdf
try {
Date d1 = format.parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
}
}
There are two ways to do it
first is your way
String dateString = "2019-10-30 12:17:47"; // Date in String format
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // sdf
Date d1 = format.parse(dateString
second is my way (Local date)
LocalDate resultDate = dateFormat("2019-10-30 12:17:47");
System.out.println(resultDate);
public static LocalDate dateFormat(String textTypeDateTime) {
final DateTimeFormatter dateTimetextFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDate.parse(textTypeDateTime, dateTimetextFormatter);
}

Java Date Parsing from string

I'm trying to parse a String into Data, I create the DataParser, in according to date format, the code I wrote is this:
String date_s = "04-May-2017 17:28:27";
DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
Date date;
try {
date = formatter.parse(date_s);
System.out.println(date);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
When I execute this, I got always an exception
java.text.ParseException: Unparseable date: "04-May-2017 17:28:27"
I don't understand why the data is not parsed, someone can help me?
This thread of answers would not be complete without the modern solution. These days you should no longer use Date and SimpleDateFormat, but switch over to the newer date and time classes:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("dd-MMM-yyyy HH:mm:ss", Locale.ENGLISH);
LocalDateTime dateTime;
try {
dateTime = LocalDateTime.parse(date_s, formatter);
System.out.println(dateTime);
} catch (DateTimeParseException e) {
System.err.println(e);
}
This prints
2017-05-04T17:28:27
(LocalDateTime.toString() returns ISO 8601 format) If leaving out Locale.ENGLISH, on my computer I get
Text '04-May-2017 17:28:27' could not be parsed at index 3
Index 3 is where it say May, so the message is somewhat helpful.
LocalDateTime and DateTimeFormatter were introduced in Java 8, but have also been backported to Java 6 and 7.
the string you want to parse is local dependent (the word May is English), so the jvm is not able to infer that may is the month of may in English
define the formatter using the constructor qith the locale.
DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss",Locale.ENGLISH);
You need another constructor with a Locale that supports MMM (May)
DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss",Locale.US)
or using standard format dd-MM-yyyy with month digits.
(Sorry, in the meantime the answer was already posted)

Java 8 LocalDateTime is parsing invalid date

I wanted to validate date in client side so I wrote the following code. But instead of getting an exception I am getting a proper date object for 31st of February date string, which is clearly an invalid date.
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/yyyy";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (Exception e) {
// Throw invalid date message
}
}
}
Output : 2015-02-28T11:30:59
Does anyone know why LocalDateTime is parsing this date instead of throwing an exception.
You just need a strict ResolverStyle.
Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.
Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):
import java.time.*;
import java.time.format.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/uuuu";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (DateTimeParseException e) {
// Throw invalid date message
System.out.println("Exception was thrown");
}
}
}
The Java 8 DateTimeFormatter uses yyyy to mean YEAR_OF_ERA, and uuuu to mean YEAR. You need to modify your pattern string as follows:
String dateFormat = "HH:mm:ss MM/dd/uuuu";
The DateTimeFormatter defaults to using the SMART resolver style, but you want it to use the STRICT resolver style. Modify your dateTimeFormatter initialization code as follows:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
It is not rounding down. February has never had 31 days, and it is impossible to use a validating date / time object to represent a day that doesn't exist.
As a result, it takes the invalid input and gives you the best approximation to the correct date (the last date of February that year).
SimpleDateFormat inherits from DateFormat which has a setLenient(boolean value) method on it. I would expect that if you called setLenient(true) prior to parsing, it would probably complain more, as detailed in the javadocs.
try {
SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
df.setLenient(false);
System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
System.out.println(e);
}
I found one solution to recognize date as a valid date with DateFormat.setLenient(boolean). If you try to parse any invalid date it will throws parse exception.
Edit:
Java 8, but this will raise exception if a month is not between 1 and 12, if a day is more than 32. Exactly not working. But for month its working.
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}
Output:
java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
LocalDateTime.parse will only throw an error if the String passed in contains invalid characters, a number of days exceeding 31 or a month exceeding 12.
For example, if you modified your code as such:
String dateString = "11:30:59 0zz2/31/2015";
an exception would be thrown caused by the invalid 'zz' characters within your given date. As to why it's 'rounding-down' the date so to speak, that I don't know.
Source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-

SimpleDateFormat.parse() - generates wrong date for different date-formats

Below is my code to parse the date using SimpleDateFormat with pattern:
String pattern = "yyyy-MM-dd";
SimpleDateFormat format = new SimpleDateFormat(pattern);
try {
Date date = format.parse("05-21-2030");
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
You can see the date which I passed to parse is different from date format which is specified in SimpleDateFormat. In this case I was expecting kind of excpetion as format is different but it parsed successfully with some different date values. I got the output - Tue Mar 22 00:00:00 IST 12
When I pass the same format like 2030-05-21 it works fine.
Can you guys please let me know how can I prevent such things in my code?
Basically you want SimpleDateFormat to be strict, so set lenient to false.
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setLenient(false);
If you can afford using Java 8 time API, its formatter works as expected:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate date = LocalDate.parse("2030-05-21", formatter);
System.out.println(date);
LocalDate date2 = LocalDate.parse("05-21-2030", formatter);
System.out.println(date2);
} catch (DateTimeParseException e) {
e.printStackTrace();
}
Output:
2030-05-21
java.time.format.DateTimeParseException: Text '05-21-2030' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1947)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1849)
at java.time.LocalDate.parse(LocalDate.java:400)
at java8.Snippet.main(Snippet.java:25)

How to get the given date string format(pattern) in java?

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,})

Categories

Resources