Handling Multiple Date Formats with One Method via Joda Time

Handling Multiple Date Formats with One Method via Joda Time

Handling Multiple Date Formats with One Method via Joda Time


Nearly any project that amounts to anything must handle the processing of Dates, Times, and Timestamps. When it comes to Java projects, I see time and time again that developers just insert date formatters anywhere and everywhere. This violates the concept of modularity and making a segment of code do one thing and it do it well. Furthermore, this leads to lots of copy and paste duplicate code, which in turn leads to less testable code. Ultimately, this degrades internal code quality for a project. Moreover, and perhaps most importantly, I feel many developers fail to understand how flawed and poor the standard Java date and time classes are, which predate Java 8 (see here for starters). Lastly, I also see many different date formats used in code bases. It is my opinion, that internally in Java code, only one date format should be used and it should be the ISO8601 standard format: yyyy-dddTHH:MM:SS.SSS. Even better, would be utilize time zone information in UTC/Zulu. This format easily and readably demarcates the date from the time, it is sortable in chronological order, and eliminates the guessing game that sometimes accompanies ready a timestamp.

As a result, I will be showing you a way to write a single utility method for parsing dates represented as Strings, in multiple formats, into java.util.Date objects. This will be done with one of the most respected date and time libraries out there: Joda Time. Joda is the defacto standard, in my opinion, for date and time manipulations prior to Java 8. Also, I will show you a JUnit Theory test to exercise the utility method I created. So, here it is:

import java.util.Date;

import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;

public class TimeUtil {

	private DateTimeFormatter formatter = null;

	/**
	 * Utility that can convert several different formatted strings into {@link Date} like
	 * <ul>
	 * <li>yyyyMMdd
	 * <li>yyyy-MM-dd
	 * <li>MM/dd/yyyy
	 * </ul>
	 * 
	 * @param strDate
	 * @return
	 */
	public Date stringAsDate(String strDate) {
		if (strDate == null || strDate.isEmpty()) {
			return null;
		} else {
			try {
				if (formatter == null) {
					formatter = new DateTimeFormatterBuilder()
							.appendOptional(DateTimeFormat.forPattern("yyyyMMdd").getParser())
							.appendOptional(DateTimeFormat.forPattern("yyyy-MM-dd").getParser())
							.appendOptional(DateTimeFormat.forPattern("MM/dd/yyyy").getParser()).toFormatter();
				}

				LocalDateTime dateTime = LocalDateTime.parse(strDate, formatter);
				return dateTime.toDate();
			} catch (Exception e) {
				return null;
			}
		}
	}
}

Now, here we can we have a utility method, which supports three different date formats. It also efficiently only builds the formatter once. Lastly, it is easily extendable to more formats if needed, but again, it should be best practice to minimize or normalize the formats used throughout a project.

Next, here is a simple JUnit Theory test, which takes multiple date Strings and all of which pass the Theory test.

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

import java.util.Date;

import org.joda.time.LocalDate;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class TimeUtilTheoryTest {

	private TimeUtil timeUtil = new TimeUtil();

	@DataPoints
	public static String[] strDates = new String[]{
			"20180429",
			"2018-04-29",
			"04/29/2018",
			"01/20/1999",
			"1899-12-25",
			"2020-02-29", // leap year future
			"00010101",
			"19160229" // leap year past
		};
	
	@Theory
	public void testStringAsDate(String strDate) {
		
		Date date = timeUtil.stringAsDate(strDate);
		
		assertThat(date, is(not(nullValue())));
		
		String year = null;
		String month = null;
		String day = null;
		if(strDate.contains("-")) {
			String[] parts = strDate.split("-");
			year = parts[0];
			month = parts[1];
			day = parts[2];
		} else if (strDate.contains("/")) {
			String[] parts = strDate.split("/");
			year = parts[2];
			month = parts[0];
			day = parts[1];			
		} else {
			year = strDate.substring(0, 4);
			month = strDate.substring(4, 6);
			day = strDate.substring(6);
		}
		
		LocalDate ld = new LocalDate(Integer.valueOf(year), Integer.valueOf(month), Integer.valueOf(day));
		assertThat(date, is(ld.toDate()));
	}

}

Here we can see we have an array of DataPoints. Each item in the array is passed to the Theory to be tested, one after the other. If all of the given DataPoints are successfully executed against the Theory, then the Unit test passes. This prevent you from writing the same or similar unit test over and over. Even so, I do find Theories a little limited when you want to mix positive and negative DataPoints. You either need to filter out bad data with Assumptions or Assertions. Or, alternatively, you could create a more complex object for your array of DataPoints with the expected answers or flags to guide your tests.

Hopefully this has helped you learn how to effectively and concisely parse formatted strings into java.util.Date objects. Remember, this was done with third party Java API known as Joda Time, which is considered a superior date and time library for versions of Java predating version 8. In fact, you will notice in Java 8 that much of Joda seems to have been borrowed and integrated into the core of Java 8 with JSR-310. Also, I hope this post gives you a taste of the power of JUnit Theories as opposed to the tradition JUnit test.

Leave a Reply

Your email address will not be published. Required fields are marked *