简体   繁体   English

日历获取年份返回错误结果

[英]Calendar get year returns wrong result

I want to create a calendar object and set it to a certain year and a week in that year.我想创建一个日历对象并将其设置为某一年和该年的某个星期。

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.WEEK_OF_YEAR, weekOfYear);  // 1
calendar.set(Calendar.YEAR, year); // 2016
setWeekChecked(calendar);

This is the toString of the calendar object as I pass it to the setWeekChecked method:这是我将日历对象传递给 setWeekChecked 方法时的 toString:

java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=2,DAY_OF_MONTH=7,DAY_OF_YEAR=7,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000,DST_OFFSET=0] java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH= 2,DAY_OF_MONTH=7,DAY_OF_YEAR=7,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000,DST_OFFSET=0 ]

In the setWeekChecked method:在 setWeekChecked 方法中:

public void setWeekChecked(final Calendar cal) {
    final int targetWeek = cal.get(Calendar.WEEK_OF_YEAR); // Returns 1
    final int targetYear = cal.get(Calendar.YEAR); // Returns 2015??
}

This is the toString of the calendar object now:这是日历对象的 toString:

java.util.GregorianCalendar[time=1451557543219,areFieldsSet=true,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=5,DAY_OF_MONTH=31,DAY_OF_YEAR=365,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000,DST_OFFSET=0] java.util.GregorianCalendar[time=1451557543219,areFieldsSet=true,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH= 5,DAY_OF_MONTH=31,DAY_OF_YEAR=365,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000,0DST_OFFSET ]

What am I doing wrong?我究竟做错了什么?

I suspect that the calendar is trying to use the current day-of-week (it's Thursday today) in the first week of 2016.我怀疑日历试图在 2016 年的第一周使用当前的星期几(今天是星期四)。

Now, looking at your calendar settings, you've got firstDayOfWeek=1 (so weeks run Sunday to Saturday) and minimalDaysInFirstWeek=1 (so the first week of the year is the one that includes January 1st).现在,查看您的日历设置,您有 firstDayOfWeek=1(因此周从星期日到星期六)和 minimalDaysInFirstWeek=1(因此一年的第一周是包括 1 月 1 日的那一周)。

That means that the first week of 2016 in your calendar ran from Decemember 27th 2015 to January 2nd 2016. Therefore Thursday in the first week was December 31st - which is exactly what the calendar you've shown us says.这意味着您日历中 2016 年的第一周是从 2015 年 12 月 27 日到 2016 年 1 月 2 日。因此,第一周的星期四是 12 月 31 日 - 这正是您向我们展示的日历所说的。

Fundamentally, calendar arithmetic with "week of year" is tricky because:从根本上说,带有“一年中的一周”的日历算术很棘手,因为:

  • There are lots of different culture-specific ways of looking at them有许多不同的文化特定方式来看待它们
  • Typically requirements don't specify which of those you're actually interested in通常要求不会指定您真正感兴趣的是哪些

I'd strongly recommend using Joda Time if at all possible to make your date/time-handling code clearer to start with, but you'll still need to work out exactly what you mean by "set it to a certain year and a week in that year".我强烈建议您尽可能使用 Joda Time 来使您的日期/时间处理代码更清晰,但您仍然需要通过“将其设置为特定的一年和一周”来准确理解您的意思在那年”。 Note that Joda Time separates the concepts of "week-year" (used with week-of-week-year and day-of-week) from "year" (used with month and day-of-month) which helps greatly.请注意,Joda Time 将“week-year”(与 week-of-week-year 和 day-of-week 一起使用)的概念与“year”(与 month 和 day-of-month 一起使用)分开,这有很大帮助。 You need to be aware that for a given date, the week-year and year may be different.您需要注意,对于给定日期,周-年和年可能不同。

tl;dr tl;博士

LocalDate.of( 2016 , Month.JULY , 1 ) 
         .with( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 1 ) 

Details细节

The Answer by Jon Skeet is correct. Jon Skeet 的回答是正确的。 Update: We have a better way.更新:我们有更好的方法。

The java.time classes built into Java 8 and later supplant the troublesome old legacy date-time classes bundled with the earliest versions of Java. Java 8 和更高版本中内置的 java.time 类取代了与最早版本的 Java 捆绑在一起的麻烦的旧旧日期时间类。 And java.time officially supplants Joda-Time, as that project is now in maintenance mode. java.time 正式取代了 Joda-Time,因为该项目现在处于维护模式。

As Skeet points out, there are different ways to define week-of-year.正如 Skeet 指出的那样,有不同的方法来定义一年中的一周。

The java.time classes provide support for the standard ISO 8601 definition of week-of-year . java.time 类为week-of-year的标准ISO 8601定义提供支持。 This definition is that week number 1 is the first week with a Thursday, and that week starts on a Monday.此定义是第 1 周是星期四的第一周,该周从星期一开始。 So the beginning of the week may include one or more days of the previous year, and the last week may include one or more days from the following year.因此,一周的开始可能包括前一年的一天或多天,最后一周可能包括下一年的一天或多天。 The year always has either 52 or 53 weeks.一年总是有 52 或 53 周。

See my Answer to a similar Question for more details.有关详细信息,请参阅我对类似问题的回答

The LocalDate class represents a date-only value without time-of-day and without time zone. LocalDate类表示没有时间和时区的纯日期值。

Get a date near the middle of the desired year.找一个接近所需年份年中的日期。 That desired year is a week-based year rather than a calendar year, so must avoid the very beginning or ending of the calendar year.所需的年份是基于周的年份而不是日历年,因此必须避免日历年的开始或结束。 In your case, you wanted week-based year of 2016.在你的例子中,你想要 2016 年的基于周的年份。

LocalDate ld = LocalDate.of( 2016 , Month.JULY , 1 ) ;

Next we adjust that date into the desired week by using IsoFields.WEEK_OF_WEEK_BASED_YEAR .接下来,我们使用IsoFields.WEEK_OF_WEEK_BASED_YEAR将该日期调整为所需的周。

LocalDate dayIn2016W01 = ld.with( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 1 ) ;

If you want the first day of that week, use another TemporalAdjuster from the TemporalAdjusters class.如果您想要该周的第一天,请使用TemporalAdjusters类中的另一个TemporalAdjuster

LocalDate firstDayOf2016W01 = dayIn2016W01.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );

Tip: When Android becomes more capable, use the YearWeek class from the ThreeTen-Extra project.提示:当 Android 变得更强大时,使用ThreeTen-Extra项目中的YearWeek类。


About java.time关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于 Java 8 及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .这些类取代了麻烦的旧的遗留日期时间类,例如java.util.DateCalendarSimpleDateFormat

To learn more, see the Oracle Tutorial .要了解更多信息,请参阅Oracle 教程 And search Stack Overflow for many examples and explanations.并在 Stack Overflow 中搜索许多示例和解释。 Specification is JSR 310 .规范是JSR 310

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.现在处于维护模式Joda-Time项目建议迁移到java.time类。

You may exchange java.time objects directly with your database.您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later.使用符合JDBC 4.2或更高版本的JDBC 驱动程序 No need for strings, no need for java.sql.* classes.不需要字符串,不需要java.sql.*类。 Hibernate 5 & JPA 2.2 support java.time . Hibernate 5 & JPA 2.2 支持java.time

Where to obtain the java.time classes?在哪里获取 java.time 类?

哪个 java.time 库与哪个版本的 Java 或 Android 一起使用的表格

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM