如何使用 DateTimeFormatter 在 java8 中处理 yyyy-mm 和 yyyy
How to process yyyy-mm and yyyy in java8 using DateTimeFormatter
我正在使用 SimpleDateFormat 来格式化或验证日期,但我想通过使用 java 8 DateTimeFormatter 使其线程安全。我无法达到某些要求。
我的申请只接受三种格式。
"yyyy-MM-dd"、"yyyy-MM"、"yyyy"
Existing Code gives me desired output:
/*simple date format to process yyyy-MM-dd format
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd")
/*simple date format to process yyyy-MM format
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM")
/*simple date format to process yyyy format
SimpleDateFormat simpleDateFormat3 = new SimpleDateFormat("yyyy")
/* to parse input
simpleDateFormat.parse(input)
/* to format
simpleDateFormat.format(simpleDateFormat1)
这是输入和预期输出:
input expected
'2018-03-19' '2018-03-19'
'2018-03' '2018-03'
'2018' '2018'
'2017-02-54' '2017-02'
'2016-13-19' '2016'
如何在 java 8 DateTimeFormaenter code here
tter 中获得相同的结果?
/* java 8 日期时间格式器
DateTimeFormatter dateTimeFormatter = new DateTimeFormatter("yyyy-MM-dd")
当所有年、月和日期值都正确时,以上代码段有效。任何帮助将不胜感激。
Am using SimpleDateFormat to format or validate the dates
永远不要使用 SimpleDateFormat
.
与 Java 的最早版本捆绑在一起的可怕日期时间 类 几年前被现代 java.time 类 定义在 JSR 310.
thread-safe by using java 8 DateTimeFormatter
是的,与传统日期时间 类 不同,java.time 类 在设计上使用 immutable objects and are thread-safe。
Here is the input and expected output:
您的一些输入可以简单地通过它们的长度来检测。
// Ten-digits long, assume ISO 8601 date.
LocalDate ld = LocalDate.parse( "2018-03-19" ) ;
// Seven digits long, assume ISO 8601 year-month.
YearMonth ym = YearMonth.parse( "2018-03" ) ;
// Four digits, assume year.
Year y = Year.parse( "2018" ) ;
注意以上输入均符合ISO 8601。当 parsing/generating 字符串时,java.time 类 默认使用 ISO 8601 格式。因此无需指定格式化模式。因此不需要明确的 DateTimeFormatter
对象。
'2017-02-54' '2017-02'
这个例子让我很困惑。如果您的意思是“当遇到日期无效的日期时,只需使用年份和月份而忽略日期”,我想您可以这样做。查看 DateTimeFormatter
上的“宽松”模式。也许使用 DateTimeFormatterBuilder
构建一个灵活的 DateTimeFormatter
。但坦率地说,我会拒绝这样的数据 作为错误输入。生成可靠数据应该是数据的 发布者 的工作,而不是 消费者 的工作来猜测错误数据背后的意图。
input expected
'2016-13-19' '2016'
再次尝试在我不会玩的危险游戏中猜测无效输入的有效部分。如果月和日无效,你怎么知道年有效?更糟糕的是,如果此数据的发布者可以发出此类错误数据,您怎么知道表面上有效的 2018-03-19
输入实际上是正确的?如果月份 13
是错误的,如何知道月份 03
的输入也不是错误?
向这些有问题的数据的发布者传授有关 ISO 8601 标准的知识,并要求他们修复错误。
就像其他答案中的 Basil Bourque 一样,我不一定相信您所要求的也是最能为您服务的。无论如何,作为对这个好答案的一个小补充,我想提出一种方法来处理最后两种情况,即无效日期,就像你说的那样。
为了验证格式,我首先解析字符串而不验证数字。这将拒绝不属于三种格式中任何一种的字符串,例如 2016-03-19T12:00
或 2016 and some nonsense
。 DateTimeFormatter.parseUnresolved
方法负责这部分。如果出现解析错误,此方法会在我们传递给它的 ParsePosition
对象和 returns null
中设置一个错误索引(因此不会抛出任何异常)。所以我检查是否返回 null
。
private static DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("uuuu-MM")
.withResolverStyle(ResolverStyle.STRICT);
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu")
.withResolverStyle(ResolverStyle.STRICT);
public static Temporal parse(String input) {
// First try the three formats, uuuu-MM-dd, uuuu-MM, uuuu, in turn without resolving
TemporalAccessor parsed = null;
for (DateTimeFormatter formatter : Arrays.asList(DateTimeFormatter.ISO_LOCAL_DATE,
yearMonthFormatter, yearFormatter)) {
ParsePosition position = new ParsePosition(0);
TemporalAccessor parseAttempt = formatter.parseUnresolved(input, position);
if (parseAttempt != null && position.getIndex() == input.length()) {
// Success; exit loop
parsed = parseAttempt;
break;
}
}
if (parsed == null) { // didn’t match any of the three formats
throw new IllegalArgumentException("Invalid format: " + input);
}
// Try resolving to either LocalDate, YearMonth or Year
try {
return LocalDate.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR),
parsed.get(ChronoField.DAY_OF_MONTH));
} catch (DateTimeException dteRLd) {
try {
return YearMonth.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR));
} catch (DateTimeException dteRYm) {
return Year.of(parsed.get(ChronoField.YEAR));
}
}
}
让我们试试你的例子:
String[] inputs = {
"2018-03-19",
"2018-03",
"2018",
"2017-02-54",
"2016-13-19"
};
for (String input : inputs) {
Temporal parsed = parse(input);
System.out.format("%-10s %-10s %s%n", input, parsed, parsed.getClass().getName());
}
输出为:
2018-03-19 2018-03-19 java.time.LocalDate
2018-03 2018-03 java.time.YearMonth
2018 2018 java.time.Year
2017-02-54 2017-02 java.time.YearMonth
2016-13-19 2016 java.time.Year
我正在使用 SimpleDateFormat 来格式化或验证日期,但我想通过使用 java 8 DateTimeFormatter 使其线程安全。我无法达到某些要求。
我的申请只接受三种格式。 "yyyy-MM-dd"、"yyyy-MM"、"yyyy"
Existing Code gives me desired output:
/*simple date format to process yyyy-MM-dd format
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd")
/*simple date format to process yyyy-MM format
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM")
/*simple date format to process yyyy format
SimpleDateFormat simpleDateFormat3 = new SimpleDateFormat("yyyy")
/* to parse input
simpleDateFormat.parse(input)
/* to format
simpleDateFormat.format(simpleDateFormat1)
这是输入和预期输出:
input expected
'2018-03-19' '2018-03-19'
'2018-03' '2018-03'
'2018' '2018'
'2017-02-54' '2017-02'
'2016-13-19' '2016'
如何在 java 8 DateTimeForma
enter code here
tter 中获得相同的结果?/* java 8 日期时间格式器 DateTimeFormatter dateTimeFormatter = new DateTimeFormatter("yyyy-MM-dd")
当所有年、月和日期值都正确时,以上代码段有效。任何帮助将不胜感激。
Am using SimpleDateFormat to format or validate the dates
永远不要使用 SimpleDateFormat
.
与 Java 的最早版本捆绑在一起的可怕日期时间 类 几年前被现代 java.time 类 定义在 JSR 310.
thread-safe by using java 8 DateTimeFormatter
是的,与传统日期时间 类 不同,java.time 类 在设计上使用 immutable objects and are thread-safe。
Here is the input and expected output:
您的一些输入可以简单地通过它们的长度来检测。
// Ten-digits long, assume ISO 8601 date.
LocalDate ld = LocalDate.parse( "2018-03-19" ) ;
// Seven digits long, assume ISO 8601 year-month.
YearMonth ym = YearMonth.parse( "2018-03" ) ;
// Four digits, assume year.
Year y = Year.parse( "2018" ) ;
注意以上输入均符合ISO 8601。当 parsing/generating 字符串时,java.time 类 默认使用 ISO 8601 格式。因此无需指定格式化模式。因此不需要明确的 DateTimeFormatter
对象。
'2017-02-54' '2017-02'
这个例子让我很困惑。如果您的意思是“当遇到日期无效的日期时,只需使用年份和月份而忽略日期”,我想您可以这样做。查看 DateTimeFormatter
上的“宽松”模式。也许使用 DateTimeFormatterBuilder
构建一个灵活的 DateTimeFormatter
。但坦率地说,我会拒绝这样的数据 作为错误输入。生成可靠数据应该是数据的 发布者 的工作,而不是 消费者 的工作来猜测错误数据背后的意图。
input expected
'2016-13-19' '2016'
再次尝试在我不会玩的危险游戏中猜测无效输入的有效部分。如果月和日无效,你怎么知道年有效?更糟糕的是,如果此数据的发布者可以发出此类错误数据,您怎么知道表面上有效的 2018-03-19
输入实际上是正确的?如果月份 13
是错误的,如何知道月份 03
的输入也不是错误?
向这些有问题的数据的发布者传授有关 ISO 8601 标准的知识,并要求他们修复错误。
就像其他答案中的 Basil Bourque 一样,我不一定相信您所要求的也是最能为您服务的。无论如何,作为对这个好答案的一个小补充,我想提出一种方法来处理最后两种情况,即无效日期,就像你说的那样。
为了验证格式,我首先解析字符串而不验证数字。这将拒绝不属于三种格式中任何一种的字符串,例如 2016-03-19T12:00
或 2016 and some nonsense
。 DateTimeFormatter.parseUnresolved
方法负责这部分。如果出现解析错误,此方法会在我们传递给它的 ParsePosition
对象和 returns null
中设置一个错误索引(因此不会抛出任何异常)。所以我检查是否返回 null
。
private static DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("uuuu-MM")
.withResolverStyle(ResolverStyle.STRICT);
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu")
.withResolverStyle(ResolverStyle.STRICT);
public static Temporal parse(String input) {
// First try the three formats, uuuu-MM-dd, uuuu-MM, uuuu, in turn without resolving
TemporalAccessor parsed = null;
for (DateTimeFormatter formatter : Arrays.asList(DateTimeFormatter.ISO_LOCAL_DATE,
yearMonthFormatter, yearFormatter)) {
ParsePosition position = new ParsePosition(0);
TemporalAccessor parseAttempt = formatter.parseUnresolved(input, position);
if (parseAttempt != null && position.getIndex() == input.length()) {
// Success; exit loop
parsed = parseAttempt;
break;
}
}
if (parsed == null) { // didn’t match any of the three formats
throw new IllegalArgumentException("Invalid format: " + input);
}
// Try resolving to either LocalDate, YearMonth or Year
try {
return LocalDate.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR),
parsed.get(ChronoField.DAY_OF_MONTH));
} catch (DateTimeException dteRLd) {
try {
return YearMonth.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR));
} catch (DateTimeException dteRYm) {
return Year.of(parsed.get(ChronoField.YEAR));
}
}
}
让我们试试你的例子:
String[] inputs = {
"2018-03-19",
"2018-03",
"2018",
"2017-02-54",
"2016-13-19"
};
for (String input : inputs) {
Temporal parsed = parse(input);
System.out.format("%-10s %-10s %s%n", input, parsed, parsed.getClass().getName());
}
输出为:
2018-03-19 2018-03-19 java.time.LocalDate 2018-03 2018-03 java.time.YearMonth 2018 2018 java.time.Year 2017-02-54 2017-02 java.time.YearMonth 2016-13-19 2016 java.time.Year