Преобразование строки в дату Java: как правильно парсить дату из строки?
String dateStr = "2023-05-15 14:30:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(dateStr);
Компилятор ругается на непойманное исключение ParseException. Я добавил throws ParseException в метод main, но теперь программа просто вылетает с ошибкой. Пробовал разные форматы — "dd.MM.yyyy", "MM/dd/yyyy" — всё равно ошибка.
Ещё я слышал, что SimpleDateFormat не потокобезопасный, но я пока работаю в одном потоке. Может быть, проблема в том, что у меня Java 8? Читал, что появился новый API java.time, но не знаю, как им пользоваться.
Подскажите, пожалуйста, как правильно преобразовать строку в дату в современной Java? И что делать с этим ParseException? Заранее спасибо!
P.S. Строка приходит из базы данных в формате "yyyy-MM-dd HH:mm:ss", иногда с миллисекундами, например "2023-05-15 14:30:00.123". Как обработать оба случая?
<<Решение: используем современный API java.time (Java 8+)
Привет! Ты столкнулся с классической проблемой, которая мучает многих новичков. В современной Java (8 и выше) не рекомендуется использовать SimpleDateFormat и java.util.Date — они устарели, неудобны и содержат множество подводных камней. Вместо этого используй пакет java.time.
Базовое преобразование строки в дату
Вот как правильно парсить твою строку "2023-05-15 14:30:00":
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateParser {
public static void main(String[] args) {
String dateStr = "2023-05-15 14:30:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime); // 2023-05-15T14:30
}
}
Важно: В отличие от SimpleDateFormat, DateTimeFormatter потокобезопасен и не выбрасывает проверяемые исключения (только DateTimeParseException — наследник RuntimeException).
Обработка миллисекунд (два формата в одной строке)
Если строка может быть как с миллисекундами, так и без, используй опциональные секции в паттерне:
String dateStr1 = "2023-05-15 14:30:00";
String dateStr2 = "2023-05-15 14:30:00.123";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]");
// Квадратные скобки делают часть паттерна опциональной
LocalDateTime dt1 = LocalDateTime.parse(dateStr1, formatter);
LocalDateTime dt2 = LocalDateTime.parse(dateStr2, formatter);
System.out.println(dt1); // 2023-05-15T14:30
System.out.println(dt2); // 2023-05-15T14:30:00.123
Если нужно получить java.util.Date (для старого кода)
Иногда требуется совместимость с устаревшими библиотеками. Конвертируй через Instant:
LocalDateTime localDateTime = LocalDateTime.parse(dateStr, formatter);
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date legacyDate = Date.from(instant);
Почему не стоит использовать SimpleDateFormat
- Не потокобезопасен — разделяемый экземпляр между потоками приведёт к гонке данных
- Проверяемое исключение ParseException — засоряет код try-catch блоками
- Проблемы с високосными годами — известны баги при парсинге 29 февраля
- Не поддерживает ISO-8601 — для стандартных форматов приходится писать ручные паттерны
Распространённые ошибки новичков
- Путаница между yyyy и YYYY —
y(year) иY(week-based year) разные вещи. Всегда используйyyyy. - Забывают про часовые пояса — если строка содержит смещение ("+03:00"), используй
OffsetDateTimeилиZonedDateTimeвместоLocalDateTime. - Неправильный паттерн для месяцев —
MM(цифры 01-12) vsMMM(текстовое "Jan") vsMMMM(полное "January").
Лучшие практики
- Храни форматтер как
static finalполе класса — он потокобезопасен - Для стандартных ISO-форматов используй готовые константы:
DateTimeFormatter.ISO_LOCAL_DATE_TIME - Всегда обрабатывай
DateTimeParseExceptionпри работе с пользовательским вводом
Теперь твой код будет работать надёжно и в многопоточной среде. Удачи!
<<Альтернативный подход: используем SimpleDateFormat с правильной обработкой ошибок
Если по каким-то причинам ты не можешь перейти на java.time (например, используешь Java 7 или старую библиотеку), то вот рабочий вариант с SimpleDateFormat, но с правильной обработкой:
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;
public class LegacyDateParser {
public static Date parseDate(String dateStr) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setLenient(false); // строгий режим — не позволит парсить "2023-13-01"
try {
return sdf.parse(dateStr);
} catch (ParseException e) {
System.err.println("Ошибка парсинга даты: " + e.getMessage());
return null; // или выбросить своё исключение
}
}
public static void main(String[] args) {
Date date = parseDate("2023-05-15 14:30:00");
if (date != null) {
System.out.println(date);
}
}
}
Обработка миллисекунд — создавай два форматтера и пробуй по очереди:
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
return sdf1.parse(dateStr);
} catch (ParseException e) {
return sdf2.parse(dateStr); // может снова выбросить ParseException
}
Важно: SimpleDateFormat не потокобезопасен — создавай новый экземпляр для каждого парсинга или используй ThreadLocal. Этот подход — "костыль" для старого кода, в новых проектах всегда используй java.time.