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
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
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?
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));
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.
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
0
th month, not 1
st
- 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)