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
异常堆栈:
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)
发布于 2019-11-30 19:21:38
tl;dr
永远不要使用Calendar
,它现在是遗留的,被java.time类(如ZonedDateTime
)所取代。
使用一个专用的类,即来自YearWeek
项目的https://www.threeten.org/threeten-extra/类来跟踪标准ISO 8601周。
自定义格式化程序
定义一个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标准格式,在基于周的年份和周之间的中间插入一个-W
。在解析/生成字符串时,默认情况下,java.time类和ThreeTen额外类都使用ISO8601格式。
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
表示的是两位数的周数。如下所示:
// 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源代码,我就能够拼凑出这个看起来可以工作的格式化程序。
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" ) ;
发布于 2019-11-30 22:56:03
巴兹尔·伯克已经给出了一个很好的答案。这里有一个不需要外部依赖项(前提是您使用的是Java 8或更高版本)。
java.time
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
决定您是在问不可能的事情,并抛出异常。我得到的堆栈跟踪是:
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。
https://stackoverflow.com/questions/59119291
复制相似问题