I have the following code in a JUnit test, which seemed to work last week is failing this week:
Calendar cal = Calendar.getInstance();
cal.set(2011, Calendar.JULY, 12);
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15
System.out.println(cal.get(Calendar.DATE));
As you could probably infer from my comment, since the 12th is a Tuesday, I expect Date to be 15 after setting the DAY_OF_WEEK to Friday. However, the value that is printed is 22, and causes the test to fail.
If I, however change the code as follows, and add an additional call to get:
Calendar cal = Calendar.getInstance();
cal.set(2011, Calendar.JULY, 12);
System.out.println(cal.get(Calendar.DATE));
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15
System.out.println(cal.get(Calendar.DATE));
I get the output that I expect, 12 and 15.
Can someone explain what is going on, and why this test was working last week?
The first thing to understand is that Month + Day + DayOfWeek does not mean anything to the Calendar. The Calendar will calculate the true value of the date based on
YEAR + MONTH + DATE
or
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
(Or some other combos like year + day of year etc.) So Date + DayOfWeek doesn't inherently mean much to it.
The second thing to understand is when you set on a Java Calendar it doesn't actually recompute the absolute time or update related fields until an operation that forces computation occurs.
After your first set, the calendar is in a conflicted state. The month and day say that it's July 12th, but the 'week of month' and 'day of week' still say that it's today, whatever today is. You then call set day of week to friday. So now year month and day say July 12th, but the 'week of month' and 'day of week' fields say it's Friday of 'this' week.
The rules of the calendar say that the most recently set field "wins" when there's a conflict, so the week of month and day of week combining to say Friday of this week are what's used to calculate the other fields.
Inserting a get in the middle 'fixes' it because it forces the entire internal state of the calendar to get recomputed to Tuesday July 12th before setting to Friday, so there are no internal conflicts. The 'week of month' got set to the week that contains July 12th by the recalculation prior to you setting day of week to Friday.
Edit: Sorry to make changes after two days, noticed this open in an old browser tab and thought I would expand for the hopeful help of future googlers:
The reason it worked for Jon in the comments is he lives in London. His computer thinks weeks start on Mondays. So when asked for Friday of 'this' week, it still responded July 15th when asked on Sunday July 17th. I bring this up because differing first days of the week in different Locales are just yet another way that trying to use the WEEK_OF fields in a calendar goes haywire.
There is Bug 4655637 (looks similar to your issue). I checked that code under latest JDK6 (Windows) and I have 15 in both cases. BTW: I am suggest to always use GregorianCalendar class explicitly unless you want something else (depending on your locale).
EDIT: official docs:
The following are the default combinations of the calendar fields. The
most recent combination, as determined by the most recently set single
field, will be used.
For the date fields:
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
For the time of day fields:
HOUR_OF_DAY
AM_PM + HOUR
In addition to #Affe's clear answer, the following combinations seem to work (as of #GrzegorzSzpetkowski's bug report link)
Calendar expects the following combinations of the fields to determine
a date.
MONTH + DAY_OF_MONTH
MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
DAY_OF_YEAR
DAY_OF_WEEK + WEEK_OF_YEAR
When you set DAY_OF_WEEK, the calendar expects a week field
(WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH or WEEK_OF_YEAR) has also been
set. So, avoid setting DAY_OF_WEEK without setting one of the week
fields.
Related
I'm trying to get the sunday of the same week as a given date.
During this I ran into this problem:
Calendar calendar = Calendar.getInstance(Locale.GERMANY);
calendar.set(2017, 11, 11);
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(calendar.getTime().toString());
results in "Sun Jan 07 11:18:42 CET 2018"
but
Calendar calendar2 = Calendar.getInstance(Locale.GERMANY);
calendar2.set(2017, 11, 11);
calendar2.getTime();
calendar2.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(calendar2.getTime().toString());
gives me the correct Date "Sun Dec 17 11:18:42 CET 2017"
Can someone explain why the first exmple is behaving this way? Is this really intended?
Thanks
Basically, the Calendar API is horrible, and should be avoided. It's not documented terribly clearly, but I think I see where it's going, and it's behaving as intended in this situation. By that I mean it's following the intention of the API authors, not the intention of you or anyone reading your code...
From the documentation:
The calendar field values can be set by calling the set methods. Any field values set in a Calendar will not be interpreted until it needs to calculate its time value (milliseconds from the Epoch) or values of the calendar fields. Calling the get, getTimeInMillis, getTime, add and roll involves such calculation.
And then:
When computing a date and time from the calendar fields, there may be insufficient information for the computation (such as only year and month with no day of month), or there may be inconsistent information (such as Tuesday, July 15, 1996 (Gregorian) -- July 15, 1996 is actually a Monday). Calendar will resolve calendar field values to determine the date and time in the following way.
If there is any conflict in calendar field values, Calendar gives priorities to calendar fields that have been set more recently. The following are the default combinations of the calendar fields. The most recent combination, as determined by the most recently set single field, will be used.
For the date fields:
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
In the first example, the fact that the last field set was "day of week" means it will then use the YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK calculation (I think). The year and month have been set to December 2017, but the week-of-month is the current week-of-month, which is the week 5 of January 2018... so when you then say to set the day of week to Sunday, it's finding the Sunday in the "week 5" of December 2017. December only had 4 weeks, so it's effectively rolling it forward... I think. It's all messy and you shouldn't have to think about that, basically.
In the second example, calling getTime() "locks in" the year/month/day you've specified, and computes the other fields. When you set the day of week, that's then adjusting it within the existing computed fields.
Basically, avoid this API as far as you possibly can. Use java.time, which is a far cleaner date/time API.
As Jon Skeet said, avoid Calendar. For your case it is truly horrible, and it’s poorly designed in general. Instead do
WeekFields weekFieldsForLocale = WeekFields.of(Locale.GERMANY);
// To find out which number Sunday has in the locale,
// grab any Sunday and get its weekFieldsForLocale.dayOfWeek()
int dayNumberOfSundayInLocale = LocalDate.now()
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY))
.get(weekFieldsForLocale.dayOfWeek());
LocalDate date = LocalDate.of(2017, Month.DECEMBER, 11);
LocalDate sunday
= date.with(weekFieldsForLocale.dayOfWeek(), dayNumberOfSundayInLocale);
System.out.println(sunday);
This prints the expected date
2017-12-17
As others have already mentioned, the solution is to use java.time, the modern Java date and time API. Also generally it is so much nicer to work with. One nice feature is the LocalDate class that I am using. It is a date without time of day, which seems to match your requirements more precisely that Calendar did.
If the above looks complicated, it’s because, as I think you are aware, “Sunday of the same week” means different things in different locales. In the international standard that Germany follows, weeks begin on Monday, so Sunday is the last day of the week. In the American standard, for example, Sunday os the first day of the week. WeekFields.dayOfWeek() numbers the days of the week from 1 to 7, so when we want to set the day to Sunday, we first need to find out which number Sunday has got in this numbering (7 in Germany, 1 in the US). So for any Sunday, get its weekFieldsForLocale.dayOfWeek() value and later use this for setting the day of week to Sunday. The reason why this is necessary is that the with() method is so general and therefore has been designed to accept only numeric values; we can’t just pass it a DayOfWeek object.
If I substitute Locale.US into the code, I get 2017-12-10, which is the correct Sunday for a calendar where Sunday is the first day of the week. If you are sure your only want your code to work for Germany, you may of course just hardcode a 7 (please make it a constant with a very explanatory name).
Link: Oracle Tutorial Date Time explaining how to use java.time. There are other resources on the net (just avoid the outdated placed that suggest java.util.Calendar :-)
Can anyone explain why do I get those values while trying to parse a date?
I've tried three different inputs, as follows:
1) Third week of 2013
Date date = new SimpleDateFormat("ww.yyyy").parse("02.2013");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR) + "." + cal.get(Calendar.YEAR));
Which outputs: 02.2013 (as I expected)
2) First week of 2013
Date date = new SimpleDateFormat("ww.yyyy").parse("00.2013");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR) + "." + cal.get(Calendar.YEAR));
Which outputs: 52.2012 (which is fine for me, since the first week of 2013 is also the last one of 2012)
3) Second week of 2013
Date date = new SimpleDateFormat("ww.yyyy").parse("01.2013");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR) + "." + cal.get(Calendar.YEAR));
Which outputs: 1.2012 (which makes absolutely no sense to me)
Does anyone know why this happens?? I need to parse a date in the format (week of year).(year). Am I using the wrong pattern?
You're using ww, which is "week of week-year", but then yyyy which is "calendar year" rather than "week year". Setting the week-of-week-year and then setting the calendar year is a recipe for problems, because they're just separate numbering systems, effectively.
You should be using YYYY in your format string to specify the week-year... although unfortunately it looks like you can't then get the value in a sane way. (I'd expect a Calendar.WEEKYEAR constant, but there is no such thing.)
Also, week-of-year values start at 1, not 0... and no week is in two week-years; it's either the first week of 2013 or it's the last week of 2012... it's not both.
I would personally avoid using week-years and weeks if you possibly can - they can be very confusing, particularly when a date in one calendar year is in a different week year.
Use Calendar.getWeekYear() to get year value synced with Calendar.WEEK_OF_YEAR field.
There is more information about Week of Year and Week Year at the GregorianCalendar doc.
In my application there lies a code which works abruptly sometimes, its about getting a week interval using the java calendar object through Calendar.DAY_OF_WEEK.
The code checked for monday as start of week and sunday as end of week like:
fromCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
toCal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
the toCal is set as the last sunday instead of coming sunday.
Is there any alternate way to do this other then this kind of hard coding.
Appriciate the help in advance.
Thanks,
Vaibhav
I guess you have to set the start of week to monday, otherwise last sunday IS the sunday of the week.
setFirstDayOfWeek
public void setFirstDayOfWeek(int value)
Sets what the first day of the week is; e.g., Sunday in US, Monday in France.
**Parameters:**
value - the given first day of the week.
Java Calender Doc
The issue is in locale. In English(US), Sunday is the first day of the week.
Check this code:
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println("FirstDayOfWeek="+cal.getFirstDayOfWeek());
System.out.println(cal.getTime().toString());
cal = Calendar.getInstance(Locale.FRANCE);
System.out.println("FirstDayOfWeek="+cal.getFirstDayOfWeek());
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(cal.getTime().toString());
Be very clear on your desired behaviour here. You start with a calendar object whose "now" is some day of the week, perhaps "today". You the call set(DAY_OF_WEEK, ...). What effect do you desire if the Calendar's today is Tuesday? Sunday? Monday?
As observed in other answers, what happens depends upon the Calendar's opinion about what the First day of week is. So first set that to your chosen value. You will then (according to this answer get a Sunday and a Monday in the current week, which may not be what you want - what exactly do you need if today is Sunday? - some systems might actually be "thinking" about the next week.
Personally I might get my Monday according to some business rules and the get the Sunday after by adding 6 days.
I'm creating an instance of java.util.Calendar class:
Calendar rightNow = Calendar.getInstance();
getInstance() gets a Calendar using the default time zone and locale... Its 20:22 1.11.2012 in Poland right now, but when I print rightNow field out, the result is wrong:
System.out.print(rightNow.get(Calendar.MONTH));
result: 10.
System.out.print(rightNow.get(Calendar.DAY_OF_MONTH));
result: 1.
System.out.print(rightNow.get(Calendar.DATE));
result: 1.
I supose that in December the YEAR field will also be wrong. Can somebody explain that to me?
Month starts from 0 when you use calendar not from 1
As per javadoc
Field number for get and set indicating the month. This is a calendar-specific value. The first month of the year in the Gregorian and Julian calendars is JANUARY which is 0; the last depends on the number of months in a year.
In my application there lies a code which works abruptly sometimes, its about getting a week interval using the java calendar object through Calendar.DAY_OF_WEEK.
The code checked for monday as start of week and sunday as end of week like:
fromCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
toCal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
the toCal is set as the last sunday instead of coming sunday.
Is there any alternate way to do this other then this kind of hard coding.
Appriciate the help in advance.
Thanks,
Vaibhav
I guess you have to set the start of week to monday, otherwise last sunday IS the sunday of the week.
setFirstDayOfWeek
public void setFirstDayOfWeek(int value)
Sets what the first day of the week is; e.g., Sunday in US, Monday in France.
**Parameters:**
value - the given first day of the week.
Java Calender Doc
The issue is in locale. In English(US), Sunday is the first day of the week.
Check this code:
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println("FirstDayOfWeek="+cal.getFirstDayOfWeek());
System.out.println(cal.getTime().toString());
cal = Calendar.getInstance(Locale.FRANCE);
System.out.println("FirstDayOfWeek="+cal.getFirstDayOfWeek());
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(cal.getTime().toString());
Be very clear on your desired behaviour here. You start with a calendar object whose "now" is some day of the week, perhaps "today". You the call set(DAY_OF_WEEK, ...). What effect do you desire if the Calendar's today is Tuesday? Sunday? Monday?
As observed in other answers, what happens depends upon the Calendar's opinion about what the First day of week is. So first set that to your chosen value. You will then (according to this answer get a Sunday and a Monday in the current week, which may not be what you want - what exactly do you need if today is Sunday? - some systems might actually be "thinking" about the next week.
Personally I might get my Monday according to some business rules and the get the Sunday after by adding 6 days.