Implementierung von Best Practices für das Parsen von Datum und Uhrzeit
In diesem letzten Schritt erstellen wir einen umfassenden Datums- und Uhrzeit-Parser, der die Best Practices der Branche für die Verarbeitung von Datums- und Uhrzeitwerten implementiert. Wir werden auch zusätzliche Techniken untersuchen, die professionelle Java-Entwickler verwenden, um DateTimeParseException zu vermeiden und zu behandeln.
Erstellen eines produktionsreifen Datums- und Uhrzeit-Parsers
Lassen Sie uns eine Utility-Klasse erstellen, die alle Best Practices, die wir gelernt haben, integriert:
- Klicken Sie in der WebIDE mit der rechten Maustaste auf den Ordner
/home/labex/project.
- Wählen Sie "New File" und nennen Sie sie
DateTimeParserUtil.java.
- Kopieren Sie den folgenden Code und fügen Sie ihn ein:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
/**
* Utility class for parsing date and time strings in a robust manner.
*/
public class DateTimeParserUtil {
// Common date formatters with STRICT resolver for validation
private static final List<DateTimeFormatter> DATE_FORMATTERS = Arrays.asList(
createFormatter("yyyy-MM-dd"),
createFormatter("yyyy/MM/dd"),
createFormatter("MM/dd/yyyy"),
createFormatter("dd-MM-yyyy"),
createFormatter("d MMM yyyy"),
createFormatter("MMMM d, yyyy")
);
// Common time formatters
private static final List<DateTimeFormatter> TIME_FORMATTERS = Arrays.asList(
createFormatter("HH:mm:ss"),
createFormatter("HH:mm"),
createFormatter("h:mm a"),
createFormatter("h:mm:ss a")
);
// Common date-time formatters
private static final List<DateTimeFormatter> DATETIME_FORMATTERS = Arrays.asList(
createFormatter("yyyy-MM-dd HH:mm:ss"),
createFormatter("yyyy-MM-dd'T'HH:mm:ss"),
createFormatter("yyyy-MM-dd HH:mm"),
createFormatter("MM/dd/yyyy HH:mm:ss"),
createFormatter("dd-MM-yyyy HH:mm:ss")
);
/**
* Creates a DateTimeFormatter with strict resolver style.
*/
private static DateTimeFormatter createFormatter(String pattern) {
return new DateTimeFormatterBuilder()
.appendPattern(pattern)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT)
.withLocale(Locale.US);
}
/**
* Parses a string into a LocalDate.
*
* @param dateStr the date string to parse
* @return an Optional containing the parsed LocalDate, or empty if parsing fails
*/
public static Optional<LocalDate> parseDate(String dateStr) {
if (dateStr == null || dateStr.trim().isEmpty()) {
return Optional.empty();
}
for (DateTimeFormatter formatter : DATE_FORMATTERS) {
try {
LocalDate date = LocalDate.parse(dateStr, formatter);
return Optional.of(date);
} catch (DateTimeParseException e) {
// Try next formatter
}
}
return Optional.empty();
}
/**
* Parses a string into a LocalTime.
*
* @param timeStr the time string to parse
* @return an Optional containing the parsed LocalTime, or empty if parsing fails
*/
public static Optional<LocalTime> parseTime(String timeStr) {
if (timeStr == null || timeStr.trim().isEmpty()) {
return Optional.empty();
}
for (DateTimeFormatter formatter : TIME_FORMATTERS) {
try {
LocalTime time = LocalTime.parse(timeStr, formatter);
return Optional.of(time);
} catch (DateTimeParseException e) {
// Try next formatter
}
}
return Optional.empty();
}
/**
* Parses a string into a LocalDateTime.
*
* @param dateTimeStr the date-time string to parse
* @return an Optional containing the parsed LocalDateTime, or empty if parsing fails
*/
public static Optional<LocalDateTime> parseDateTime(String dateTimeStr) {
if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
return Optional.empty();
}
// First try with combined date-time formatters
for (DateTimeFormatter formatter : DATETIME_FORMATTERS) {
try {
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, formatter);
return Optional.of(dateTime);
} catch (DateTimeParseException e) {
// Try next formatter
}
}
// Then try to split into date and time parts
String[] parts = dateTimeStr.split(" ", 2);
if (parts.length == 2) {
Optional<LocalDate> date = parseDate(parts[0]);
Optional<LocalTime> time = parseTime(parts[1]);
if (date.isPresent() && time.isPresent()) {
return Optional.of(LocalDateTime.of(date.get(), time.get()));
}
}
return Optional.empty();
}
/**
* Attempts to parse a string as a date, time, or date-time value.
*
* @param input the string to parse
* @return a string describing the parsed result or an error message
*/
public static String parseAny(String input) {
Optional<LocalDate> date = parseDate(input);
if (date.isPresent()) {
return "Parsed as date: " + date.get();
}
Optional<LocalTime> time = parseTime(input);
if (time.isPresent()) {
return "Parsed as time: " + time.get();
}
Optional<LocalDateTime> dateTime = parseDateTime(input);
if (dateTime.isPresent()) {
return "Parsed as date-time: " + dateTime.get();
}
return "Could not parse: " + input;
}
}
Lassen Sie uns nun eine Testklasse erstellen, um die Utility zu demonstrieren:
- Erstellen Sie in der WebIDE eine neue Datei mit dem Namen
DateTimeParsingDemo.java.
- Kopieren Sie den folgenden Code und fügen Sie ihn ein:
public class DateTimeParsingDemo {
public static void main(String[] args) {
// Test with various date, time, and date-time strings
String[] inputs = {
// Valid dates
"2023-05-15",
"05/15/2023",
"15 May 2023",
// Valid times
"14:30:00",
"2:30 PM",
"14:30",
// Valid date-times
"2023-05-15 14:30:00",
"2023-05-15T14:30:00",
"05/15/2023 14:30:00",
"15-05-2023 14:30:00",
// Invalid examples
"2023-02-30",
"25:30:00",
"2023-13-01",
"Not a date or time",
""
};
for (String input : inputs) {
String result = DateTimeParserUtil.parseAny(input);
System.out.println("Input: \"" + input + "\" → " + result);
}
}
}
Kompilieren und führen Sie dieses Programm aus:
cd ~/project
javac DateTimeParserUtil.java DateTimeParsingDemo.java
java DateTimeParsingDemo
Sie sollten eine Ausgabe ähnlich dieser sehen:
Input: "2023-05-15" → Parsed as date: 2023-05-15
Input: "05/15/2023" → Parsed as date: 2023-05-15
Input: "15 May 2023" → Parsed as date: 2023-05-15
Input: "14:30:00" → Parsed as time: 14:30
Input: "2:30 PM" → Parsed as time: 14:30
Input: "14:30" → Parsed as time: 14:30
Input: "2023-05-15 14:30:00" → Parsed as date-time: 2023-05-15T14:30
Input: "2023-05-15T14:30:00" → Parsed as date-time: 2023-05-15T14:30
Input: "05/15/2023 14:30:00" → Parsed as date-time: 2023-05-15T14:30
Input: "15-05-2023 14:30:00" → Parsed as date-time: 2023-05-15T14:30
Input: "2023-02-30" → Could not parse: 2023-02-30
Input: "25:30:00" → Could not parse: 25:30:00
Input: "2023-13-01" → Could not parse: 2023-13-01
Input: "Not a date or time" → Could not parse: Not a date or time
Input: "" → Could not parse:
Best Practices für das Parsen von Datum und Uhrzeit in Java
Lassen Sie uns die wichtigsten Best Practices untersuchen, die in unserer Utility-Klasse implementiert sind:
1. Verwenden Sie die Einstellung ResolverStyle.STRICT
.withResolverStyle(ResolverStyle.STRICT)
Der STRICT-Resolver-Stil stellt sicher, dass nur gültige Daten und Uhrzeiten akzeptiert werden. Dies verhindert Probleme wie das Parsen von "Februar 31" zu März 3.
2. Geben Sie ein Standard-Gebietsschema an
.withLocale(Locale.US)
Geben Sie immer das Gebietsschema an, um ein konsistentes Parsing-Verhalten zu gewährleisten, insbesondere für Monatsnamen und AM/PM-Indikatoren.
3. Verwenden Sie den optionalen Rückgabetyp
public static Optional<LocalDate> parseDate(String dateStr) {
// ...
return Optional.empty();
}
Die Verwendung von Optional macht deutlich, dass das Parsen fehlschlagen kann, und zwingt den aufrufenden Code, diesen Fall explizit zu behandeln.
private static final List<DateTimeFormatter> DATE_FORMATTERS = Arrays.asList(
createFormatter("yyyy-MM-dd"),
createFormatter("yyyy/MM/dd"),
// ...
);
Die Unterstützung mehrerer Formate erhöht die Robustheit Ihrer Parsing-Logik bei der Verarbeitung verschiedener Eingabequellen.
5. Fehlende Zeitfelder standardmäßig festlegen
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
Das Standardisieren fehlender Zeitfelder ermöglicht ein flexibleres Parsen, insbesondere beim Konvertieren zwischen verschiedenen Datums- und Uhrzeittypen.
6. Eingabevalidierung
if (dateStr == null || dateStr.trim().isEmpty()) {
return Optional.empty();
}
Validieren Sie die Eingabe immer, bevor Sie versuchen, sie zu parsen, um unnötige Ausnahmen zu vermeiden.
7. Fehlerbehandlung
try {
LocalDate date = LocalDate.parse(dateStr, formatter);
return Optional.of(date);
} catch (DateTimeParseException e) {
// Try next formatter
}
Behandeln Sie Ausnahmen ordnungsgemäß und geben Sie klares Feedback, wenn das Parsen fehlschlägt.
8. Intelligentes zusammengesetztes Parsen
// Then try to split into date and time parts
String[] parts = dateTimeStr.split(" ", 2);
if (parts.length == 2) {
Optional<LocalDate> date = parseDate(parts[0]);
Optional<LocalTime> time = parseTime(parts[1]);
if (date.isPresent() && time.isPresent()) {
return Optional.of(LocalDateTime.of(date.get(), time.get()));
}
}
Das Aufteilen komplexer Parsing-Aufgaben in einfachere Komponenten kann oft bessere Ergebnisse erzielen.
Durch die Implementierung dieser Best Practices können Sie eine robuste Logik zum Parsen von Datum und Uhrzeit erstellen, die eine Vielzahl von Eingaben problemlos verarbeitet und klares Feedback gibt, wenn das Parsen fehlschlägt.