validationJava 8日期
我想validation几种日期格式,如下例所示:
YYYY YYYY-MM YYYY-MM-DD
validation必须确保日期格式正确且日期存在。
我知道Java 8提供了一个新的Date API,所以我想知道它是否能够完成这样的工作。
有没有更好的方法使用Java 8 date API? 将Calendar类与lenient参数一起使用仍然是一个好习惯吗?
您可以使用parseDefaulting
指定缺少的字段,以使所有格式化程序工作:
public static boolean isValid(String input) { DateTimeFormatter[] formatters = { new DateTimeFormatterBuilder().appendPattern("yyyy") .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) .toFormatter(), new DateTimeFormatterBuilder().appendPattern("yyyy-MM") .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) .toFormatter(), new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd") .parseStrict().toFormatter() }; for(DateTimeFormatter formatter : formatters) { try { LocalDate.parse(input, formatter); return true; } catch (DateTimeParseException e) { } } return false; }
要validationYYYY-MM-DD
格式,您可以简单地使用自JDK 8以来在java.time
引入的LocalDate.parse
。
从文本字符串(如2007-12-03)获取LocalDate的实例。
该字符串必须表示有效日期,并使用DateTimeFormatter.ISO_LOCAL_DATE进行解析。
如果日期无效,则抛出DateTimeParseException
。
对于您给我们的其他两种格式,将抛出exception。 这是合乎逻辑的,因为它们不是真正的约会,只是日期的一部分。
LocalDate还提供了一种方法of(int year, int month, int dayOfMonth)
因此,如果你真的想在某些情况下简单地validation年份,在其他情况下的月份或完整日期那么你可以做这样的事情:
public static final boolean validateInputDate(final String isoDate) { String[] dateProperties = isoDate.split("-"); if(dateProperties != null) { int year = Integer.parseInt(dateProperties[0]); // A valid month by default in the case it is not provided. int month = dateProperties.length > 1 ? Integer.parseInt(dateProperties[1]) : 1; // A valid day by default in the case it is not provided. int day = dateProperties.length > 2 ? Integer.parseInt(dateProperties[2]) : 1; try { LocalDate.of(year, month, day); return true; } catch(DateTimeException e) { return false; } } return false; }
请注意,您提到了几种格式,但没有提供它们,所以我认为这些只是3种。
使用可选字段和parseBest
您只想validation,我理解,但之后您很可能希望以适当的方式提取数据。 幸运的是,确实如您所写,Java 8提供了这样一种方法parseBest
。
parseBest
适用于可选字段。 因此,首先定义要解析的格式: yyyy[-MM[-dd]]
,括号( [
和]
)包含可选字段。
parseBest
还要求您提供几个TemporalQuery
。 实际上,它只是模板方法
的function包装器。 所以我们实际上可以将TemporalQuery
定义为Year::from
。 好的:这正是我们想要的。 问题是parseBest
命名并不是很好:它将按顺序解析所有内容并在第一个匹配的TemporalQuery
之后停止。 所以在你的情况下,我们必须从最精确到最不精确。 以下是您要处理的各种类型: LocalDate
, YearMonth
和Year
。 因此,让我们将TemporalQuery[]
定义为LocalDate::from, YearMonth::from, Year::from
。 现在,如果parseBest
无法识别您的输入,它将抛出exception。
总而言之,我们将构建parseBest
,如下所示:
parseBest(DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]"), LocalDate::from, YearMonth::from, Year::from);
所以让我们正确地写下来:
static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]"); static TemporalAccessor parseDate(String dateAsString) { return FORMATTER.parseBest(dateAsString, LocalDate::from, YearMonth::from, Year::from); }
但是……你只想validation……那么在这种情况下,计算一个日期并且已经完成了昂贵的工作。 所以我们只需将validation定义如下:
public static boolean isValidDate(String dateAsString) { try { parseDate(dateAsString); return true; } catch (DateTimeParseException e) { return false; } }
我知道,使用exception来处理这样的情况是不好的,但是当前的API非常强大时,这个非常具体的情况并未考虑在内,所以让我们坚持下去并按原样使用它。
这是完整的代码:
import java.time.*; import java.time.format.*; import java.time.temporal.*; class Main { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]"); static TemporalAccessor parseDate(String dateAsString) { return FORMATTER.parseBest(dateAsString, LocalDate::from, YearMonth::from, Year::from); } public static boolean isValidDate(String dateAsString) { try { parseDate(dateAsString); return true; } catch (DateTimeParseException e) { return false; } } public static void main(String[] args) { String[] datesAsString = { "2018", "2018-05", "2018-05-22", "abc", "2018-" }; for (String dateAsString: datesAsString) { System.out.printf("%s: %s%n", dateAsString, isValidDate(dateAsString) ? "valid" : "invalid"); } } }
在线尝试!
输出:
2018: valid 2018-05: valid 2018-05-22: valid abc: invalid 2018-: invalid
你想要的不仅仅是validation,比如获得实际价值?
请注意,您仍然可以使用从parseBest
检索到的数据进行进一步的使用,如下所示:
TemporalAccessor dateAccessor = parseDate(dateAsString); if (dateAccessor instanceof Year) { Year year = (Year)dateAccessor; // Use year } else if (dateAccessor instanceof YearMonth) { YearMonth yearMonth = (YearMonth)dateAccessor; // Use yearMonth } else if (dateAccessor instanceof LocalDate) { LocalDate localDate = (LocalDate)dateAccessor; // Use localDate }
public static final boolean validateInputDate(final String isoDate, final String dateFormat){ final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); try { final Date date = simpleDateFormat.parse(isoDate); System.out.println("Date: " + date); return true; } catch (ParseException e) { e.printStackTrace(); return false; } }