import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
	public static void main(String[] args) {
		DateTimeFormatter dtf = 
				new DateTimeFormatterBuilder()
				.appendValue(ChronoField.YEAR, 1, 4, SignStyle.NORMAL)
				.appendLiteral('-')
				.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
				.appendLiteral('-')
				.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
				.appendLiteral(' ')
				.appendValue(ChronoField.HOUR_OF_DAY, 1, 2, SignStyle.NORMAL)
				.appendLiteral(':')
				.appendValue(ChronoField.MINUTE_OF_HOUR, 1, 2, SignStyle.NORMAL)
				.appendLiteral(':')
				.appendValue(ChronoField.SECOND_OF_MINUTE, 1, 2, SignStyle.NORMAL)
				.optionalStart()
				.appendLiteral('.')
				.appendFraction(ChronoField.NANO_OF_SECOND, 0, 3, false)
				.optionalEnd()
				.toFormatter(Locale.ENGLISH)
				.withResolverStyle(ResolverStyle.STRICT);
		
		// Test
		Stream.of(
				"2008-06-31 23:59:59.000",
				"2008-06-30 23:59:59.000",
				"2008-6-30 23:59:59.000",
				"2008-6-8 23:59:59.000",
				"2008-6-8 2:59:59.000",
				"2008-6-8 23:5:59.000",
				"2008-6-8 23:59:9.000",
				"2008-06-30 23:59:59"
				
		).forEach (s -> {
			try {
				LocalDateTime ldt = LocalDateTime.parse(s, dtf);
				System.out.println(ldt);
			}catch(DateTimeParseException e) {
				System.out.printf("%s is an invalid date-time string.%n", s);
				// ...
			}
		});		
	}
}