首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Calendar.getTime IllegalArgumentException

Calendar.getTime IllegalArgumentException
EN

Stack Overflow用户
提问于 2019-11-30 19:01:45
回答 2查看 866关注 0票数 0
代码语言:javascript
运行
复制
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()得到一个Calendar.getTime()。但当它在2020年11月27日执行时,效果很好。

文件上说:

setLenient(boolean leniency)类中的Calendar方法用于指定对日期和时间的解释是否宽松。参数:该方法接受一个boolean类型的参数boolean,它引用日历的模式。

有什么解释吗?即使是在我的本地人,我也无法复制这个问题。当地时间设置为CST

异常堆栈:

代码语言:javascript
运行
复制
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)
代码语言:javascript
运行
复制
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-11-30 19:21:38

tl;dr

永远不要使用Calendar,它现在是遗留的,被java.time类(如ZonedDateTime )所取代。

使用一个专用的类,即来自YearWeek项目的https://www.threeten.org/threeten-extra/类来跟踪标准ISO 8601周

自定义格式化程序

定义一个DateTimeFormatter对象以匹配非标准输入字符串。

代码语言:javascript
运行
复制
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标准格式,在基于周的年份和周之间的中间插入一个-W。在解析/生成字符串时,默认情况下,java.time类和ThreeTen额外类都使用ISO8601格式。

代码语言:javascript
运行
复制
String input = "202001";
String inputModified = input.substring( 0 , 4 ) + "-W" + input.substring( 4 );
YearWeek yearWeek = YearWeek.parse( inputModified ) ;

yearWeek.toString():2020年-W01

避免遗留日期-时间类

不要浪费时间去理解Calendar。这个可怕的类几年前被定义在JSR 310中的现代java.time类所取代。

周的定义

您必须指定一周的定义。你是说第一周是一年的第一天吗?还是第一周包含了一周中的某一天?或者第一周是第一个日历周,完全由新年的日期组成?或者是一种特定于行业的周定义?其他定义?

关于Calendar的一个令人困惑的事情是,它对一周的定义被Locale改变了。这是避免使用遗留类的众多原因之一。

周基年

根据您对周的定义,一周的年份可能不是该周某些日期的日历年。以周为基础的年份可能与日历年重叠。

标准周和以周为基础的年份

例如,标准ISO 8601周将一周定义为:

  • 从星期一开始,
  • 第一周包含日历年的第一个星期四。

因此,一年中每周有52或53周。当然,这意味着某些日期可能出现在基于周的年份的第一/最后几周。

org.threeten.extra.YearWeek

一个问题是,您试图用一个表示时间的类来表示一年的一周,在时区的上下文中用时间来表示日期。

相反,使用一个目的构建的类。您可以在https://www.threeten.org/threeten-extra/YearWeek中找到一个。这个库扩展了内置在Java8及更高版本中的java.time类的功能。

有了这个类,我想我们可以定义一个DateTimeFormatter来使用格式化模式YYYYww来解析您的输入,其中YYYY表示基于周的4位数的年份,而ww表示的是两位数的周数。如下所示:

代码语言:javascript
运行
复制
// FAIL
String input = "202001" ; 
DateTimeFormatter f = DateTimeFormatter.ofPattern( "YYYYww" ) ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;

但是使用该格式化程序会抛出一个DateTimeParseException,原因是我无法理解。

线程“java.time.format.DateTimeParseException”中的异常:文本'202001‘无法解析:无法从TemporalAccessor获得YearWeek:{WeekOfWeekBasedYear[WeekFieldsSUNDAY,1]=1,WeekBasedYear[WeekFieldsSUNDAY,1]=2020},java.time.format.Parsed类型 … 由: java.time.DateTimeException:无法从TemporalAccessor获得YearWeek:{WeekOfWeekBasedYear[WeekFieldsSUNDAY,1]=1,WeekBasedYear[WeekFieldsSUNDAY,1]=2020},java.time.format.Parsed类型 … 由: java.time.temporal.UnsupportedTemporalTypeException:不支持的字段: WeekBasedYear引起

