Calendar Month Instance is off by one - java

Here is my code:
Month is passed in on a loop. First 0 (since it is January as I type), then 1, and so on. Next month when it is February, this loop will start from that date. 1, 2, etc.
int thisMonth = Calendar.getInstance().get(Calendar.MONTH) + 1;
cal = Calendar.getInstance();
df = new SimpleDateFormat("MMM yyyy", Locale.ENGLISH);
cal.set(Calendar.MONTH, month + thisMonth);
String sMonth = df.format(cal.getTime());
In the first loop, sMonth is March 2015.
In the second loop, sMonth is March 2015.
In the third loop, sMonth is April 2015.
In the fourth loop, sMonth is May 2015.
..and so on
As you see, the first month is NOT February as expected. I believe it is January 29th so that may have some cause as we are so close to February. But if that is the case, why would this happen twice?
I know since I am not working with unix timestamps things aren't exact, is there away to calculate this where I can at least get accurate month ordering?

Since today is the 30th, you're getting the current date (2015-01-30), and incrementing the month to get 2015-02-30 - which "rolls over" to 2015-03-02 since February doesn't have 30 days.
One way to avoid this problem is to set the day-of-month to 1 before changing the month.

Related

How can I find all week numbers of month that are the # week of month in a year with Java?

I am looking to build up a function that return an array with all week numbers of the previous months in a year that are the same week number of one particular month.
I am using as first day of week Monday and I am taking as first week of month week with the first Monday of current month.
Input: week of year and year. For example, 27 and 2019. The first week of July (7).
Output: array of week of months. For example, [2, 6, 10, 14, 19, 23, 27].
What I try:
private void getResult(int weekYear)
{
LocalDate date = LocalDate.now();
final int weekNumber = 27;
LocalDate newDate = date.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber);
int month = newDate.getMonthValue();;
int weekMonth = LocalDate.from(newDate).get(WeekFields.ISO.weekOfMonth());
System.out.println(newDate);
System.out.println(month);
System.out.println(weekMonth);
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= month; i++)
{
LocalDate tempDate = date.withYear(2019).withMonth(i).with(WeekFields.ISO.weekOfMonth(), weekMonth);
int tempYear = LocalDate.from(tempDate).get(WeekFields.ISO.weekOfWeekBasedYear());
list.add(tempYear);
}
list.forEach((e) -> System.out.print(e + " "));
}
int weekYear = 27;
getResult(weekYear);
What I get: [1 6 10 14 18 23 27].
What I am looking for: I have two question:
First one: the results obtained are different from those expected. I think the problem is due to the fact that I didn't specify how to calculate the first week of the month (first Monday of the month). Is it right? How can I solve that?
Second one: What is a better solution?
The key here is understanding a few points:
You are numbering weeks in two different ways. For the week of year you are using ISO numbering: the first week of the year is the one that includes at least 4 days of the new year. For week of month you are counting the Mondays (you may say that the first week of the month is the one that includes seven days of the month, not four).
The week number may not always exist. If your starting point is in 0th or the 5th week of the month, a preceding month may not have that week in it.
the results obtained are different from those expected. I think the
problem is due to the fact that I didn't specify how to calculate the
first week of the month (first Monday of the month). Is it right? How
can I solve that?
You are correct. To count the Mondays of the month you may use:
LocalDate tempDate = date.withYear(2019)
.withMonth(i)
.with(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
.with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekMonth);
(DayOfWeek.MONDAY.getValue() is just of wordy way of saying 1, of course, but conveys the intention better, so I prefer it.)
With this change to your code the output is the expected:
2 6 10 14 19 23 27
The key is ChronoField.ALIGNED_WEEK_OF_MONTH. The aligned weeks of a month start from the 1st of the month and are always 7 days regardless of the days of the week. The first aligned week is from the 1st through the 7th of the month, the 2nd aligned week if from 8th through 14th, etc. Since we have set the day of week to Monday, setting the aligned week to 1 gives us the 1st Monday of the month, etc.
We’re not done yet. If I set weekNumber to 40, I get:
2 6 10 14 14 23 27 27 36 41
I had expected 40 to be the last number in the list, but it is not there. Week 40 of 2019 is from Monday September 30 through October 6, so if I understand correctly you want the 5th week of those months that have a 5th week. This brings us back to the issue of not all month having a week 5 (because they don’t have 5 Mondays). What happened was that since I ran your code on a Tuesday, it took Tuesday in week 40, which is October 1, as a starting point, and therefore gave me the 1st rather than the 5th week of every month.
are there better solutions? Can you suggest one?
I can’t really. What you’ve got is fine.
Only you’re not using the int weekYear parameter. You may want to use it in place of your weekNumber local variable. In any case you should delete one of them and use the other.
And this unrelated tip: Your use of LocalDate.from(someLocalDate) is redundant since it just gives you the same LocalDate again (either the same object or an equal one, I don’t know or care). Just use someLocalDate in those situations.

Why does java.util.Calendar return minimal WEEK_OF_MONTH value as zero?

