I am trying to parse some dates that are coming out of a document. It would appear users have entered these dates in a similar but not exact format.
here are the for
I was having multiple date formats into json, and was extracting csv with universal format. I looked multiple places, tried different ways, but at the end I'm able to convert with the following simple code.
private String getDate(String anyDateFormattedString) {
@SuppressWarnings("deprecation")
Date date = new Date(anyDateFormattedString);
SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
String convertedDate = dateFormat.format(date);
return convertedDate;
}
Matt's approach above is fine, but please be aware that you will run into problems if you use it to differentiate between dates of the format y/M/d
and d/M/y
. For instance, a formatter initialised with y/M/d
will accept a date like 01/01/2009
and give you back a date which is clearly not what you wanted. I fixed the issue as follows, but I have limited time and I'm not happy with the solution for 2 main reasons:
getDateFormat()
method becoming a bit of a nightmare if you needed it to handle lots of other date formats.If I had to make something that could handle lots and lots of different date formats and needed to be highly performant, then I think I would use the approach of creating an enum which linked each different date regex to its format. Then use MyEnum.values()
to loop through the enum and test with if(myEnum.getPattern().matches(date))
rather than catching a dateformatexception.
Anway, that being said, the following can handle dates of the formats 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y'
and all other variations of those which include time formats as well:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final String[] timeFormats = {"HH:mm:ss","HH:mm"};
private static final String[] dateSeparators = {"/","-"," "};
private static final String DMY_FORMAT = "dd{sep}MM{sep}yyyy";
private static final String YMD_FORMAT = "yyyy{sep}MM{sep}dd";
private static final String ymd_template = "\\d{4}{sep}\\d{2}{sep}\\d{2}.*";
private static final String dmy_template = "\\d{2}{sep}\\d{2}{sep}\\d{4}.*";
public static Date stringToDate(String input){
Date date = null;
String dateFormat = getDateFormat(input);
if(dateFormat == null){
throw new IllegalArgumentException("Date is not in an accepted format " + input);
}
for(String sep : dateSeparators){
String actualDateFormat = patternForSeparator(dateFormat, sep);
//try first with the time
for(String time : timeFormats){
date = tryParse(input,actualDateFormat + " " + time);
if(date != null){
return date;
}
}
//didn't work, try without the time formats
date = tryParse(input,actualDateFormat);
if(date != null){
return date;
}
}
return date;
}
private static String getDateFormat(String date){
for(String sep : dateSeparators){
String ymdPattern = patternForSeparator(ymd_template, sep);
String dmyPattern = patternForSeparator(dmy_template, sep);
if(date.matches(ymdPattern)){
return YMD_FORMAT;
}
if(date.matches(dmyPattern)){
return DMY_FORMAT;
}
}
return null;
}
private static String patternForSeparator(String template, String sep){
return template.replace("{sep}", sep);
}
private static Date tryParse(String input, String pattern){
try{
return new SimpleDateFormat(pattern).parse(input);
}
catch (ParseException e) {}
return null;
}
}
For the modern answer I am ignoring the requirement to use SimpleDateFormat
. While using this class for parsing was a good idea in 2010 when this question was asked, it is now long outdated. The replacement, DateTimeFormatter
, came out in 2014. The idea in the following is pretty much the same as in the accepted answer.
private static DateTimeFormatter[] parseFormatters = Stream.of("M/yy", "M/y", "M/d/y", "M-d-y")
.map(DateTimeFormatter::ofPattern)
.toArray(DateTimeFormatter[]::new);
public static YearMonth parseYearMonth(String input) {
for (DateTimeFormatter formatter : parseFormatters) {
try {
return YearMonth.parse(input, formatter);
} catch (DateTimeParseException dtpe) {
// ignore, try next format
}
}
throw new IllegalArgumentException("Could not parse " + input);
}
This parses each of the input strings from the question into a year-month of 2009-09
. It’s important to try the two-digit year first since "M/y"
could also parse 9/09
, but into 0009-09
instead.
A limitation of the above code is it ignores the day-of-month from the strings that have one, like 9/1/2009
. Maybe it’s OK as long as most formats have only month and year. To pick it up, we’d have to try LocalDate.parse()
rather then YearMonth.parse()
for the formats that include d
in the pattern string. Surely it can be done.
Using DateTimeFormatter it can be achieved as below:
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.TimeZone;
public class DateTimeFormatTest {
public static void main(String[] args) {
String pattern = "[yyyy-MM-dd[['T'][ ]HH:mm:ss[.SSSSSSSz][.SSS[XXX][X]]]]";
String timeSample = "2018-05-04T13:49:01.7047141Z";
SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
TemporalAccessor accessor = formatter.parse(timeSample);
ZonedDateTime zTime = LocalDateTime.from(accessor).atZone(ZoneOffset.UTC);
Date date=new Date(zTime.toEpochSecond()*1000);
simpleDateFormatter.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
System.out.println(simpleDateFormatter.format(date));
}
}
Pay attention at String pattern
, this is the combination of multiple patterns. In open [
and close ]
square brackets you can mention any kind of patterns.
If working in Java 1.8 you can leverage the DateTimeFormatterBuilder
public static boolean isTimeStampValid(String inputString)
{
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd'T'HH:mm:ss.SSSZ]" + "[yyyy-MM-dd]"));
DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter();
try {
dateTimeFormatter.parse(inputString);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
See post: Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?
Here is the complete example (with main method) which can be added as a utility class in your project. All the format mentioned in SimpleDateFormate API is supported in the below method.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
public class DateUtility {
public static Date parseDate(String inputDate) {
Date outputDate = null;
String[] possibleDateFormats =
{
"yyyy.MM.dd G 'at' HH:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"hh 'o''clock' a, zzzz",
"K:mm a, z",
"yyyyy.MMMMM.dd GGG hh:mm aaa",
"EEE, d MMM yyyy HH:mm:ss Z",
"yyMMddHHmmssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
"YYYY-'W'ww-u",
"EEE, dd MMM yyyy HH:mm:ss z",
"EEE, dd MMM yyyy HH:mm zzzz",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz",
"yyyy-MM-dd'T'HH:mm:sszzzz",
"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'HHmmss.SSSz",
"yyyy-MM-dd",
"yyyyMMdd",
"dd/MM/yy",
"dd/MM/yyyy"
};
try {
outputDate = DateUtils.parseDate(inputDate, possibleDateFormats);
System.out.println("inputDate ==> " + inputDate + ", outputDate ==> " + outputDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return outputDate;
}
public static String formatDate(Date date, String requiredDateFormat) {
SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat);
String outputDateFormatted = df.format(date);
return outputDateFormatted;
}
public static void main(String[] args) {
DateUtility.parseDate("20181118");
DateUtility.parseDate("2018-11-18");
DateUtility.parseDate("18/11/18");
DateUtility.parseDate("18/11/2018");
DateUtility.parseDate("2018.11.18 AD at 12:08:56 PDT");
System.out.println("");
DateUtility.parseDate("Wed, Nov 18, '18");
DateUtility.parseDate("12:08 PM");
DateUtility.parseDate("12 o'clock PM, Pacific Daylight Time");
DateUtility.parseDate("0:08 PM, PDT");
DateUtility.parseDate("02018.Nov.18 AD 12:08 PM");
System.out.println("");
DateUtility.parseDate("Wed, 18 Nov 2018 12:08:56 -0700");
DateUtility.parseDate("181118120856-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-07:00");
DateUtility.parseDate("2018-W27-3");
}
}