简体   繁体   中英

Java calendar.month returns sunday's month

I have an SQL database which has a year and week field, and I need to get the month data. When I try to retrieve it, it gives me the month after when the week is at the end of the month. For example from 2019.01.28-2019.02.01 week I get the month number 2.

What is the problem?

(plus info: I tried with other day_of_week too)

Here's the code:

rs = stmt.executeQuery("SELECT * FROM " + sqlDatabase.beosztas() + " WHERE id = \'" + id + "\'");`
        ResultSetMetaData md = rs.getMetaData();
        while (rs.next()) {
            year = rs.getInt(2);
            week = rs.getInt(3);
            cal.set(Calendar.YEAR, year);
            cal.set(Calendar.WEEK_OF_YEAR, week);
            cal.set(Calendar.DAY_OF_WEEK, 1);
            if (count == 0) {
                month = cal.get(Calendar.MONTH) + 1;
            }
            count++;
        }

Calendar.DAY_OF_WEEK starts with sunday, not monday. So in your case, its normal that it returns sunday's month. If you want to put the date to monday, simply use:

cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

You should be careful when using Calendar. Calendar.MONTH is 0-based index and Calendar.DAY_OF_WEEK is 1-based index.

So you should use the constants defined in Calendar like Calendar.MONDAY or Calendar.JUNE to be sure that you use the correct month or day without mistakes.

To a friendly use of Calendar, constants is better. Wanting to define a date of 2018-02-18:

Calendar cal = Calendar.getInstance();
cal.set(2018, 2, 18); // wrong. here the date will be 2018-03-18
cal.set(2018, Calendar.FEBRUARY, 18); // correct

DAY_OF_WEEK 1 does not mean the first day of the week.

As javadoc for DAY_OF_WEEK says:

Field number for get and set indicating the day of the week. This field takes values SUNDAY , MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , and SATURDAY .

The SUNDAY constant has value 1, so DAY_OF_WEEK 1 means Sunday.

To specify the first day of the week, you need to call getFirstDayOfWeek() :

Gets what the first day of the week is; eg, SUNDAY in the US, MONDAY in France.

So:

int year = 2019;
int week = 5;

Calendar cal = Calendar.getInstance(Locale.US);
cal.clear();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.WEEK_OF_YEAR, week);
cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
System.out.println(cal.getTime());

cal = Calendar.getInstance(Locale.FRANCE);
cal.clear();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.WEEK_OF_YEAR, week);
cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
System.out.println(cal.getTime());

Output

Sun Jan 27 00:00:00 EST 2019
Mon Jan 28 00:00:00 EST 2019

In both cases you get January.

Output for week 14

Sun Mar 31 00:00:00 EDT 2019
Mon Apr 01 00:00:00 EDT 2019

Here locale matters for which month you get.

网格日历显示 2019 年 1 月至 2 月的标准 ISO 8601 周

tl;dr

Week # 5 of 2019 does indeed include days from both January and February, depending on your definition of week.

YearWeek.parse( "2019-W05" ).atDay( DayOfWeek.MONDAY ).toString()

2019-01-28

…and…

YearWeek.parse( "2019-W05" ).atDay( DayOfWeek.SUNDAY ).toString()

2019-02-03

You neglected to explain your definition of week. The above uses standard ISO 8601 week . Weeks cross over months, and even calendar-years.

Week?

What is your definition of a week? Does week # 1 start on January 1st? Or does the week start on a certain day-of-week before January 1st? Or is week # 1 the first week consisting only of days from the new year? Or, per the ISO 8601 standard's week , does a week start on Monday, with week # 1 containing the first Thursday of the new calendar year, and a year consists of 52 or 53 complete weeks with the possibility that some of the last or first few days of a calendar year end up in the next/previous week-based year?

I will assume the ISO 8601 week. See this list for 2019 .

2018-W52    December 24, 2018   December 30, 2018
2019-W01    December 31, 2018   January 6, 2019
2019-W02    January 7, 2019     January 13, 2019
2019-W03    January 14, 2019    January 20, 2019
2019-W04    January 21, 2019    January 27, 2019
2019-W05    January 28, 2019    February 3, 2019
2019-W06    February 4, 2019    February 10, 2019

Avoid legacy date-time classes

The Calendar class you are using is terrible, designed by people who did not understand the complexity and subtleties of date-time handling. The old date-time classes bundled with Java are new legacy, supplanted by java.time defined in JSR 310.

Among the many problems of Calendar is that the definition of a week varies by locale. Your code ignores the crucial issue of locale. So the JVM's current default locale is being applied implicitly. This means your results may vary at runtime. One of many reasons to never use this class.

java.time

The modern solution involves:

  • the java.time classes built into Java 8 and later
  • the YearWeek class from the ThreeTen-Extra library
  • the ISO 8601 standard for representing date-time values as text.

an SQL database which has a year and week field

I suggest instead you use a single text field. Use the standard ISO 8601 format for both the year and week together: yyyy-Www such as 2010-W01. Notice how this formats results in alphabetical sorting also being chronological.

Parse such values using the YearWeek class.

YearWeek yearWeek = YearWeek.parse( "2019-W05" ) ;

If you insist on keeping the week-based-year number separate from the week number, use this factory method to get a YearWeek object.

YearWeek yearWeek = YearWeek.of( 2019 , 5 ) ;  // Pass number of the week-based-year and of the week. 

Generate an ISO 8601 string.

String output = yearWeek.toString() ;

Get first day of that week, always a Monday.

LocalDate start = yearWeek.atDay( DayOfWeek.MONDAY ) ;

And the last day, always a Sunday.

LocalDate stop = yearWeek.atDay( DayOfWeek.SUNDAY ) ;

Generate text in standard ISO 8601 format.

String report = "Week " + yearWeek + " = " + start + "/" + stop ;

Week 2019-W05 = 2019-01-28/2019-02-03

So you can see that week # 5 of 2019 does indeed run from January 28 (month 1) to February 3 (month 2).


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 .

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

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

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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