java - Calendar.getTime IllegalArgumentException

标签 java illegalargumentexception week-number java.util.calendar java-calendar

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 in Calendar class is used to specify whether the interpretation of the date and time is to be lenient or not. Parameters: The method takes one parameter leniency of the boolean 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,现在是遗留的,已被 ZonedDateTimejava.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 ,在基于周的年份和周之间插入 -Wjava.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/

相关文章:

Android - 在 2.1 和低版本 android 上创建对话框时出现 java.lang.IllegalArgumentException 错误

java - 用 Joda-Time 解析周日期?

c++ - strftime - 将周数 ISO8601 转换为公历标准 C++

mysql - 一年中每周的开始和结束日期

java - String.contains 找到一个子字符串但没有找到 Pattern.matches?

Java:捕获异常 - 未检查与检查

java - Maven插件执行失败

java - CardLayout IllegalArgumentException 与 next()

java - Class.getResource 中奇怪的区分大小写错误?

java - 如果输入非int值如何退出While循环?