Here is my code. This method works only for 1 ( current year). When week number is more than 53, dates start from dates in first week in current year but not the next.
Year has 52 or 53 weeks, so when week is last (52or53) i have 2020-12-28 - 2021-01-03, but when I want get first week of next year (2021-01-04 - 2021-01-10) it outputs me first week from 2020 ( 2019-12-30 - 2020-01-05).
public void showPreviousNextWeekDays(DataBaseHelper dataBaseHelper, long weekChange) {
if (weekChange >= 0) {
LocalDate ld = LocalDate.now().plusWeeks(weekChange);
final long weekNumber = ld.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); //takes week from current date
LocalDate firstDayOfWeek = ld.ofYearDay(ld.getYear(), (int)weekNumber)
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
.with(DayOfWeek.MONDAY);
LocalDate lastDayOfWeek = ld.ofYearDay(ld.getYear(), (int)weekNumber)
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
.with(DayOfWeek.SUNDAY);
String firstAndLastDayOfWeek = firstDayOfWeek.toString() + " - " + lastDayOfWeek.toString();
daysArrayAdapter = new ArrayAdapter<DayModel>(getActivity(), android.R.layout.simple_list_item_1, db.NextWeek(weekChange));
daysList.setAdapter(daysArrayAdapter);
How can i go forward with years also by adding weeks?
You are correct that your code is not correct. When I set weekChange to 14 and run today (September 29), I get 2019-12-30 - 2020-01-05 where expected result would be 2021-01-04 - 2020-01-10. Also for 15 and 16 weeks your code goes back to a week in the beginning of this year rather than finding a week in the beginning of next year as it should.
I suggest the following, which is also a bit simpler:
int weekChange = 14;
LocalDate ld = LocalDate.now(ZoneId.systemDefault()).plusWeeks(weekChange);
LocalDate firstDayOfWeek = ld.with(DayOfWeek.MONDAY);
LocalDate lastDayOfWeek = ld.with(DayOfWeek.SUNDAY);
String firstAndLastDayOfWeek = firstDayOfWeek.toString() + " - " + lastDayOfWeek.toString();
System.out.println(firstAndLastDayOfWeek);
Output when run today:
2021-01-04 - 2021-01-10
LocalDate uses the ISO calendar system including ISO weeks if not explicitly instructed otherwise. So we can be sure that ld.with(DayOfWeek.MONDAY) gives us Monday at the start of the same ISO week. Similarly ld.with(DayOfWeek.SUNDAY) gives us Sunday at the end of the ISO week. So there is nothing more to it.
What happened in your code?
Sticking to the case of weekChange 14 and running today.
ld becomes 2021-01-05 (Tuesday 14 weeks from now). This date is in week 1 of 2021, so weekNumber will be 1.
ofYearDay is a static method of LocalDate. So ld.ofYearDay(ld.getYear(), (int)weekNumber) gives you the same date as you would have got from LocalDate.ofYearDay(ld.getYear(), (int)weekNumber). It does not use ld for anything. Since weekNumber is 1, you get day 1 of 2021, so 2021-01-01. Passing the week number as the day of year to ofYearDay() is meaningless. 2021-01-01 happens to fall in week 53 of 2020. So with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber) adjusts the date back to week 1 of 2020. This is where we go a year wrong. The result is 2020-01-03. The Monday of that week is 2019-12-30, which explains why you got this as the first day of week in the output.
The last day of week is similar. And week numbers 2 and 3 are similar since you use the week number as a day of year, and January 2 and 3 fall in the last week of the previous year, 2020, too.
How did I find out? I ran your code in my debugger. There it was clear to see. If you haven’t yet learned to use a debugger. I suggest that now is the best of all times.
Related
If you consider that week will start from Jan 01 of every year & week start is SUNDAY then there will be 53 weeks in 2019.
Following above Jan 29,30,31 2019 will be into Week-53 of 2019.
As given in documentation of IsoFields for WEEK_OF_WEEK_BASED_YEAR that all three fields are validated against their range of valid values. The week-of-week-based-year field is validated from 1 to 52 or 53 depending on the week-based-year.
So I'm assuming that following code should give the output as: WEEK_OF_WEEK_BASED_YEAR 53 & WEEK_BASED_YEAR 2019.
But it's giving output as: 1 & 2020
import java.time.LocalDate;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.time.temporal.IsoFields;
public class WeekStartDemo {
public static void main(String args[]) {
DateTimeFormatter DATE_FORMATTER = DateTimeFormatter
.ofPattern("uuuu-MM-dd")
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
LocalDate updatedDate = LocalDate.parse("2019-12-30", DATE_FORMATTER);
System.out.println(updatedDate.toString());
System.out.println(updatedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR));
System.out.println(updatedDate.get(IsoFields.WEEK_BASED_YEAR));
}
}
If I pass the date as 2019-12-28 then it's returning WEEK_OF_WEEK_BASED_YEAR 52 & WEEK_BASED_YEAR 2019. But doesn't work for last week of 2019 (which is 53rd week)
Let me know what I'm missing in above code.
As I mentioned in the comments, and from your Javadoc link for IsoFields, The week-based-year itself is defined relative to the standard ISO proleptic year. It differs from the standard year in that it always starts on a Monday (not a Sunday). It should be easy enough to find the years with 53 weeks using your posted code, iterate from 1900 to 2300 and parse the WEEK_OF_WEEK_BASED_YEAR for the last day of the given year and print the values where it is 53. Like,
DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
for (int i = 1900; i < 2300; i++) {
LocalDate updatedDate = LocalDate.parse(String.format("%d-12-31", i), DATE_FORMATTER);
if (updatedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) == 53) {
System.out.println(i);
}
}
The first few values I get are
1903
1908
1914
1920
1925
1931
1936
1942
skipping ahead a bit...
2009
2015
2020
2026
So this year (2020) has 53 weeks, and 2019 does not.
LocalDate date = LocalDate.of(2019, Month.DECEMBER, 30);
int weekOfYear = date.get(WeekFields.SUNDAY_START.weekOfYear());
System.out.println(weekOfYear);
Output from this snippet is:
53
I believe that this is the exact difference between WeekFields.weekOfWeekBasedYear() and WeekFields.weekOfYear().
It may also be that your main source of confusion is using the wrong week fields, as Elliott Frisch also mentioned. The ISO week fields that you use define Monday as the first day of the week and week one of the year as the first week containing at least 4 days of the year. In contrast you said that you wanted:
If you consider that week will start from Jan 01 of every year &
week start is SUNDAY …
From your comments:
… will the new week (Week-01) will always start on Jan 01 of every year ?
Yes it will.
How can I perform minus 1 week on this weekOfYear ? As for Jan
01,2020, weekOfYear will be Week-01 2020. What type of minus 1
week I can perform to get weekOfYear as Week-53 2019 ? I tried
with date.minusWeeks(1) but it returns Week-52 2019
LocalDate dateInWeek1 = LocalDate.of(2020, Month.JANUARY, 3);
int weekOfYear = dateInWeek1.get(wf.weekOfYear());
System.out.println(weekOfYear);
LocalDate dateInPreviousWeek;
if (weekOfYear == 1) {
dateInPreviousWeek = dateInWeek1.minusWeeks(1)
.with(TemporalAdjusters.lastDayOfMonth());
}
else {
dateInPreviousWeek = dateInWeek1.minusWeeks(1);
}
System.out.format("%s %2d%n", dateInPreviousWeek, dateInPreviousWeek.get(wf.weekOfYear()));
We need to handle the case of week 1 specially. When subtracting 1 week, we know we are getting into December the previous year. Selecting the last day of the month will give us December 31. This will always be in the last week of the year (usually week 53; occasionally week 54 if in a leap year that begins on a Saturday, I think that years 2000 and 2028 are examples of this).
The issue here is that a given year has either 365 or 366 days (the latter for a leap year), which means that every year has either 1 or 2 days extra beyond 52 weeks. The ISO system for week years is what handles this, and it means that sometimes the first week of the year might not start of the 1st of January, nor would the last day of a year fall in the 52nd week. One workaround here would be to just calculate the week starting from the beginning of each year, e.g.
LocalDate firstOfYear = LocalDate.of(2019, Month.JANUARY, 1);
LocalDate updatedDate = LocalDate.of(2019, Month.DECEMBER, 30);
int diff = (int)ChronoUnit.DAYS.between(firstOfYear, updatedDate);
int week = 1 + (diff / 7);
System.out.println("The week number is: " + week);
Based on the ISO-8601 (https://www.cl.cam.ac.uk/~mgk25/iso-time.html) this is correct. 2019 has no calendarweek 53. Week 01 of a year is per definition the first week that has the Thursday in this year, which is equivalent to the week that contains the fourth day of January.
As far as I know this is done to avoid a cw 1 without a workday.
I need to get the 4-5-4 Calendar Week from a Date. Is there any utility like Georgian Calendar in Java for 4-5-4 Retail Calendar?
If not, how can I create one? What all logic is needed? What is 53rd Week in case of Leap Year?
For example, if I pass a date (DD-MM-YYY) 04-03-2018 as input I should get March Week 1 as output.
Or, if I give 01-04-2018 as input I should get March Week 5 as output.
Please help me by providing a way to build this utility.
The following class should do it:
public class NrfMonthWeek {
public static NrfMonthWeek getWeek(LocalDate date) {
// Determine NRF calendar year.
// The year begins on the Sunday in the interval Jan 29 through Feb 4.
LocalDate firstDayOfNrfYear = date.with(MonthDay.of(Month.JANUARY, 29))
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
if (date.isBefore(firstDayOfNrfYear)) { // previous NRF year
firstDayOfNrfYear = date.minusYears(1)
.with(MonthDay.of(Month.JANUARY, 29))
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
}
// 1-based week of NRF year
int weekOfNrfYear = (int) ChronoUnit.WEEKS.between(firstDayOfNrfYear, date) + 1;
assert 1 <= weekOfNrfYear && weekOfNrfYear <= 53 : weekOfNrfYear;
YearMonth firstMonthOfNrfYear = YearMonth.from(firstDayOfNrfYear)
.with(Month.FEBRUARY);
if (weekOfNrfYear == 53) {
// Special case: the last week of a 53 weeks year belongs to
// the last month, January; this makes it a 5 weeks month.
return new NrfMonthWeek(firstMonthOfNrfYear.plusMonths(11), 5);
} else {
// 1-based month of NRF year (1 = February through 12 = January).
// A little math trickery to make the 4-5-4 pattern real.
int monthOfNrfYear = (weekOfNrfYear * 3 + 11) / 13;
// Number of weeks before the NRF month: 0 for February, 4 for March, 9 for April, etc.
int weeksBeforeMonth = (monthOfNrfYear * 13 - 12) / 3;
int weekOfMonth = weekOfNrfYear - weeksBeforeMonth;
return new NrfMonthWeek(
firstMonthOfNrfYear.plusMonths(monthOfNrfYear - 1), weekOfMonth);
}
}
private YearMonth month;
/** 1 through 5 */
private int weekOfMonth;
public NrfMonthWeek(YearMonth month, int weekOfMonth) {
this.month = Objects.requireNonNull(month);
if (weekOfMonth < 1 || weekOfMonth > 5) {
throw new IllegalArgumentException("Incorrect week number " + weekOfMonth);
}
this.weekOfMonth = weekOfMonth;
}
#Override
public String toString() {
return month.getMonth().getDisplayName(TextStyle.FULL, Locale.US)
+ " Week " + weekOfMonth;
}
}
Let’s try it. Here I pass the two dates from your question to the getWeek method:
System.out.println(NrfMonthWeek.getWeek(LocalDate.of(2018, Month.MARCH, 4)));
System.out.println(NrfMonthWeek.getWeek(LocalDate.of(2018, Month.APRIL, 1)));
This prints the desired:
March Week 1
March Week 5
Though only month and week are printed, also the year is contained in the object returned from getWeek.
The formulas for calculating the month and week-of-month are cryptic. I have no really good argument why they work, though such an argument could probably be constructed. I have tested them with all relevant values, and you are free to do the same. Other than that, using java.time, the modern Java date and time API, it wasn’t too bad.
If that were me, I would have finer validation in the NrfMonthWeek constructor, only allowing week 5 in the months that may have 5 weeks. I am leaving that to you. And I would have a pretty thorough unit test.
Please check whether my understanding agrees with yours: If I have understood correctly from the example calendars that Basil Bourque linked to in his answer, the NRF 4-5-4 year starts with February. Its weeks begin on Sunday, and the first week of the year is the first week that contains at least 4 days of February. In other words, the week that contains February 4. In yet other words, the week that begins on a Sunday in the interval January 29 through February 4. Months March, June, September and December always have 5 weeks. In case of a 53 weeks year also January has 5 weeks.
No support built-in
Neither the modern java.time classes nor the legacy date-time classes (Date/Calendar) directly support the National Retail Federation 4-5-4 Calendar.
Implement Chronology
I suspect the best way to solve this problem is to implement a Chronology for the java.time framework.
Java 8 and later bundle five implementations (HijrahChronology, IsoChronology, JapaneseChronology, MinguoChronology, ThaiBuddhistChronology). Their source is available in the OpenJDK project.
The ThreeTen-Extra project provides ten more chronologies (AccountingChronology, BritishCutoverChronology, CopticChronology, DiscordianChronology, EthiopicChronology, InternationalFixedChronology, JulianChronology, PaxChronology, Symmetry010Chronology, Symmetry454Chronology) whose source code might help.
I'm trying to get the number of the week for a date , In my country the week begins on Sunday so the week number of 6/5/2016 is 23 but it returning 22 because the ISO week in JAVA starts from Monday , I have used the following methods but it's not working
mCalendar = Calendar.getInstance();
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR); //returns 22 I need 23
// I have tried the following method but it has no effect
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
note that I can't use the Time Class I can only use Java 7
Java 8 version full answer:
public<T extends Temporal> long getWeekNumber(T tObj) {
DayOfWeek firstDayOfWeek = DayOfWeek.SUNDAY; // set your
Temporal firstDayOfThisYear = tObj.with(TemporalAdjusters.firstDayOfYear());
Temporal firstDayOfWeekInMonth = firstDayOfThisYear
.with(TemporalAdjusters.dayOfWeekInMonth(1, firstDayOfWeek));
return ChronoUnit.WEEKS.between(tObj, firstDayOfWeekInMonth);
}
The parameter can be any Temporal type, even the Temporal itself.
I don't know from where you are but Java has an awesome Calendar which allows the following:
Calendar calendar = Calendar.getInstance(Locale.TRADITIONAL_CHINESE);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 24
Calendar calendar = Calendar.getInstance(Locale.UK);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 22
You could use the locale constants to specify your location and i think you will get the right number of weeks.
Edit:
Now I see the failure in your code. Please note that Java works from the top to the button of your code:
Calendar calendar = Calendar.getInstance();
// First set the first day of the week ...
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
// ... and than you could ask the calendar for the week
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
// will produce 23
System.out.println("Number of week: " + weekNumber);
I don't know where you are from.
I'm french and that works perfectly this way :
Calendar mCalendar = Calendar.getInstance(Locale.FRANCE);
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR);
System.out.println(weekNum); // --> 23
Get the current moment.See this Question: How to initialize a variable of type date in Java?
Determine the first Sunday of the year.Handled thoroughly in this question: How to get all the Sunday's of a year
Count weeks between that first Sunday and current moment.See the Question: Get the number of weeks between two Dates
I've just figured out how to change it
you need to set up two things
1-first day of the week
2-the minimal day of week
setFirstDayOfWeek(Calendar.SUNDAY);
setMinimalDaysInFirstWeek(7);
this will tell the calendar to make the fist day is sunday and with 7 days minimal week
My team is looking to switch from Joda time to java.time, but we're seeing different behavior in formatting using the same pattern. The issue arises when we're using the week-of-week-year w symbol:
final String dateString = "2016-01-04 00:00:00";
final String inputPattern = "yyyy-MM-dd HH:mm:ss";
// parse the input string using Joda
final org.joda.time.format.DateTimeFormatter jodaInputFormatter = org.joda.time.format.DateTimeFormat.forPattern(inputPattern);
final org.joda.time.DateTime jodaDateTime = jodaInputFormatter.parseDateTime(dateString);
// parse the input string using two different java.time classes
final java.time.format.DateTimeFormatter javaTimeInputFormatter = java.time.format.DateTimeFormatter.ofPattern(inputPattern).withZone(java.time.ZoneOffset.UTC);
final java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse(dateString, javaTimeInputFormatter);
final java.time.ZonedDateTime zonedDateTime = java.time.ZonedDateTime.parse(dateString, javaTimeInputFormatter);
final String outputPattern = "'week' w - dd/MM/yyyy HH:mm:ss";
final org.joda.time.format.DateTimeFormatter jodaOutputFormatter = org.joda.time.format.DateTimeFormat.forPattern(outputPattern);
final java.time.format.DateTimeFormatter javaTimeOutputFormatter = java.time.format.DateTimeFormatter.ofPattern(outputPattern);
// output: week 1 - 04/01/2016 00:00:00
System.out.println("With joda: " + jodaOutputFormatter.print(jodaDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With LocalDateTime: " + javaTimeOutputFormatter.format(localDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With ZonedDateTime: " + javaTimeOutputFormatter.format(zonedDateTime));
For some reason, the output from the w symbol is off-by-one across the two implementations.
What is causing this inconsistency? Is the w symbol inconsistently implemented across Joda time and java.time?
Well, it is a little bit speculative, but since you told me that your system timezone is EST (-05:00) I assume that you are sitting in US (New York?). And US does not apply ISO-8601-week rules. Weeks start on Sunday, and the first week of the year does not need to contain at least 4 days (even one day is enough to be counted as first week of year).
So let's look at your example date of 4th of January. It is a Monday. The first US-week is from 2016-01-01 until 2016-01-02 (2 days - enough for US). And the second US-week starts on Sunday the 3rd of January, so the fourth of January is in the second week, too.
And now the critical point: java.time (JSR-310) uses a localized week of week-based-year for the pattern symbol w, see also its backport which should have the same code. Code excerpt:
} else if (cur == 'w') {
if (count > 2) {
throw new IllegalArgumentException("Too many pattern letters: " + cur);
}
appendInternal(new WeekFieldsPrinterParser('w', count));
...
static final class WeekFieldsPrinterParser implements DateTimePrinterParser {
private final char letter;
private final int count;
public WeekFieldsPrinterParser(char letter, int count) {
this.letter = letter;
this.count = count;
}
#Override
public boolean print(DateTimePrintContext context, StringBuilder buf) {
WeekFields weekFields = WeekFields.of(context.getLocale());
DateTimePrinterParser pp = evaluate(weekFields);
return pp.print(context, buf);
}
The use of WeekFields.of(context.getLocale()) for the pattern symbol "w" is evident.
In contrast, Joda-Time only uses ISO-8601-week-definition which let weeks start on Monday and count that week as first week of year which contains at least four days in current calendar year. So the Monday 4th of January is the start of the first week-of-year because the three days before are not enough for ISO-8601 to be counted as week. Those preceding days are instead considered as last week of previous year.
Consequently, Joda-Time displays week 1 for 4th of January while java.time uses the US-week 2.
Solution of your problem is to specify the locale such that the formatter will use ISO-weeks so you get the same result as in Joda-Time. For example, you could choose Locale.UK which also uses English but other week rules. Don't rely on your default locale. This can fool you.
Edit: As Richard points out, I'm wrong—Java SE actually does say that the first week of a week-based year is the first Monday-based week containing at least four days, just like Joda-Time.
From the documentation of Java SE's IsoFields.WEEK_OF_WEEK_BASED_YEAR:
The week-of-week-based-year has values from 1 to 52, or 53 if the week-based-year has 53 weeks.
No mention is made of excluding any weeks, so it makes sense to assume that all weeks are counted.
From the Joda-Time Fields overview:
Weeks run from 1 to 52-53 in a week based year. The first day of the week is defined as Monday and given the value 1. The first week of a year is defined as the first week that has at least four days in the year.
January 1 and 2 comprise the first partial week of 2016, and since that's fewer than four days, Joda-Time does not count it as a week at all. January 4 is in the first week which contains four or more days.
I need the number of days in a year and I wanted to use Java8's new time api.
However, I can't do Duration.ofDays(365) because it doesn't account for leap years. And Duration.of(1, ChronoUnit.YEARS) doesn't fly because of java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
I looked into Period, but it doesn't appear useful for going from years to days.
I feel like I'm missing something here? I could write something to add a day if the year is a leap year, but it seems like I should be able to handle this out of the box.
As per the response in Getting Duration using the new dateTime API you should be using
Period p = Period.ofYears(1);
It's important to understand the difference between Duration (exact number of nanoseconds < 1 day) and Period (variable > 1 day).
Duration won't account for leap days, daylight savings time or leap seconds, for example, and is intended for durations of less than a day, at most a few days.
So you should use Period instead.
Because different years have different number of days, if you want to find the number of days in a year, you need to specify which year you're talking about.
If you want the number of days in a specific year, you can use
Year.of(year).length()
If you want the date one year from now, you can use
LocalDate.now().plusYears(1)
or
LocalDate.now().plus(Period.ofYears(1))
If you need the number of days between two dates, you can use
ChronoUnit.DAYS.between(start, end)
So to find the number of days to the date a year from now, you can use
LocalDate today = LocalDate.now();
long days = ChronoUnit.DAYS.between(today, today.plusYears(1));
If you want to see whether a membership of one year is still valid, you can use
Period membershipLength = Period.ofYears(1);
LocalDate membershipStart = ...;
LocalDate membershipEnd = membershipStart.plus(membershipLength);
LocalDate today = LocalDate.now();
boolean memberShipEnded = today.isAfter(membershipEnd);
boolean membershipValid = !membershipEnded;
It seems clear you do not want a duration (= between two dates), but the year length of a specific date.
LocalDate dateLeap = LocalDate.of(2004, Month.MARCH, 1);
System.out.println("leap year of " + dateLeap
+ " has days: " + dateLeap.lengthOfYear());
leap year of 2004-03-01 has days: 366
Java 8 Date & Time is astonishing complete.
If you mean, in January 5th 2004 to January 5th 2005 = 366 and March 2nd 2004 to March 2rd 2005 = 365:
int lengthOfYear(LocalDate date) {
return date.getMonthValue() <= 2
? date.lengthOfYear() // Count Feb in this year
: date.plusYears(1).lengthOfYear(); // Count Feb in next year
}
Explanation: basically the length is 365. But if date is >= March, the February in the next year is counted, otherwise this year's February.
Mind that plusYears(1) will not change DAY or MONTH.
Also neither leap second nor hour/minuts on February, 29th are considered.