Java 字符串到日期时间解析问题
Java String to DateTime parse issue
我有一条记录有一个异常的 DateTime 字符串 '20170-09-17',我试图将它解析为 DateTime 但它没有抛出任何异常并且这条记录无法放入 MySQL还有...
DateFormat fmt =new SimpleDateFormat("yyyy-MM-dd");
System.out.println(fmt.parse("20170-09-17"));
如何处理这些数据?
SimpleDateFormat
不知道存储日期的支持年份范围,因此 you 需要验证日期。
既然你提到了 MySQL,并且 DATETIME
数据类型支持 1000-9999 范围内的年份,你应该这样做:
static Date parseDate(String dateStr) throws ParseException {
// Parse the string (strict)
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
fmt.setLenient(false); // Reject invalid month and day values
Date date = fmt.parse(dateStr);
// Validate year
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
if (year < 1000 || year > 9999)
throw new IllegalArgumentException("Invalid year: \"" + dateStr + "\"");
return date;
}
重要的部分是:
开启严格解析模式,确保month
和day
值在有效范围内。
自定义检查以确保 year
值在有效范围内。
如果你能使用较新的 Java 8+ 时间 API 可能会更好,在这种情况下代码将是:
static LocalDate parseDate(String dateStr) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuu-MM-dd")
.withResolverStyle(ResolverStyle.STRICT);
LocalDate date = LocalDate.parse(dateStr, fmt);
if (date.getYear() < 1000 || date.getYear() > 9999)
throw new DateTimeException("Invalid value for Year (valid values 1000 - 9999): " + date.getYear());
return date;
}
和以前一样:严格解析和自定义年份范围检查。
SimpleDateFormat 和 Date 已过时 类。如果您使用 java 8 或更高版本,请切换到 DateTimeFormatter class and classes like LocalDate LocalDateTime 或他们的任何“兄弟”
java.time
我总是建议使用 java.time,现代 Java 日期和时间 API,进行日期工作。在这种情况下,它使您的任务变得非常简单。
String abnormalInput = "20170-09-17";
LocalDate date = LocalDate.parse(abnormalInput);
System.out.println(date);
这引发了您预期的异常:
Exception in thread "main" java.time.format.DateTimeParseException:
Text '20170-09-17' could not be parsed at index 0
我们甚至不需要指定任何格式化程序。您预期的输入是 ISO 8601 格式(link 在底部),LocalDate
将 ISO 8601 解析为默认格式。如果要解析的文本不符合格式,则抛出异常。它 可以 接受超过四位数的年份,但只能有一个符号(减号或加号)。因为标准是这么说的。
Andreas 在他的回答中建议的范围检查仍然是一个好主意。我建议您设置限制不是根据 MySQL 可以处理的内容,而是根据您的域中可以认为是正确的内容。举个例子:
LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
if (date.getYear() < today.getYear() - 5 || date.isAfter(today)) {
throw new IllegalStateException("Date is out of range: " + date);
}
链接
- Oracle tutorial: Date Time 解释如何使用 java.time.
- Wikipedia article: ISO 8601
我有一条记录有一个异常的 DateTime 字符串 '20170-09-17',我试图将它解析为 DateTime 但它没有抛出任何异常并且这条记录无法放入 MySQL还有...
DateFormat fmt =new SimpleDateFormat("yyyy-MM-dd");
System.out.println(fmt.parse("20170-09-17"));
如何处理这些数据?
SimpleDateFormat
不知道存储日期的支持年份范围,因此 you 需要验证日期。
既然你提到了 MySQL,并且 DATETIME
数据类型支持 1000-9999 范围内的年份,你应该这样做:
static Date parseDate(String dateStr) throws ParseException {
// Parse the string (strict)
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
fmt.setLenient(false); // Reject invalid month and day values
Date date = fmt.parse(dateStr);
// Validate year
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
if (year < 1000 || year > 9999)
throw new IllegalArgumentException("Invalid year: \"" + dateStr + "\"");
return date;
}
重要的部分是:
开启严格解析模式,确保
month
和day
值在有效范围内。自定义检查以确保
year
值在有效范围内。
如果你能使用较新的 Java 8+ 时间 API 可能会更好,在这种情况下代码将是:
static LocalDate parseDate(String dateStr) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuu-MM-dd")
.withResolverStyle(ResolverStyle.STRICT);
LocalDate date = LocalDate.parse(dateStr, fmt);
if (date.getYear() < 1000 || date.getYear() > 9999)
throw new DateTimeException("Invalid value for Year (valid values 1000 - 9999): " + date.getYear());
return date;
}
和以前一样:严格解析和自定义年份范围检查。
SimpleDateFormat 和 Date 已过时 类。如果您使用 java 8 或更高版本,请切换到 DateTimeFormatter class and classes like LocalDate LocalDateTime 或他们的任何“兄弟”
java.time
我总是建议使用 java.time,现代 Java 日期和时间 API,进行日期工作。在这种情况下,它使您的任务变得非常简单。
String abnormalInput = "20170-09-17";
LocalDate date = LocalDate.parse(abnormalInput);
System.out.println(date);
这引发了您预期的异常:
Exception in thread "main" java.time.format.DateTimeParseException: Text '20170-09-17' could not be parsed at index 0
我们甚至不需要指定任何格式化程序。您预期的输入是 ISO 8601 格式(link 在底部),LocalDate
将 ISO 8601 解析为默认格式。如果要解析的文本不符合格式,则抛出异常。它 可以 接受超过四位数的年份,但只能有一个符号(减号或加号)。因为标准是这么说的。
Andreas 在他的回答中建议的范围检查仍然是一个好主意。我建议您设置限制不是根据 MySQL 可以处理的内容,而是根据您的域中可以认为是正确的内容。举个例子:
LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
if (date.getYear() < today.getYear() - 5 || date.isAfter(today)) {
throw new IllegalStateException("Date is out of range: " + date);
}
链接
- Oracle tutorial: Date Time 解释如何使用 java.time.
- Wikipedia article: ISO 8601