import java.util.Calendar;
public class WeekYear {
static String input = "202001";
//static String format = "YYYYMM";
public static void main(String[] args) throws ParseException {
Calendar lCal = Calendar.getInstance();
System.out.println(lCal.isLenient());
lCal.setLenient(false);
lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//lCal.setMinimalDaysInFirstWeek(5);
System.out.println(lCal.isLenient());
System.out.println(lCal.getTime());
//lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
//lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//System.out.println(lCal.getTime());
}
}
当此代码于 2020 年 11 月 22 日执行时,我从 Calendar.getTime() 收到 IllegalArgumentException。但在 2020 年 11 月 27 日执行时,它运行良好。
文档说:
The
setLenient(boolean leniency)
method inCalendar
class is used to specify whether the interpretation of the date and time is to be lenient or not. Parameters: The method takes one parameterleniency
of theboolean
type that refers to the mode of the calendar.
有什么解释吗?即使在我的本地,我现在也无法重现该问题。本地时间设置为 CST
异常堆栈:
Exception in thread "main" java.lang.IllegalArgumentException: year: 2020 -> 2019
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.util.Calendar.getTime(Calendar.java:1755)
at WildDog.main(WildDog.java:13)
`````````
最佳答案
tl;博士
永远不要使用 Calendar
,现在是遗留的,已被 ZonedDateTime
等 java.time 类取代。
使用专门构建的类,YearWeek
来自ThreeTen-Extra项目,跟踪标准ISO 8601 weeks .
自定义格式化程序
定义一个 DateTimeFormatter
对象来匹配您的非标准输入字符串。
org.threeten.extra.YearWeek
.parse(
"202001" ,
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
)
.toString()
2020-W01
标准格式化程序
或者操作您的输入字符串以符合 ISO 8601 standard format ,在基于周的年份和周之间插入 -W
。 java.time 类和 ThreeTen-Extra 类在解析/生成字符串时默认都使用 ISO 8601 格式。
String input = "202001";
String inputModified = input.substring( 0 , 4 ) + "-W" + input.substring( 4 );
YearWeek yearWeek = YearWeek.parse( inputModified ) ;
yearWeek.toString(): 2020-W01
避免遗留日期时间类
不要浪费时间去理解日历
。这个可怕的类在几年前就被 JSR 310 中定义的现代 java.time 类所取代。
周的定义
您必须指定一周的定义。您的意思是第 1 周包含该年的第一天吗?或者第 1 周包含一周中的某一天?或者第 1 周是第一个完全由新年日期组成的日历周?或者也许是行业特定的周定义?还有其他定义吗?
关于Calendar
的一个令人困惑的事情是它对一周的定义会随着Locale
的变化而变化。这是避免使用遗留类的众多原因之一。
基于周的年份
根据您对周的定义,一周中的年份可能不是该周某些日期的日历年。基于周的年份可能与日历年重叠。
标准周和基于周的年份
例如,standard ISO 8601 week将一周定义为:
- 从周一开始,并且
- 第 1 周包含该日历年的第一个星期四。
因此,基于周的一年有 52 或 53 整周。当然,这意味着上一个和/或下一个日历年的某些日期可能会出现在基于周的年份的第一周/最后几周。
org.thirden.extra.YearWeek
一个问题是,您试图用一个表示时刻的类来表示一年中的一周,该类表示时区上下文中的日期和时间。
相反,请使用专门构建的类。您可以在 ThreeTen-Extra 中找到一个图书馆,YearWeek
。该库扩展了 Java 8 及更高版本中内置的 java.time 类的功能。
有了这个类,我认为我们可以定义一个 DateTimeFormatter
来使用格式模式 YYYYww
解析您的输入,其中 YYYY
表示基于周的年份的 4 位数年份,ww
表示两位数的周数。像这样:
// FAIL
String input = "202001" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "YYYYww" ) ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
但是使用该格式化程序会抛出 DateTimeParseException
出于我无法理解的原因。
Exception in thread "main" java.time.format.DateTimeParseException: Text '202001' could not be parsed: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.DateTimeException: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: WeekBasedYear
或者,我们可以使用 DateTimeFormatterBuilder
从各个部分构建 DateTimeFormatter
。通过仔细阅读the OpenJDK source code对于 Java 13 DateTimeFormatter.ISO_WEEK_DATE
我能够拼凑出这个似乎有效的格式化程序。
DateTimeFormatter f =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
;
使用:
String input = "202001" ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
ISO 8601
向您的数据发布者介绍 ISO 8601用于以文本方式表示日期时间值的标准定义格式。
要生成表示 YearWeek
值的标准格式字符串,请调用 toString
。
String output = yearWeek.toString() ;
2020-W01
并解析标准字符串。
YearWeek yearWeek = YearWeek.parse( "2020-W01" ) ;
关于java - Calendar.getTime IllegalArgumentException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59119291/