hoo*_*knc 5 java java-time datetimeformatter
我们正在尝试编写一个 DateTimeFormatter 来帮助我们验证ISO 8601,该 ISO 8601 允许最终用户仅输入年份、年份和月份,或者年份、月份和日期。我们还想验证输入的日期是否确实存在。
在下面的代码中,有两个日期和日期验证表现得很时髦的示例。第一个是仅针对一年和可选月份的验证测试。这不能正确验证月份“00”。
第二个示例显示测试(NOT)因不正确的可选月份值而失败。
任何友好的指示将不胜感激。
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.TemporalQuery;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class DateTimeFormatterForStackOverflowTest {
@Test
public void test_year_or_year_and_month_not_valid() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu[-MM]")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
this.expectException("1984-0", formatter, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-00", formatter, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-13", formatter, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-99", formatter, YearMonth::from, Year::from);
}
@Test
public void test_year_or_year_month_or_year_month_day_not_valid() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[uuuu-MM-dd][uuuu-MM][uuuu]")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
this.expectException("1984-0", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-00", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-13", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-99", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-00-01", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-13-01", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-0", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-00", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-32", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-12-00", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-12-32", formatter, LocalDate::from, YearMonth::from, Year::from);
}
private void expectException(String value, DateTimeFormatter formatter, TemporalQuery<?>... queries) {
assertThrows(DateTimeParseException.class,
() -> formatter.parseBest(value, queries));
}
}
Run Code Online (Sandbox Code Playgroud)
测试输出:
org.opentest4j.AssertionFailedError: Expected java.time.format.DateTimeParseException to be thrown, but nothing was thrown.
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152)
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:73)
at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:35)
at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3083)
at DateTimeFormatterForStackOverflowTest.expectException(DateTimeFormatterForStackOverflowTest.java:59)
at DateTimeFormatterForStackOverflowTest.test_year_or_year_month_or_year_month_day_not_valid(DateTimeFormatterForStackOverflowTest.java:43)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:185)
at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:189)
at java.base/java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Run Code Online (Sandbox Code Playgroud)
以下是我们最终为解决特定 iso 日期验证问题所做的事情。
首先,我们创建了 5 个不同的验证器(一个对应于我们接受的每个不同的 iso 日期),每个验证器都实现了以下接口:
package app.validation.type;
public interface IsoDateFormatValidator {
boolean isValid(String date);
}
Run Code Online (Sandbox Code Playgroud)
然后,我们创建了一个验证器,它将检查基于 iso 日期的验证器列表,以查看输入的日期是否有效。
package app.validation.type;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
public class ValidIsoDateFormatValidator implements ConstraintValidator<ValidIsoDateFormat, String> {
private final List<IsoDateFormatValidator> isoDateFormatValidatorList;
public ValidIsoDateFormatValidator(List<IsoDateFormatValidator> isoDateFormatValidatorList) {
this.isoDateFormatValidatorList = isoDateFormatValidatorList;
}
@Override
public void initialize(ValidIsoDateFormat annotation) {
}
@Override
public boolean isValid(String inputDate, ConstraintValidatorContext constraintValidatorContext) {
boolean valid = true;
if (StringUtils.hasText(inputDate)) {
valid = isoDateFormatValidatorList.stream()
.map(v -> v.isValid(inputDate))
.anyMatch((Boolean.TRUE::equals));
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
以下是我们使用的各个 iso 日期验证器。
日期/时间 YYYY-MM-DD HH:MM:SS
package app.validation.type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
public class IsoDateTimeFormatValidator implements IsoDateFormatValidator {
private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd HH:mm:ss")
.toFormatter()
.withResolverStyle(ResolverStyle.LENIENT);
@Override
public boolean isValid(String date) {
boolean valid = false;
try {
LocalDateTime ldt = LocalDateTime.parse(date, formatter);
valid = true;
} catch (DateTimeParseException ignored) {
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
带毫秒的日期/时间 YYYY-MM-DD HH:MM:SS.SSS
package app.validation.type;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.TemporalAccessor;
public class IsoDateTimeMillisFormatValidator implements IsoDateFormatValidator {
private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd HH:mm:ss.SSS")
.toFormatter()
.withResolverStyle(ResolverStyle.LENIENT);
@Override
public boolean isValid(String date) {
boolean valid = false;
try {
TemporalAccessor ta = formatter.parse(date);
valid = true;
} catch (DateTimeParseException ignored) {
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
年份 YYYY
package app.validation.type;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
public class IsoYearFormatValidator implements IsoDateFormatValidator {
private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu")
.toFormatter();
@Override
public boolean isValid(String date) {
boolean valid = false;
try {
TemporalAccessor ta = formatter.parse(date);
valid = true;
} catch (DateTimeParseException ignored) {
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
年/月 YYYY/MM
package app.validation.type;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
public class IsoYearMonthFormatValidator implements IsoDateFormatValidator {
private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM")
.toFormatter();
@Override
public boolean isValid(String date) {
boolean valid = false;
try {
TemporalAccessor ta = formatter.parse(date);
valid = true;
} catch (DateTimeParseException ignored) {
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
年/月/日 YYYY/MM/DD
package app.validation.type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
public class IsoYearMonthDayFormatValidator implements IsoDateFormatValidator {
private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd")
.toFormatter()
.withResolverStyle ( ResolverStyle.LENIENT );
@Override
public boolean isValid(String date) {
boolean valid = false;
try {
LocalDate ld = LocalDate.parse (date, formatter);
valid = true;
} catch (DateTimeParseException ignored) {
}
return valid;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果您有其他 iso 日期/时间所需的验证未在此处列出,则可以添加这些验证,因为ValidIsoDateFormatValidator需要 s 列表IsoDateFormatValidator。
| 归档时间: |
|
| 查看次数: |
117 次 |
| 最近记录: |