我正在尝试将格式为“YYYYww”(例如 201901)的简单字符串解析为 LocalDate,但我的尝试都没有成功。
我尝试通过简单地使用模式“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 包含基于周的年份、一年中的星期和星期几,这应该足以构造一个本地日期。
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$7.000000001061ED20.queryFrom(Unknown Source)
at java.time.format.Parsed.query(Parsed.java:237)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1862)
... 3 more
这适用于 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 Bug 数据库,但没有找到 Bug 描述。
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
据我所知你不能这样做。据我所知,您的代码存在两个问题。首先是
LocalDate
没有足够的信息,因为您的代码无法知道它应该解析到哪个工作日。
第二个是稍微奇怪的字段的使用。如果你只使用 Chrono 字段,应该没问题。您也许还可以调整您的模式以包含空格或破折号,因为解析似乎不喜欢它出现在一个数字中 - 但不确定为什么
例如,您可以执行以下操作:
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 的建议,现在在
ChronoField.DAY_OF_WEEK
-setep中使用
parseDefaulting
您可以使用年月
public static void main(String[] args) {
System.out.println(YearMonth.parse("201901", DateTimeFormatter.ofPattern("yyyyMM")));
}
输出
2019-01
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
YearWeek
班级要处理一年中的几周,请使用类来表示该概念。
您忽略了定义“周”。也许您指的是标准 ISO 8601 周。第 1 周包含日历年的第一个星期四,基于周的年份恰好由 52 或 53 个完整的 7 天周组成,因此,第一周/最后几周可能与上一个/连续的日历年有几天不同。
ThreeTen-Extra库中找到一个代表标准 ISO 8601 周的类,
YearWeek
,这是一个免费提供的开源项目。
DateTimeFormatter
在 DateTimeFormatterBuilder
对象中定义格式模式以匹配您的输入。
final DateTimeFormatter PARSER =
new DateTimeFormatterBuilder ( )
.parseCaseInsensitive ( )
.appendValue ( IsoFields.WEEK_BASED_YEAR , 4 , 10 , SignStyle.EXCEEDS_PAD )
.appendValue ( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 2 )
.toFormatter ( );
解析您的输入,生成一个
YearWeek
对象。
String input = "201901";
YearWeek yearWeek = YearWeek.parse ( input , PARSER );
通过生成标准 ISO 8601 格式的文本进行验证。
String input = "201901";
YearWeek yearWeek = YearWeek.parse ( input , PARSER );
2019-W01
确定该周第一天的日期,即星期一。
YearWeek
对象可以返回该日期的 LocalDate
对象。通过 DayOfWeek
枚举指定您想要一周中的哪一天。
LocalDate localDate = yearWeek.atDay ( DayOfWeek.MONDAY );
2018-12-31