I'm trying to set minimal value of WEEK_OF_MONTH field as follows:
calendar.set(WEEK_OF_MONTH, calendar.getActualMinimum(WEEK_OF_MONTH));
The call to
calendar.getActualMinimum(WEEK_OF_MONTH)
returns 0
But at calculation during get* operations this field becomes 5.
Moreover, without leniency mode, I get
java.lang.IllegalArgumentException: WEEK_OF_MONTH: 0 -> 5 // or MONTH: 9 -> 8
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.complete(Calendar.java:2265)
at java.util.Calendar.get(Calendar.java:1826)
at Main.main(Main.java:19)
If I set WEEK_OF_MONTH = 1, then I get it correctly as 1.
Check out an example
Can anyone clarify such a behavior? Thanks in advance.
java.time
Locale russia = Locale.forLanguageTag("ru-RU");
WeekFields wf = WeekFields.of(russia);
LocalDate date = LocalDate.now(ZoneId.of("Europe/Moscow"));
int minimumWeekOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()).get(wf.weekOfMonth());
System.out.println("Minimum week of month: " + minimumWeekOfMonth);
LocalDate dateInFirstWeekOfMonth = date.with(wf.weekOfMonth(), minimumWeekOfMonth);
System.out.println("Date in first week of month: " + dateInFirstWeekOfMonth);
When running this snippet just now I got the following output:
Minimum week of month: 1
Date in first week of month: 2018-10-05
I have assumed that you are in Russian locale. Russia uses the international week numbering where Monday is the first day of the week and week one of a year or month is the first week that contains at least 4 days of the year or month. So week 1 of October 2018 was from Monday October 1 through Sunday October 7. This in turn means that the minumum week in this month is 1. Starting out from today (a Friday) and setting the week of month to 1 gives Friday in week 1, that is, Friday October 5.
If I start out from Wednesday September 12 instead I get:
Minimum week of month: 0
Date in first week of month: 2018-08-29
Week 1 of September was from Monday September 3 through September 9. This means that September 1 and 2 were in week 0, so 0 is the minimum week of month for September. And when starting from a Wednesday I set week number to 0, I get the Wednesday of that week, which happens to lie in August: August 29. If we ask for the week of month of that date, do we get 0?
System.out.println("Week of month: " + dateInFirstWeekOfMonth.get(wf.weekOfMonth()));
Output:
Week of month: 5
Since the date is in August, we now get which week of August the date is in, which happens to be week 5.
What happened in your code?
It seems to me that GregorianCalendar.getActualMinimum(Calendar.WEEK_OF_MONTH) always returns 0. I cannot make sense of this observation. Since Russia uses the Gregorian calendar, an instance of GregorianCalendar is what you really get from Calendar.getInstance.
I wouldn’t want to bother. As I said in a comment already, the Calendar class is long outdated and has a range of design problems with it, so I recommend you don’t use it. I’d certainly prefer java.time, the modern Java date and time API, any time.
Link
Oracle tutorial: Date Time explaining how to use java.time.

How to determine the date passing the Week Number and Day Number (Week) in java

