I had similar requirement recently. I was overhauling a legacy application that was written back when Java 5 was the newest release. I needed to keep parsing specific date and date/time formats, but I wanted the same 'strict' behaviour that the old SimpleDateFormat
class used to provide. I also had a need to convert to/from java.util.Date
for compatibility with older existing code.
Here's my utility class and unit tests:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
/**
* Static utility class for converting from java.util.Date to modern Java time classes,
* and some predefined DateTimeFormatter instances to handle formats used in data files
* that this application processes.
*/
public class DateTimeUtils {
private DateTimeUtils() {}
// Jim Tough - 2020-09-14
// These two date/time formats are part of the data file specification.
// We are stuck with these formats now.
private static final String DTF_YYYYMMDDHHMMSS_FORMAT_STRING = "uuuuMMdd HH:mm:ss";
private static final String DTF_YYYYMMDDHHMM_FORMAT_STRING = "uuuuMMddHHmm";
private static final String DTF_YYYYMMDD_FORMAT_STRING = "uuuuMMdd";
public static final DateTimeFormatter DTF_YYYYMMDDHHMMSS =
DateTimeFormatter.ofPattern(DTF_YYYYMMDDHHMMSS_FORMAT_STRING)
.withResolverStyle(ResolverStyle.STRICT);
public static final DateTimeFormatter DTF_YYYYMMDDHHMM =
DateTimeFormatter.ofPattern(DTF_YYYYMMDDHHMM_FORMAT_STRING)
.withResolverStyle(ResolverStyle.STRICT);
public static final DateTimeFormatter DTF_YYYYMMDD =
DateTimeFormatter.ofPattern(DTF_YYYYMMDD_FORMAT_STRING)
.withResolverFields(ChronoField.YEAR, ChronoField.MONTH_OF_YEAR, ChronoField.DAY_OF_MONTH)
.withResolverStyle(ResolverStyle.STRICT);
//-------------------------------------------------------------------------
public static LocalDate convertToLocalDateViaInstant(Date dateToConvert) {
if (dateToConvert == null) {
return null;
}
return dateToConvert.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
}
public static LocalDateTime convertToLocalDateTimeViaInstant(Date dateToConvert) {
if (dateToConvert == null) {
return null;
}
return dateToConvert
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
}
public static Date convertToDateViaInstant(LocalDate dateToConvert) {
if (dateToConvert == null) {
return null;
}
return java.util.Date.from(dateToConvert.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant());
}
public static Date convertToDateViaInstant(LocalDateTime dateToConvert) {
if (dateToConvert == null) {
return null;
}
return java.util.Date.from(
dateToConvert.atZone(ZoneId.systemDefault()).toInstant()
);
}
//-------------------------------------------------------------------------
/**
* Parse the supplied string using the supplied {@code DateTimeFormatter} and return the result
* as a {@code LocalDate}
* @param dtf Non-null
* @param s String, or null value
* @return {@code LocalDate}, or null if the supplied string parameter is null
* @throws java.time.DateTimeException when supplied string cannot be parsed by the
* {@code DateTimeFormatter} and converted to a {@code LocalDate}
*/
public static LocalDate parseAsLocalDate(DateTimeFormatter dtf, String s) {
if (s == null) {
return null;
}
TemporalAccessor ta = dtf.parse(s);
return LocalDate.from(ta);
}
/**
* Parse the supplied string using the supplied {@code DateTimeFormatter} and return the result
* as a {@code LocalDateTime}
* @param dtf Non-null
* @param s String, or null value
* @return {@code LocalDateTime}, or null if the supplied string parameter is null
* @throws java.time.DateTimeException when supplied string cannot be parsed by the
* {@code DateTimeFormatter} and converted to a {@code LocalDateTime}
*/
public static LocalDateTime parseAsLocalDateTime(DateTimeFormatter dtf, String s) {
if (s == null) {
return null;
}
TemporalAccessor ta = dtf.parse(s);
return LocalDateTime.from(ta);
}
}
unit tests...
import org.junit.jupiter.api.Test;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author JTough
*/
public class DateTimeUtilsTest {
@Test
void testConvertToLocalDateViaInstant_nominal() {
LocalDate ld = DateTimeUtils.convertToLocalDateViaInstant(new java.util.Date());
assertNotNull(ld);
}
@Test
void testConvertToLocalDateViaInstantWithNullValue() {
assertNull(DateTimeUtils.convertToLocalDateViaInstant(null));
}
//-------------------------------------------------------------------------
@Test
void testConvertToLocalDateTimeViaInstant_nominal() {
LocalDateTime ldt = DateTimeUtils.convertToLocalDateTimeViaInstant(new java.util.Date());
assertNotNull(ldt);
}
@Test
void testConvertToLocalDateTimeViaInstantWithNullValue() {
assertNull(DateTimeUtils.convertToLocalDateTimeViaInstant(null));
}
//-------------------------------------------------------------------------
@Test
void testConvertToDateViaInstant_nominal_A() {
java.util.Date d = DateTimeUtils.convertToDateViaInstant(LocalDate.now());
assertNotNull(d);
}
@Test
void testConvertToDateViaInstantWithNullValueA() {
assertNull(DateTimeUtils.convertToDateViaInstant((LocalDate)null));
}
//-------------------------------------------------------------------------
@Test
void testConvertToDateViaInstant_nominal_B() {
java.util.Date d = DateTimeUtils.convertToDateViaInstant(LocalDateTime.now());
assertNotNull(d);
}
@Test
void testConvertToDateViaInstantWithNullValueB() {
assertNull(DateTimeUtils.convertToDateViaInstant((LocalDate)null));
assertNull(DateTimeUtils.convertToDateViaInstant((LocalDateTime)null));
}
//-------------------------------------------------------------------------
@Test
void testParseAsLocalDate_nominal_A() {
LocalDate ld = DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20201225");
assertNotNull(ld);
assertEquals(2020, ld.getYear());
assertEquals(Month.DECEMBER, ld.getMonth());
assertEquals(25, ld.getDayOfMonth());
}
@Test
void testParseAsLocalDate_nominal_B() {
LocalDate ld = DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20200229");
assertNotNull(ld);
assertEquals(2020, ld.getYear());
assertEquals(Month.FEBRUARY, ld.getMonth());
assertEquals(29, ld.getDayOfMonth());
}
@Test
void testParseAsLocalDateWithInvalidDayOfMonth() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20200230"));
}
@Test
void testParseAsLocalDateWithGarbageInputString() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "garbage"));
}
//-------------------------------------------------------------------------
@Test
void testParseAsLocalDateTime_withoutseconds_nominal_A() {
LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012251359");
assertNotNull(ldt);
assertEquals(2020, ldt.getYear());
assertEquals(Month.DECEMBER, ldt.getMonth());
assertEquals(25, ldt.getDayOfMonth());
assertEquals(13, ldt.getHour());
assertEquals(59, ldt.getMinute());
}
@Test
void testParseAsLocalDateTime_withseconds_nominal_A() {
LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMMSS, "20201225 13:59:33");
assertNotNull(ldt);
assertEquals(2020, ldt.getYear());
assertEquals(Month.DECEMBER, ldt.getMonth());
assertEquals(25, ldt.getDayOfMonth());
assertEquals(13, ldt.getHour());
assertEquals(59, ldt.getMinute());
assertEquals(33, ldt.getSecond());
}
@Test
void testParseAsLocalDateTime_withoutseconds_nominal_B() {
LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202002291234");
assertNotNull(ldt);
assertEquals(2020, ldt.getYear());
assertEquals(Month.FEBRUARY, ldt.getMonth());
assertEquals(29, ldt.getDayOfMonth());
assertEquals(12, ldt.getHour());
assertEquals(34, ldt.getMinute());
}
@Test
void testParseAsLocalDateTime_withseconds_nominal_B() {
LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMMSS, "20200229 12:34:56");
assertNotNull(ldt);
assertEquals(2020, ldt.getYear());
assertEquals(Month.FEBRUARY, ldt.getMonth());
assertEquals(29, ldt.getDayOfMonth());
assertEquals(12, ldt.getHour());
assertEquals(34, ldt.getMinute());
assertEquals(56, ldt.getSecond());
}
@Test
void testParseAsLocalDateTimeWithInvalidDayOfMonth() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202002301234"));
}
@Test
void testParseAsLocalDateTimeWithInvalidHour() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012252500"));
}
@Test
void testParseAsLocalDateTimeWithInvalidMinute() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012251366"));
}
@Test
void testParseAsLocalDateTimeWithGarbageInputString() {
assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDD, "garbage"));
}
}