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:

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:

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:

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]

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.

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).

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.

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". 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. You need to be aware that for a given date, the week-year and year may be different.


    tl;dr

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

    Details

    The Answer by Jon Skeet is correct. 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. And java.time officially supplants Joda-Time, as that project is now in maintenance mode.

    As Skeet points out, there are different ways to define week-of-year.

    The java.time classes provide support for the standard ISO 8601 definition of week-of-year. This definition is that week number 1 is the first week with a Thursday, and that week starts on a Monday. 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.

    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.

    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.

    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 .

    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.

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

    Tip: When Android becomes more capable, use the YearWeek class from the ThreeTen-Extra project.


    About java.time

    The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .

    The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

    To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

    Where to obtain the java.time classes?

  • Java SE 8 , Java SE 9 , and later
  • Built-in.
  • Part of the standard Java API with a bundled implementation.
  • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
  • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport .
  • Android
  • The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
  • See How to use ThreeTenABP….
  • 链接地址: http://www.djcxy.com/p/3094.html

    上一篇: 公历日历java错误

    下一篇: 日历获得年份返回错误的结果