I need to get the date by passing these parameters
year
week number (in a month) i.e. 1,2,3,4,5
day number (in a week) 0 (Sunday) to 6 (Saturday)
Month
I looked for a constructor in Calendar class but does not contain these parameters.
In spite of your tags I agree with Joe C’s comment, you should prefer the modern Java date and time API (AKA known as java.time or JSR-310) over the long outdated Calendar and friends. The modern classes are more programmer friendly and nicer to work with, they come with fewer surprises, lead to clearer and more natural code (at least when you get used to the fluent style) and are easier to debug should you make a bug.
I don’t know how you count your weeks of the month. I have assumed that the first week starts on the first of the month and lasts 7 days (so the second week starts on the 8th and the 5th week on the 29th day of the month). If you count differently, please see Hugo’s answer.
I suggest this method:
public static LocalDate weekNoAndDayToDate(Year year, Month month, int weekOfMonth, int dayOfWeek) {
// find day of week: convert from 0 (Sunday) to 6 (Saturday)
// to DayOfWeek, from 1 (Monday) to 7 (Sunday)
DayOfWeek dow;
if (dayOfWeek == 0) { // Sunday
dow = DayOfWeek.SUNDAY;
} else {
dow = DayOfWeek.of(dayOfWeek);
}
return year.atMonth(month)
.atDay(1)
.with(TemporalAdjusters.nextOrSame(dow))
.with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekOfMonth);
}
With this we may do for example
LocalDate today = weekNoAndDayToDate(Year.of(2017), Month.JULY, 1, 1);
This yields
2017-07-03
If you need either a Date or a GregorianCalendar, for example for an old API that you cannot change, do one of the following:
Date oldfashiondDateObject
= Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
GregorianCalendar oldfashionedCalendarObject
= GregorianCalendar.from(today.atStartOfDay(ZoneId.systemDefault()));
In both cases the result will be different in different time zones (one of the inherent troubles of the old classes). On my computer the first yields a Date of
Mon Jul 03 00:00:00 CEST 2017
The second yields a GregorianCalendar equal to the same point in time.
To create date from year, week number and week's day use java.util.Calendar instance:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2017);
cal.set(Calendar.WEEK_OF_YEAR, 26);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
To convert from Calendar to java.util.Date :
Date date = cal.getTime();
To convert Date into java.time.LocalDateTime use :
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
As #Ole V.V. explained, you need to define in what day the week starts, and how many days it must have to be considered the first week.
For example, the ISO-8601 standard considers:
Monday to be the first day-of-week.
The minimal number of days in the first week: the standard counts the first week as needing at least 4 days
The month is divided into periods where each period starts on the defined first day-of-week. The earliest period in the same month is referred to as week 0 if it has less than the minimal number of days and week 1 if it has at least the minimal number of days.
Depending on how you define those, you can have different results. Consider the calendar for July 2017:
July 2017
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
If we consider ISO's definition, we have:
week zero - 2017-07-01 to 2017-07-02
week 1: from 2017-07-03 to 2017-07-09
week 2: from 2017-07-10 to 2017-07-16
week 3: from 2017-07-17 to 2017-07-22
week 4: from 2017-07-23 to 2017-07-30
week 5: 2017-07-31
If we consider first day of week as Sunday and minimal number of days in the first week as 1, we have:
week 1: 2017-07-01
week 2: from 2017-07-02 to 2017-07-08
week 3: from 2017-07-09 to 2017-07-15
week 4: from 2017-07-16 to 2017-07-21
week 5: from 2017-07-22 to 2017-07-29
week 6: from 2017-07-30 to 2017-07-31
As a solution with Calendar was already posted, I'm gonna write one using the new API. If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
I'm using a DateTimeFormatter because it takes all the fields (month, year, day of week and week of month) and resolves the day accordingly, creating a LocalDate (which has the day/month/year fields). I'm also using the WeekFields class, which can be configured to use different week definitions (first day and minimal number of days in first week)
There's also a little adjustment to consider Sunday as zero:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// week of month, using week definition from WeekFields
.appendValue(wf.weekOfMonth()).appendPattern(" ")
// day of week
.appendValue(ChronoField.DAY_OF_WEEK)
// month and year
.appendPattern(" M/yyyy")
// create formatter
.toFormatter();
return LocalDate.parse(weekOfMonth + " " +
// Sunday is 0, adjusting value
(dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}
Using this code (week starts on Sunday, and 2 days are required to be considered the first week - otherwise week will be zero as in the first example above):
LocalDate d = getDate(1, 6, 7, 2017);
d will be 2017-07-08 (Saturday in the week 1 of July 2017).
If you want to use ISO 8601 definition, use the constant WeekFields.ISO instead of using WeekFields.of() method.
As #Ole V.V. suggested in the comments, it can also be done without creating a formatter: get the first dayOfWeek of the month and adjust it to the desired weekOfMonth:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
// Sunday is 0, adjusting value
DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);
// get the first weekday of the month
LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));
// check in which week this date is
int week = first.get(wf.weekOfMonth());
// adjust to weekOfMonth
return first.plusWeeks(weekOfMonth - week);
}
This works exactly the same way, but without the need of a formatter - I've tested with dates from year 1600 to 2100 and it gets the same results.
PS: Calendar also has a similar configuration via the methods setFirstDayOfWeek() and setMinimalDaysInFirstWeek(). You can check the default configuration calling the respective get methods.

Increment date in android

I have an application that asks a user to input a start date. Then the user will go through a series of 5 pages and input data to be exported to an excel sheet with page 1 being on the first block with the date day 2 being on the second block with a incremented date etc etc. Is there a any type of function available to accomplish this or am I doomed to the use of if statements to increment the date correctly. I tried to increment the date by simply returning the date in a series of variables
int day = getIntent().getExtras().getInt("day");
int year = getIntent().getExtras().getInt("year");
String month = getIntent().getStringExtra("month");
String dayofweek = getIntent().getStringExtra("dayofweek");
And the I would output those variables in the appropriate cells for the excel sheet. Monday would be just the variables since thats the start date. Tuesday would be the day + 1. Wednesday would be the day + 1 to continuously increment it to day 5. The problem I ran into is it the start day is selected as the last day of the month it would continue to increment the day ie Jan 31 Jan 32 Jan 33 Jan 34 etc etc. So I have to take the end of the month, end of the year, and leap year in 2016 and 2020 into account. On top of weekends. For example if Tuesday is selected I want the start date to show up on tuesday the fourth date would show up on friday and then the next correct date on monday again.
So I guess the short of it all would be Is there a better way to implement this?
You have to make a date or calendar object to increment....
1 for month is February. The 30th of February is changed to 1st of March. You should set 0 for month. The best is to use the constant defined in Calendar:
c1.set(2000, Calendar.JANUARY, 30);
Then you can increment it
c1.add(Calendar.DATE, 1);
Date newDate = c1.getTime();
You will get new date object of the next day :)

Java Calendar - Date is unpredictable after setting day_of_week

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.

Categories

Resources