This is ubansi in charge of the second day of the Java Advent calendar! Around last year Fighted devilish Java code.
Dump files of various DBs are put into Bashibashi BigQuery!
Eh, the date isn't parsed by BigQuery ...? Can I change the output because other systems are running? Is there a time difference? Eh, add DB?
・ ・ ・
For those who encounter such things
(Rather, I met such an eye)
Let's create a process to convert all date strings to LocalDateTime
in Java8.
Introducing the squid time class! ZonedDateTime A class that handles ** dates with time ** for each time zone. It also considers daylight saving time.
OffsetDateTime A class that handles ** dates with time ** for each time difference. If there is a time difference specification instead of a time zone specification, this is the turn.
LocalDateTime Needless to say, this class deals with ** time without time difference **. This time we will convert to this class.
LocalDate
A class that handles ** dates ** with no time difference.
If no time is specified, this class will be used for analysis.
You can convert it to LocalDateTime as midnight with LocalDate # atStartOfDay ()
.
Timestamp Used for conversion from Unixtime.
Yes, let's start the explanation. As a procedure, conversion is attempted in descending order of the amount of information as the analysis result. Then, when the conversion is completed, the result is returned. ** I will only throw all errors ** if all parsing fails.
Create an abstract class to parse and inherit it.
TimeParser.java
public abstract class TimeParser {
/**
* fails info
*/
protected List<String> failsMessages = new ArrayList<>();
/**
* This function forcibly converts a string to a date.
*<p>
* If the return value is null, please throw an {@code DateTimeParseException}.
* </p>
* @param input
* @return LocalDateTime instance or {@code null}
*/
public abstract LocalDateTime parse(String input);
protected void addExceptionMessage(Exception e) {
failsMessages.add(e.getMessage() + " ("+this.getClass().getSimpleName()+")");
}
public List<String> getExceptionInfo(){
return failsMessages;
};
}
It has an analysis part and a log acquisition part. For the time being, I keep a log so that I can see what happened if everything failed.
LocalDateParser.java
public class LocalDateParser extends TimeParser {
//Define the format from the beginning
private final static List<DateTimeFormatter> FORMATS = new ArrayList<DateTimeFormatter>() {
{
add(DateTimeFormatter.ISO_LOCAL_DATE);
add(DateTimeFormatter.BASIC_ISO_DATE);
add(DateTimeFormatter.ISO_DATE);
add(DateTimeFormatter.ofPattern("yy-MM-dd"));
add(DateTimeFormatter.ofPattern("yy-M-d"));
add(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
add(DateTimeFormatter.ofPattern("yyyy/M/d"));
add(DateTimeFormatter.ofPattern("yyyy year M month d day"));
}
};
@Override
public LocalDateTime parse(String input) {
failsMessages.clear();
//Try default analysis
try {
return LocalDate.parse(input).atStartOfDay();
} catch (DateTimeParseException e) {
addExceptionMessage(e);
}
//Analyze in specified format
for (DateTimeFormatter formatter : FORMATS) {
try {
return LocalDate.parse(input, formatter).atStartOfDay();
} catch (DateTimeParseException e) {
//Save the failed log to the list
addExceptionMessage(e);
}
}
return null;
}
}
If a DateTimeParseException
occurs, just stock the log and
I will squeeze everything.
The reason why the logger does not output when an exception occurs is that it is output even if there is no problem in processing.
Create a class that inherits TimeParser like this in other date classes.
DateTimeParser.java
public class DateTimeParser {
//Error retention list
private List<String> errors = new ArrayList<>();
//Create analysis class
private final static List<TimeParser> PARSERS = new ArrayList<TimeParser>() {
{
//Analyze in descending order of amount of information
add(new ZonedDateTimeParser());
add(new OffsetDateTimeParser());
add(new LocalDateTimeParser());
add(new LocalDateParser());
add(new TimestampParser());
}
};
public LocalDateTime parse(String input) {
errors.clear();
LocalDateTime result = null;
for (TimeParser parser : PARSERS) {
result = parser.parse(input);
//Returns the result when it succeeds
if (result != null) {
return result;
}
errors.addAll(parser.getExceptionInfo());
}
throw new DateTimeParseException("Analysis failed.(\"" + input + "\")", input, 0);
//There is no return!
}
public List<String> getErrors(){
return errors;
}
}
It has a list of parsing classes and loops.
If the analysis is successful, an instance of LocalDateTime will be returned, so
Returns the result if it is not null
.
In addition, parsing exceptions in the middle can be retrieved after parsing with getErrors ()
.
(It's not thread-safe, but ...)
And only if it fails, it throws an exception at the end.
It's a weird method without a return
.
return
, Eclipse will get angry because it is an error.DateTimeParserTest.java
@Test
public void testDateFormat() {
//Test if this date can be converted
List<String> dates = new ArrayList<String>() {
{
add("2017-12-02");
add("17-12-02");
add("17-12-2");
add("20171202");
add("2017/12/02");
add("2017/12/2");
add("December 2, 2017");
}
};
for (String date : dates) {
LocalDateTime time = dtp.parse(date);
assertEquals("2017-12-02T00:00:00", time.format(formater));
}
}
It's a rough test, but if you analyze all the dates like this, All could be analyzed as "2017-12-02T 00:00:00". You should be able to convert any date by adding formats to the ArrayList of the various parser classes.
You did it!
By the way, the sample code used this time is published on GitHub. https://github.com/ubansi/DateTimeParser
Recommended Posts