I need to obtain certain data for current week and the same week of the previous year based on the current date.
For example, today is 23/11/2017 - Thursday.
I have the next date range for current week: 20/11/2017 (Monday) - 23/11/2017 (Thursday).
What is the best way to obtain a corresponding weekly date range for the previous year ( 21/11/2016 (Monday) - 24/11/2016 (Thursday) )?
You must make a specification document, which the business side should affirm.
Today is 23/11/2017 - Thursday. So the current week is: 20/11/2017
(Monday) - 26/11/2017 (Sunday).
In the previous year we take today 23/11/2017 and subtract a year..
That is a Wednesday 23/11/2016.
We obtain the week: 21/11/2016 (Monday) - 27/11/2016 (Sunday).
A. Irregularity: 29th of February becomes the 28th in the previous
year.
B. We are interested only in Monday - Thursday of the result,
but do all, so we can work on Saturday as well.
Then consider weeks & week numbers. Alternative:
Today is 23/11/2017 - Thursday. This is the week with week number 47,
with week year 2017. (01/01/2017 would be in week 52 of week year
2016). Yield week 47 of previous week year 2016.
A. 53 possibly becomes 52 if there is no week 53.
B. Start of January
can be of the previous week year.
As you see, one should not simply implement, but document such business logic.
By the way the new java date time API is great.
The hard parts are defining what you want and which time zone you want it in. The latter is true because at time of writing it is November 23 in Smolensk, but in other places in the world it is already November 24, so you need to decide.
I understand your problem this way, please check if correct: Find the date range from the beginning of this week (Monday) till today. Then find a range last year also from Monday till the same day of week as today, as close as possible to the same dates. This means that you will always want to go 52 weeks back. Then you will get dates that are one day later (two days later if crossing February 29 in a leap year), this will always be as close as you can get.
With java.time, the modern Java date and time API, the code is simple:
LocalDate today = LocalDate.now(ZoneId.of("Europe/Moscow"));
LocalDate firstDayOfWeek
= today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
System.out.println("This year: " + firstDayOfWeek + " - " + today);
LocalDate firstDayLastYear = firstDayOfWeek.minusWeeks(52);
LocalDate correspondingDayLastYear = today.minusWeeks(52);
System.out.println("Last year: " + firstDayLastYear + " - "
+ correspondingDayLastYear);
Running just now this printed:
This year: 2017-11-20 - 2017-11-23
Last year: 2016-11-21 - 2016-11-24
Please substitute your desired time zone instead of Europe/Moscow if this is not the one you want.
If you want to print the day of week too, use a DateTimeFormatter.
DateTimeFormatter dateFormatter = DateTimeFormatter
.ofPattern("d/M/uuuu (EEEE)", Locale.forLanguageTag("ru"));
System.out.println("This year: " + firstDayOfWeek.format(dateFormatter) + " - "
+ today.format(dateFormatter));
This prints:
This year: 20/11/2017 (понедельник) - 23/11/2017 (четверг)
If you run the code on December 31, 2018, you will not reach back into 2017:
This year: 2018-12-31 - 2018-12-31
Last year: 2018-01-01 - 2018-01-01
Again, check if this is what you want.
Related
This question already has answers here:
Y returns 2012 while y returns 2011 in SimpleDateFormat
(5 answers)
How does Java "week year" really work?
(2 answers)
Closed 3 years ago.
I found strange behaviour when using Java's DateTimeFormatter on LocalDate for 30th and 31st of December.
LocalDate date1 = LocalDate.of(2019, Month.DECEMBER, 30);
System.out.println("date1: " + date1);
System.out.println("converted date1: " + DateTimeFormatter.ofPattern("YYYY-MM-dd").format(date1));
LocalDate date2 = LocalDate.of(2019, Month.JANUARY, 30);
System.out.println("date2: " + date2);
System.out.println("converted date2: " + DateTimeFormatter.ofPattern("YYYY-MM-dd").format(date2));
Output:
date1: 2019-12-30
converted date1: 2020-12-30
date2: 2019-01-30
converted date2: 2019-01-30
The first date (December 30) is converted with the next year, the second date (January 30) is converted with the correct year.
Am I missing something out, or is it a bug?
Y means "year of week-based year". It's not the same as y which means "year of era" as per docs.
This post explains the difference and purpose of Y:
A week year is a year where all the weeks in the year are whole weeks. This is specified by some standard (which I don't remember at the moment). Basically, this guarantees that a program working on a week's data will not transition between years. Unfortunately, this also means that the beginning of the year may not start on the first of January. What year a particular day belongs in depends on these rules, and of course, there are days where the year and the week year are different.
In your example 30st Dec '19 was a Monday, 31st Dec '19 was a Tuesday and 1st Jan '20 was Wednesday so "year of week-based year" for this three days is 2020.
Change 'Y' to 'y' (Capital case to lower case) 'Y' is week based year and since most of the week of Dec-30, 2019 falls into 2020 the year is 2020. while lower case 'y' is your regular year. See JavaDoc for DateTimeFormatter for details
I only found a solution for Joda Time.
My solution works only if the last day is not in the first week:
LocalDate.now() // or any other LocalDate
.withDayOfMonth(31)
.withMonth(12)
.get(weekFields.weekOfWeekBasedYear())
So what is the correct way in Java Time (like in Joda Time)?
This information is available directly using the java.time.* API.
The key method is rangeRefinedBy(Temporal) on TemporalField. It allows you to obtain a ValueRange object that provides the minimum and maximum values for the field, refined by the temporal object passed in.
To find out how many ISO weeks there are in the year, do the following:
LocalDate date = LocalDate.of(2015, 6, 1);
long weeksInYear = IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(date).getMaximum();
System.out.println(weeksInYear);
Note that the date you pass in is used to determine the answer. So when passing in dates in early January or late December ensure you understand how the ISO week-based calendar works, and the difference between the calendar year and the week-based year.
If one wants to get the week number based on 7 days no matter when the week starts and how many days the first partial week of the year has, ChronoField.ALIGNED_WEEK_OF_YEAR might be helpful.
For example, the 1st of January 2016 based on the ISO-8601 definition (where a week starts on Monday and the first week has a minimum of 4 days) falls into week number 0, but in the aligned it is week number 1.
LocalDate date = LocalDate.of(2016, 1, 1);
int iso8601 = date.get(WeekFields.ISO.weekOfYear()); // result is 0
int aligned = date.get(ChronoField.ALIGNED_WEEK_OF_YEAR); // result is 1
It seems that when the last day is in the first week, you don't want to get 1 as an answer but 52/3/4, in which case you may be looking for:
LocalDate.of(2017, 12, 31).get(WeekFields.ISO.weekOfYear());
There are several ways to define week numbers - if that doesn't do what you want you need to clarify which method you want to use.
The correct and best solution is given by #JodaStephen. Here are some alternatives anyways.
December, 28th is always in the last week of a year, because the remaining three days after can not form a major part of another week:
int weeks = LocalDate.of(2017, 12, 28).get(WeekFields.ISO.weekOfYear());
A year has 53 weeks if it starts or ends with a thursday:
Year year = Year.of(2017);
DayOfWeek firstDay = year.atDay(1).getDayOfWeek();
DayOfWeek lastDay = year.atDay(year.length()).getDayOfWeek();
int weeks = firstDay == DayOfWeek.THURSDAY || lastDay == DayOfWeek.THURSDAY ? 53 : 52;
And finally this will give you the "week number" of the last day of year. It's 53 also in cases where the last week's number is 52 iff the major part of the last day's week lies in the next year (the week is claimed by the next year).
// This will not give the correct number of weeks for a given year
Year year = Year.of(2018);
year.atDay(year.length()).get(WeekFields.ISO.weekOfYear()); // 53
That's what you actually did.
By the following way I set date object to calendar instance.
But when I get the week of year , wrongly it returns week of the year as 1.
Same as week of the year, week of the month value also wrong. It return as 6. But it should be 5.
Following is output of calendar instance value
java.util.GregorianCalendar[time=1514678400000,
areFieldsSet=true,
areAllFieldsSet=true,
lenient=true,
zone=sun.util.calendar.ZoneInfo[id="UTC",
offset=0, dstSavings=0, useDaylight=false, transitions=0, lastRule=null],
firstDayOfWeek=1, minimalDaysInFirstWeek=1, ERA=1, YEAR=2017, MONTH=11, WEEK_OF_YEAR=1, WEEK_OF_MONTH=6, DAY_OF_MONTH=31, DAY_OF_YEAR=365, DAY_OF_WEEK=1, DAY_OF_WEEK_IN_MONTH=5, AM_PM=0, HOUR=0, HOUR_OF_DAY=0, MINUTE=0, SECOND=0, MILLISECOND=0, ZONE_OFFSET=0, DST_OFFSET=0]
firstDayOfWeek=1 means that Sunday is the first day of the week. So Sunday December 31, 2017 belonged to week 1 of 2018.
It’s not well documented, but it turns out that the week of month follows the month, so on Dec 31, it gives you the number of the week in December. Since December 1 and 2 were Friday and Saturday, they formed week 1 of December. Week 2 began on December 3. So week 6 began on December 31 and lasted only that day. This explains why week of month is 6 in your case.
Whether we use IST time zone (I am assuming it means Asia/Kolkata) or UTC doesn’t make a difference. At 5:30 AM in India, it was 00:00 in UTC, the date was still December 31, and therefore the week numbers are also unchanged.
Your Calendar’s settings of first day of week and minimal days in first week follow the standard used in USA and some other countries. If instead you wanted the international standard where Monday is the first day of the week and there is a minimum of 4 days in week one, you may instantiate it with a locale of a country that uses the international standard, for example:
Calendar c = Calendar.getInstance(Locale.FRANCE);
In this case week of year will be 52 and week of month will be 4. Not 5. This is because the first three days of December are not enough to form a week, so week 1 of December begins on December 4. Then the first three days of December are in week 0.
Prefer to use java.time
All of that said, the Calendar class is long outmoded and poorly designed. I recommend you use java.time, the modern Java date and time API, instead. Specifically, the LocalDate & WeekFields classes.
For example:
LocalDate date = LocalDate.of(2017, Month.DECEMBER, 31);
System.out.println("ISO week of year " + date.get(WeekFields.ISO.weekOfWeekBasedYear()));
System.out.println("ISO week of month " + date.get(WeekFields.ISO.weekOfMonth()));
System.out.println("US week of year " + date.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()));
System.out.println("US week of month " + date.get(WeekFields.SUNDAY_START.weekOfMonth()));
Output:
ISO week of year 52
ISO week of month 4
US week of year 1
US week of month 6
The results agree with those we got from Calendar.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I only found a solution for Joda Time.
My solution works only if the last day is not in the first week:
LocalDate.now() // or any other LocalDate
.withDayOfMonth(31)
.withMonth(12)
.get(weekFields.weekOfWeekBasedYear())
So what is the correct way in Java Time (like in Joda Time)?
This information is available directly using the java.time.* API.
The key method is rangeRefinedBy(Temporal) on TemporalField. It allows you to obtain a ValueRange object that provides the minimum and maximum values for the field, refined by the temporal object passed in.
To find out how many ISO weeks there are in the year, do the following:
LocalDate date = LocalDate.of(2015, 6, 1);
long weeksInYear = IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(date).getMaximum();
System.out.println(weeksInYear);
Note that the date you pass in is used to determine the answer. So when passing in dates in early January or late December ensure you understand how the ISO week-based calendar works, and the difference between the calendar year and the week-based year.
If one wants to get the week number based on 7 days no matter when the week starts and how many days the first partial week of the year has, ChronoField.ALIGNED_WEEK_OF_YEAR might be helpful.
For example, the 1st of January 2016 based on the ISO-8601 definition (where a week starts on Monday and the first week has a minimum of 4 days) falls into week number 0, but in the aligned it is week number 1.
LocalDate date = LocalDate.of(2016, 1, 1);
int iso8601 = date.get(WeekFields.ISO.weekOfYear()); // result is 0
int aligned = date.get(ChronoField.ALIGNED_WEEK_OF_YEAR); // result is 1
It seems that when the last day is in the first week, you don't want to get 1 as an answer but 52/3/4, in which case you may be looking for:
LocalDate.of(2017, 12, 31).get(WeekFields.ISO.weekOfYear());
There are several ways to define week numbers - if that doesn't do what you want you need to clarify which method you want to use.
The correct and best solution is given by #JodaStephen. Here are some alternatives anyways.
December, 28th is always in the last week of a year, because the remaining three days after can not form a major part of another week:
int weeks = LocalDate.of(2017, 12, 28).get(WeekFields.ISO.weekOfYear());
A year has 53 weeks if it starts or ends with a thursday:
Year year = Year.of(2017);
DayOfWeek firstDay = year.atDay(1).getDayOfWeek();
DayOfWeek lastDay = year.atDay(year.length()).getDayOfWeek();
int weeks = firstDay == DayOfWeek.THURSDAY || lastDay == DayOfWeek.THURSDAY ? 53 : 52;
And finally this will give you the "week number" of the last day of year. It's 53 also in cases where the last week's number is 52 iff the major part of the last day's week lies in the next year (the week is claimed by the next year).
// This will not give the correct number of weeks for a given year
Year year = Year.of(2018);
year.atDay(year.length()).get(WeekFields.ISO.weekOfYear()); // 53
That's what you actually did.
in my android app I'm working on to get the week number of a provided date. but it doesn't give me the correct week number I'm using following code to get the week number.
String format = "dd-MM-yyyy";
SimpleDateFormat df = new SimpleDateFormat(format);
Date date = df.parse(date);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int week = cal.get(Calendar.WEEK_OF_YEAR);
but this not give me what I'm expecting. for instance lets take January 2016.
As of January 2016 as seen from the above calendar, days belongs to the week number 1 are only 1st,2nd and 3rd. and for the week number 2, relevant days are 4th, 5th, 6th, 7th,8th, 9th and 10th. then for the week number 3, relevant days are 11th, 12th, 13th, 14th, 15th, 16th and 17th and so on. but for the date 12/01/2016 above code gives me week number 2 instead of week number 3. am I doing something wrong or is this the way the function works normally. how can I meet my goal to get the week number as I mention above.
1st, 2nd and 3rd of January are actually week 53 of year 2015. The code is working correctly, your assumptions about week numbering are wrong. The default behaviour of GregorianCalendar is the same as ISO definition for the first week in Wikipedia:
The ISO 8601 definition for week 01 is the week with the year's first
Thursday in it. The following definitions based on properties of this
week are mutually equivalent, since the ISO week starts with Monday:
It is the first week with a majority (4 or more) of its days in January.
Its first day is the Monday nearest to 1 January.
It has 4 January in it. Hence the earliest possible dates are 29 December through 4 January, the latest 4 through 10 January.
It has the year's first working day in it, if Saturdays, Sundays and 1 January are not working days.
If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in
week 01. If 1 January is on a Friday, it is part of week 53 of the
previous year; if on a Saturday, it is part of week 52 (or 53 if the
previous year was a leap year); if on a Sunday, it is part of week 52
of the previous year.
Found at https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html :
Values calculated for the WEEK_OF_YEAR field range from 1 to 53. The first week of a calendar year is the earliest seven day period starting on getFirstDayOfWeek() that contains at least getMinimalDaysInFirstWeek() days from that year. It thus depends on the values of getMinimalDaysInFirstWeek(), getFirstDayOfWeek(), and the day of the week of January 1.
So your first week will be week 0 because it starts counting from your first full 7 days week. You can change this by setting getMinimalDaysInFirstWeek() to a lower value