我想验证几种日期格式,如下例所示:
YYYY
YYYY-MM
YYYY-MM-DD
Run Code Online (Sandbox Code Playgroud)
验证必须确保日期格式正确且日期存在.
我知道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;
}
Run Code Online (Sandbox Code Playgroud)
要验证YYYY-MM-DD格式,您可以简单地使用JDK 8中LocalDate.parse引入的java.time。
从文本字符串(例如2007-12-03)获取LocalDate的实例。
该字符串必须表示一个有效日期,并使用DateTimeFormatter.ISO_LOCAL_DATE进行解析。
DateTimeParseException如果日期无效,将引发A。
对于您提供给我们的其他两种格式,将引发异常。这是合乎逻辑的,因为它们不是真实日期,而只是日期的一部分。
LocalDate还提供了一种方法,of(int year, int month, int dayOfMonth)因此,如果您确实确实希望仅在某些情况下验证年份,在其他情况下验证带有年份的年份或完整日期,则可以执行以下操作:
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,您提到了几种格式,但没有提供它们,因此我认为只有3种。
parseBest您只想验证,我理解这一点,但之后您很可能希望以适当的方式提取数据。幸运的是,正如您所写的,Java 8 提供了这样一种方法,parseBest.
parseBest适用于可选字段。因此,首先定义您要解析的格式:yyyy[-MM[-dd]],用括号 ([和]) 包装可选字段。
parseBest还需要你提供几个TemporalQuery<R>。实际上,它只是一个模板方法的功能包装器<R> R queryFrom(TemporalAccessor)。所以我们实际上可以TemporalQuery<R>简单地定义 a Year::from。好:这正是我们想要的。问题是它的parseBest名字不是很好:它将按顺序解析所有内容并在TemporalQuery匹配的第一个正确之后停止。所以在你的情况下,我们必须从最精确到不太精确。以下是您要处理的各种类型:LocalDate、YearMonth和Year。所以让我们定义TemporalQuery[]as LocalDate::from, YearMonth::from, Year::from。现在,如果parseBest不能识别您的输入,它将抛出异常。
总而言之,我们将构建parseBest如下:
parseBest(DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]"), LocalDate::from, YearMonth::from, Year::from);
Run Code Online (Sandbox Code Playgroud)
所以让我们正确地写它:
static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]");
static TemporalAccessor parseDate(String dateAsString) {
return FORMATTER.parseBest(dateAsString, LocalDate::from, YearMonth::from, Year::from);
}
Run Code Online (Sandbox Code Playgroud)
但是......你只想验证......那么在这种情况下,计算日期并且已经完成了昂贵的工作。所以让我们定义验证如下:
public static boolean isValidDate(String dateAsString) {
try {
parseDate(dateAsString);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
我知道,使用异常来处理这样的情况是不好的,但是虽然当前的 API 非常强大,但没有考虑到这个非常具体的情况,所以让我们坚持下去并按原样使用它。
但是现在,我们遇到了这个问题:可以解析无效的日期。例如"2018-15"将被解析为Year{2018}因为Year是TemporalAccessor解析器可以使用的最好的:YearMonth会失败,但它是 a YearMonth,所以它必须根据那个是有效的。所以让我们在FORMATTER.
public static boolean isValidDate(String dateAsString) {
try {
TemporalAccessor date = parseDate(dateAsString);
return FORMATTER.format(date).equals(dateAsString);
} catch (DateTimeParseException e) {
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
这是完整的代码:
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 {
TemporalAccessor parsedDate = parseDate(dateAsString);
return FORMATTER.format(parsedDate).equals(dateAsString);
} catch (DateTimeParseException e) {
return false;
}
}
public static void main(String[] args) {  
String[] datesAsString = {
"2018",
"2018-05",
"2018-05-22",
"abc",
"2018-",
"2018-15",
};
for (String dateAsString: datesAsString) {
System.out.printf("%s: %s%n", dateAsString, isValidDate(dateAsString) ? "valid" : "invalid");
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
2018: valid
2018-05: valid
2018-05-22: valid
abc: invalid
2018-: invalid
2018-15: invalid
Run Code Online (Sandbox Code Playgroud)
请注意,您仍然可以将检索到的数据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
}
Run Code Online (Sandbox Code Playgroud)
但要小心,因为如前所述,"2018-15"仍然会被解析为 asYear{2018}和 not YearMonth{2018,15},或者"2018-02-30"仍然会被解析为 aYearMonth而不是 a LocalDate。
| 归档时间: |
|
| 查看次数: |
15221 次 |
| 最近记录: |