无法从基于周的字符串的 TemporalAccessor 获取 LocalDate
Unable to obtain LocalDate from TemporalAccessor for week based string
我正在尝试将 "YYYYww" 格式的简单字符串(例如 201901)解析为 LocalDate,但 none 我的尝试成功了。
我试图通过简单地使用模式 "YYYYww" 以及通过手动将值附加到 FormatterBuilder 来解析它。
由于我的输入字符串不包含一天,我还将格式化程序配置为默认为星期日。
这是对我来说失败的代码,运行 Java 8 (IBM JRE 8.0.5.25)。
public static void main(String[] args) {
formatter1(); // Unable to obtain LocalDate from TemporalAccessor
formatter2(); // Text '201901' could not be parsed at index 0
formatter3(); // Text '201901' could not be parsed at index 6
}
public static void formatter1() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear(), 4, 4, SignStyle.NEVER)
.appendValue(WeekFields.ISO.weekOfYear(), 2, 2, SignStyle.NEVER)
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
public static void formatter2() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYYww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
public static void formatter3() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseLenient()
.appendPattern("YYYYww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
如示例代码所示,我收到了不同的错误消息,尤其是第一个示例让我感到困惑,因为 TemporalAccessor 包含基于周的年份、一年中的星期和工作日,它们应该是足以构建一个 LocalDate。
Exception in thread "main" java.time.format.DateTimeParseException: Text '201901' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1931)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1866)
at java.time.LocalDate.parse(LocalDate.java:411)
at Main.formatter1(Main.java:22)
at Main.main(Main.java:10)
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:379)
at java.time.LocalDate$$Lambda.000000001061ED20.queryFrom(Unknown Source)
at java.time.format.Parsed.query(Parsed.java:237)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1862)
... 3 more
您可以使用YearMonth
public static void main(String[] args) {
System.out.println(YearMonth.parse("201901", DateTimeFormatter.ofPattern("yyyyMM")));
}
输出
2019-01
据我所知你不能那样做。据我所知,您的代码有两个问题。首先是 LocalDate
没有足够的信息,因为您的代码不知道它应该解析到哪个工作日。
二是略显奇怪的字段的使用。如果你只是使用 Chrono 字段,你应该没问题。您也可以调整您的模式以包含 space 或破折号,因为解析似乎不喜欢它在一个数字中 - 虽然不知道为什么
例如您可以这样做:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
.appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2, 2, SignStyle.NEVER)
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate d = LocalDate.parse("201933", formatter);
但是,使用周数似乎不是一个好的选择。如果你能按照@butiri-dan 的建议度过几个月,你很可能会过得更好。
编辑:根据@carlos-heuberger 的建议,它现在在 parseDefaulting
-setep
中使用 ChronoField.DAY_OF_WEEK
嗯,2019 年的第一周并没有给你一个特定的日期。您需要添加一周中的特定日期。我运行这个:
System.out.println(LocalDate.parse("2019-01-01", DateTimeFormatter.ofPattern("YYYY-ww-ee")));
得到"2018-12-30"
(那是因为2019年1月1日是星期二,也就是一周的第三天,所以2019年第一周的第一天实际上是"2018-12-30"
)。请注意,由于某种原因没有破折号 "-"
它不起作用。要阅读有关格式化程序选项的信息,请访问此处:DateTimeFormatter
这适用于 Java 8 及更高版本:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear(), 4)
.appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2)
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
System.out.println(LocalDate.parse("201901", formatter));
输出为:
2019-01-06
我已经在 Oracle Java 1.8.0_101 上进行了测试。我还没有安装任何 IBM Java。
让它发挥作用的关键是使用 weekOfWeekBasedYear()
而不是 weekOfYear()
。我的其他变化是装饰性的。 WeekFields.ISO.weekOfYear()
通过计算一年中的星期一来定义周数,这不是您想要的,因为根据 ISO,第 1 周可能从上一年年底(12 月 29 日到 31 日)附近的星期一开始。因此,它也不能很好地与 基于周的年份 一起使用,并且 Java 拒绝将两者一起使用来标识一周。
在 Java 9 上,您的 formatter2()
和 formatter3()
也可以工作(在 Oracle Java 9.0.4 上测试)。 Java 8 中似乎存在一个错误,其中相邻字段解析对 YYYYww
不起作用。不过,我搜索了 Oracle Java 错误数据库,但没有找到错误描述。
ISO 8601
ISO 8601 是规定您正在使用的星期定义的标准,它还定义了星期的格式。它就像 2012-W48
。因此,除非您有充分且非常具体的理由不这样做,否则这是您应该使用的格式,尤其是在与任何外部系统交换数据时。它也恰好消除了 Java 8.
上的相邻字段解析问题
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYY-'W'ww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
System.out.println(LocalDate.parse("2019-W01", formatter));
2019-01-06
Link
我正在尝试将 "YYYYww" 格式的简单字符串(例如 201901)解析为 LocalDate,但 none 我的尝试成功了。
我试图通过简单地使用模式 "YYYYww" 以及通过手动将值附加到 FormatterBuilder 来解析它。 由于我的输入字符串不包含一天,我还将格式化程序配置为默认为星期日。
这是对我来说失败的代码,运行 Java 8 (IBM JRE 8.0.5.25)。
public static void main(String[] args) {
formatter1(); // Unable to obtain LocalDate from TemporalAccessor
formatter2(); // Text '201901' could not be parsed at index 0
formatter3(); // Text '201901' could not be parsed at index 6
}
public static void formatter1() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear(), 4, 4, SignStyle.NEVER)
.appendValue(WeekFields.ISO.weekOfYear(), 2, 2, SignStyle.NEVER)
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
public static void formatter2() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYYww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
public static void formatter3() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseLenient()
.appendPattern("YYYYww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate.parse("201901", formatter);
}
如示例代码所示,我收到了不同的错误消息,尤其是第一个示例让我感到困惑,因为 TemporalAccessor 包含基于周的年份、一年中的星期和工作日,它们应该是足以构建一个 LocalDate。
Exception in thread "main" java.time.format.DateTimeParseException: Text '201901' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1931)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1866)
at java.time.LocalDate.parse(LocalDate.java:411)
at Main.formatter1(Main.java:22)
at Main.main(Main.java:10)
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {WeekOfYear[WeekFields[MONDAY,4]]=1, WeekBasedYear[WeekFields[MONDAY,4]]=2019, DayOfWeek=7},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:379)
at java.time.LocalDate$$Lambda.000000001061ED20.queryFrom(Unknown Source)
at java.time.format.Parsed.query(Parsed.java:237)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1862)
... 3 more
您可以使用YearMonth
public static void main(String[] args) {
System.out.println(YearMonth.parse("201901", DateTimeFormatter.ofPattern("yyyyMM")));
}
输出
2019-01
据我所知你不能那样做。据我所知,您的代码有两个问题。首先是 LocalDate
没有足够的信息,因为您的代码不知道它应该解析到哪个工作日。
二是略显奇怪的字段的使用。如果你只是使用 Chrono 字段,你应该没问题。您也可以调整您的模式以包含 space 或破折号,因为解析似乎不喜欢它在一个数字中 - 虽然不知道为什么
例如您可以这样做:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
.appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2, 2, SignStyle.NEVER)
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.SUNDAY.getValue())
.toFormatter();
LocalDate d = LocalDate.parse("201933", formatter);
但是,使用周数似乎不是一个好的选择。如果你能按照@butiri-dan 的建议度过几个月,你很可能会过得更好。
编辑:根据@carlos-heuberger 的建议,它现在在 parseDefaulting
-setep
ChronoField.DAY_OF_WEEK
嗯,2019 年的第一周并没有给你一个特定的日期。您需要添加一周中的特定日期。我运行这个:
System.out.println(LocalDate.parse("2019-01-01", DateTimeFormatter.ofPattern("YYYY-ww-ee")));
得到"2018-12-30"
(那是因为2019年1月1日是星期二,也就是一周的第三天,所以2019年第一周的第一天实际上是"2018-12-30"
)。请注意,由于某种原因没有破折号 "-"
它不起作用。要阅读有关格式化程序选项的信息,请访问此处:DateTimeFormatter
这适用于 Java 8 及更高版本:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear(), 4)
.appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2)
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
System.out.println(LocalDate.parse("201901", formatter));
输出为:
2019-01-06
我已经在 Oracle Java 1.8.0_101 上进行了测试。我还没有安装任何 IBM Java。
让它发挥作用的关键是使用 weekOfWeekBasedYear()
而不是 weekOfYear()
。我的其他变化是装饰性的。 WeekFields.ISO.weekOfYear()
通过计算一年中的星期一来定义周数,这不是您想要的,因为根据 ISO,第 1 周可能从上一年年底(12 月 29 日到 31 日)附近的星期一开始。因此,它也不能很好地与 基于周的年份 一起使用,并且 Java 拒绝将两者一起使用来标识一周。
在 Java 9 上,您的 formatter2()
和 formatter3()
也可以工作(在 Oracle Java 9.0.4 上测试)。 Java 8 中似乎存在一个错误,其中相邻字段解析对 YYYYww
不起作用。不过,我搜索了 Oracle Java 错误数据库,但没有找到错误描述。
ISO 8601
ISO 8601 是规定您正在使用的星期定义的标准,它还定义了星期的格式。它就像 2012-W48
。因此,除非您有充分且非常具体的理由不这样做,否则这是您应该使用的格式,尤其是在与任何外部系统交换数据时。它也恰好消除了 Java 8.
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYY-'W'ww")
.parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
.toFormatter();
System.out.println(LocalDate.parse("2019-W01", formatter));
2019-01-06