Does the setMonth method of Java Calendar work wro

2019-06-21 03:28发布

问题:

I have small code such as below. I expected that the result should be 7, but it printed 6. If I uncomment the line tmp.get(Calendar.MONTH), it runs OK (prints 7).

Please let me know the reason. I'm using JDK 1.7.0_25 in MacOS.

public static void main(String[] args) {
    Calendar tmp = Calendar.getInstance();
    tmp.set(Calendar.DAY_OF_MONTH, 4);
    tmp.set(Calendar.MONTH, Calendar.AUGUST);
    //tmp.get(Calendar.MONTH);
    tmp.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    System.out.println(tmp.get(Calendar.MONTH));
}

Screenshot:
Comment code: http://gyazo.com/4c099b1b2b90d72d1954b98b134e4ac3
Uncomment code: http://gyazo.com/fe368745da168646140ca9f3a60d2021

回答1:

Because month index starts with index 0. add +1 while get month. it is c based structure copied into java. It has indexes 0 to 11.

And i think day of month is incorrect. comment that and run it it shows correctly.(tmp.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);)

    tmp.set(Calendar.DAY_OF_MONTH, 4);
    tmp.set(Calendar.MONTH, Calendar.AUGUST);
    //tmp.get(Calendar.MONTH);
    //tmp.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    System.out.println(tmp.get(Calendar.MONTH));

By default it takes year as 2015 and August 4 is tuesday not monday



回答2:

I tried to run your code and it prints 7 every time (even tried to uncomment that line you mention). Which seems correct to me.

EDIT The problem is explained in detail Java Calendar - Date is unpredictable after setting day_of_week

I am glad that with Java 8 came new API for working with date and time.

EDIT2

My understanding of what happened:

Only sensible combinations are

YEAR + MONTH + DATE (let's call it A)

or

YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK (let's call it B)

And the Calendar has its inner state of date in which he thinks he is. So basically before you set

DAY_OF_WEEK

you were working with combination A but after it you were working with combination B and this line

tmp.set(Calendar.DAY_OF_MONTH, 4);

was ignored. Thus it took Monday in the first week of August (2015-07-27). But when you called

tmp.get(Calendar.MONTH);

you "materialized" the date according to combination A (so to 2015-08-04) and the

DAY_OF_WEEK

was set "correctly" starting from 2015-08-04.

Anyway could you try that with JDK 8?



回答3:

When you set DAY_OF_WEEK = MONDAY, Calendar returns back to last Monday and it is 27th of Jul, you can check it with System.out.println(tmp.get(Calendar.DAY_OF_MONTH));



回答4:

When you set the day of week, Calendar has no way to know what you expect. If you expect to get the date of another day of the same week, you should set the week of year too to indicate which week you expect.



回答5:

Avoid pre-Java-8 Date and Calendar

The existing Java date and time classes are poor, mutable, and have unpredictable performance.

This applies somehow equally to Calendar, which has some of the same problems. Weaknesses of Calendar (german link) are

  • not really a calendar, but date + time
  • mutable
  • January is 0th month, not 1st
  • unintuitive API: you have to use get(Calendar.HOUR) instead of getHour
  • ...

if you are using Java <8, use Joda-Time

In the comments to java-calendar-date-is-unpredictable-after-setting-day-of-week, Joda-Time is recommended as an alternative by @JonSkeet, as it is by those folks of the JDK Enhancement Proposal. Your code could look like

import org.joda.time.MutableDateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class Test2 {
    public static void main(String[] args) {
    MutableDateTime tmp = new MutableDateTime();
    tmp.setDayOfMonth(4);
    tmp.setMonthOfYear(8);
    tmp.setDayOfWeek(1);

    DateTimeFormatter fmt = DateTimeFormat.fullDate();
    System.out.println(fmt.print(tmp));
    }
}

Immutable

There is also the immutable DateTime class, which you will want to use instead. (immutability adds several positive effects, in Java 8 it is the default)

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class Test {
    public static void main(String[] args) {
        DateTimeFormatter fmt = DateTimeFormat.fullDate();

        DateTime tmp = new DateTime().withDayOfMonth(4).withMonthOfYear(8);
        System.out.println(fmt.print(tmp));

        tmp = tmp.withDayOfWeek(1);
        System.out.println(fmt.print(tmp));
    }
}

If on Java-8,

the java.time API is based on Joda-Time. Use

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Test {
    public static void main(String[] args) {
        DateTimeFormatter fmt = DateTimeFormatter.RFC_1123_DATE_TIME;

        LocalDateTime tmp
            = LocalDateTime.now().withDayOfMonth(4).withMonthOfYear(8);
        System.out.println(fmt.format(tmp));

        tmp = tmp.withDayOfWeek(1);
        System.out.println(fmt.format(tmp));
    }
}

(I have not tested the Java-8 version)



标签: java calendar