或者,我们可以使用DateTimeFormatterBuilder从部件构建DateTimeFormatter。通过仔细阅读源代码 forJava13forJava13forJava13forJava13forJava13forJava13forJava13forJava13for源代码,我就能够拼凑出这个看起来可以工作的格式化程序。

代码语言:javascript
运行
复制
DateTimeFormatter f =  
        new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
        .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
        .toFormatter()
;

利用这一点:

代码语言:javascript
运行
复制
String input = "202001" ; 
YearWeek yearWeek = YearWeek.parse( input , f ) ;

ISO 8601

向您的数据发布者介绍ISO 8601标准定义格式,以便以文本形式表示日期-时间值。

若要生成标准格式的字符串,表示YearWeek的值,请调用toString

代码语言:javascript
运行
复制
String output = yearWeek.toString() ;

2020年-W01

并解析一个标准字符串。

代码语言:javascript
运行
复制
YearWeek yearWeek = YearWeek.parse( "2020-W01" ) ;
票数 2
EN

Stack Overflow用户

发布于 2019-11-30 22:56:03

巴兹尔·伯克已经给出了一个很好的答案。这里有一个不需要外部依赖项(前提是您使用的是Java 8或更高版本)。

java.time

代码语言:javascript
运行
复制
    WeekFields wf = WeekFields.of(Locale.US);
    DateTimeFormatter yearWeekFormatter = new DateTimeFormatterBuilder()
            .appendValue(wf.weekBasedYear(), 4)
            .appendValue(wf.weekOfWeekBasedYear(), 2)
            .parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.SUNDAY.getValue())
            .toFormatter();

    String input = "202001";
    LocalDate date = LocalDate.parse(input, yearWeekFormatter);
    System.out.println(date);

产出如下:

2019-12-29

假设周日是一周的第一天,第一周是包含1月1日的一周,这是正确的:2020年的第一周从2019年12月29日星期日开始。如果希望以其他方式定义数周,只需使用不同的WeekFields对象即可。

我建议您不要使用Calendar类。这门课的设计总是很差,现在已经过时了。相反,我使用的是java.time,这是现代的java.time和time。

有什么解释吗?

感谢user85421的繁衍。您首先要创建一个Calendar对象(实际上是GregorianCalendar的一个实例),它代表当前的一天,在您的例子中是2020年11月22日,即星期日(显然已经提前了将近一年的时间设置了您的计算机时钟)。然后,您将其年份设置为2020 (不变),将其周数设置为1。然而,正如我们前面所看到的,这将将日期更改为2019年12月29日,从而与您设定为2020年的年份产生冲突。因此,GregorianCalendar决定您是在问不可能的事情,并抛出异常。我得到的堆栈跟踪是:

代码语言:javascript
运行
复制
java.lang.IllegalArgumentException: YEAR: 2020 -> 2019
    at java.base/java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2826)
    at java.base/java.util.Calendar.updateTime(Calendar.java:3395)
    at java.base/java.util.Calendar.getTimeInMillis(Calendar.java:1782)
    at java.base/java.util.Calendar.getTime(Calendar.java:1755)
    at ovv.misc.Test.main(Test.java:17)

在第二个例子中,您在2020年11月27日(星期五)运行您的程序。这一次,日期改为2020年1月3日(星期五),因此仍在2020年内,因此没有冲突,因此也没有例外。

该解释假定您的默认区域设置是将一年中的第一周定义为包含1月1日的一周。在将计算机的时间设置为2020年11月22日,以及将我的时区设置为美国/芝加哥之后,我已经在自己的地区运行了您的代码。也没有例外(产出包括Sun Jan 05 13:54:27 CST 2020)。我的地区遵循国际标准ISO。星期一是一周的第一天,第一周是至少有4天新年的第一周。所以2020年的第一周是从2019年12月30日星期一到1月5日星期日。我想在周一或周二我也可以在这个地方重现你的问题,我还没有试过。

PS如何解析整数

只是一个提示,要将字符串解析为int,只需使用Integer.parseInt(yourString)。不需要创建新的Integer对象。

链接

Oracle教程:日期时间解释了如何使用java.time。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59119291

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档