I would like to get all the weeks between 2 dates with weeks that cross 2 months counted twice for each month. For example, in 2021 week 14 of the year hosted both March and April so in this case, I would like that week counted twice (once for March and once for April). I've looked and found just libraries that count the number of weeks between 2 dates. I think I could get week numbers and month numbers and form a unique array but this seems a bit over the top. Has anyone got any suggestions?
weeks that cross 2 months counted twice
The code below allows to do that by utilizing only the standard LocalDate class and it's methods isBefore(), plusWeeks(), plusDays().
Keep in mind the days of the week and months are represented by enums from the java.time package.
I've made a couple of assumptions:
week starts with Sunday;
chunks of the week at the start and at the end of the given period have to be taken into account as well as full-length weeks.
public static void main(String[] args) {
System.out.println(getWeekCount(LocalDate.of(2022, 1, 1),
LocalDate.of(2022, 2, 1)));
System.out.println(getWeekCount(LocalDate.of(2022, 1, 1),
LocalDate.of(2022, 3, 1)));
}
public static int getWeekCount(LocalDate date1, LocalDate date2) {
int weekCount = 0;
LocalDate cur = date1;
LocalDate finish = date2;
// assumption: week starts with sunday
// assumption: chunk of week at the start and at the end have to be taken into account as well as full weeks
if (cur.getDayOfWeek() != DayOfWeek.SUNDAY) { // adjusting current date
LocalDate next = cur.plusDays(DayOfWeek.SUNDAY.ordinal() - cur.getDayOfWeek().ordinal() + 1);
weekCount += getWeeksIncrement(cur, next);
cur = next;
}
if (finish.getDayOfWeek() != DayOfWeek.SUNDAY) { // adjusting finish date
LocalDate previous = finish.minusDays(finish.getDayOfWeek().ordinal() + 1);
weekCount += getWeeksIncrement(previous, finish);
finish = previous;
}
while (cur.isBefore(finish) || cur.equals(finish)) {
LocalDate next = cur.plusWeeks(1);
weekCount += getWeeksIncrement(cur, next);
cur = next;
}
return weekCount;
}
public static int getWeeksIncrement(LocalDate cur, LocalDate next) {
return weekIsSharedBetweenTwoMonth(cur, next) ? 2 : 1;
}
public static boolean weekIsSharedBetweenTwoMonth(LocalDate cur, LocalDate next) {
return next.getMonth() != cur.getMonth() &&
next.withDayOfMonth(1).isAfter(cur);
}
Output
7 - weeks between: 2022-01-01 and 2022-02-01
12 - weeks between: 2022-01-01 and 2022-03-01
Well, this is achievable with a combination of the Java Date and Time API (java.time) and the Java Streams API (java.util.stream):
long weeksBetween(LocalDate start, LocalDate endInclusive) {
LocalDate normalizedStart = start.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
LocalDate normalizedEndExclusive = endInclusive.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
return Stream.iterate(normalizedStart, d -> d.plusWeeks(1))
.takeWhile(d -> d.isBefore(normalizedEndExclusive))
.mapToInt(d -> d.getMonthValue() == d.plusDays(6).getMonthValue() ? 1 : 2)
.sum();
}
What happens here, is as follows.
First, the dates are normalized, that is, they are set to the start of the week (Monday according to ISO standards).
Then we walk over the Monday of each week, and check if its last day of the week (Sunday) lies within the same month. If it is, then it yields 1, otherwise it yields 2.
At last, we sum all yielded values.
Note that I assumed that a week starts on Monday (ISO). The code also considers the week of both the start date as the end date as full ones.
You can get the weeknumber like this using java.time:
LocalDate date = LocalDate.of(year, month, day);
int weekOfYear = date.get(ChronoField.ALIGNED_WEEK_OF_YEAR);
You did not mention which java version you are using. java.time was introduced in java 8. There are other solutions available for pre-java 8.
Based on the above, you should be able to solve your problem.
Related
This question already has answers here:
Is there a good way to get the date of the coming Wednesday?
(6 answers)
Closed 1 year ago.
How can I get a date by day's name?
For example:
Input: Monday
Output: 02/08/2021
Input: Tuesday
Output: 03/08/2021
I want to get the closest date of the day.
This is my understanding of what the OP wants -
Given a day of the week as input, print the date (having the same day of the week as the input) which is closest to today.
We can do this using LocalDate, DayOfWeek and TemporalAdjuster.
The logic is -
Convert the input day of week to an instance of DayOfWeek.
If today is the same day of week as the input, print today's date and stop, else proceed to the next steps.
Get the date of the same day of the week in the previous week.
Get the date of the same day of the week in the next week.
Check which day is closer to today by using .toEpochDay().
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public static void main(String[] args) {
String inputDayOfWeekString = "SUNDAY";
DayOfWeek inputDayOfWeek = DayOfWeek.valueOf(inputDayOfWeekString);
LocalDate today = LocalDate.now();
if (today.getDayOfWeek().equals(inputDayOfWeek)) {
System.out.println(today);
} else {
LocalDate sameDayNextWeek = today.with(TemporalAdjusters.next(inputDayOfWeek));
LocalDate sameDayPreviousWeek = today.with(TemporalAdjusters.previous(inputDayOfWeek));
LocalDate dateCloserToToday = (sameDayNextWeek.toEpochDay() - today.toEpochDay()) < (today.toEpochDay() - sameDayPreviousWeek.toEpochDay()) ? sameDayNextWeek : sameDayPreviousWeek;
System.out.println(dateCloserToToday);
}
}
Assuming that you want to find the closest day from today that has a specific day of week, one way to do this is to compute both the next and previous day from today that has that day of week, and compare them:
private static LocalDate closestDOW(DayOfWeek dow) {
LocalDate today = LocalDate.now();
LocalDate next = today.with(TemporalAdjusters.nextOrSame(dow));
LocalDate previous = today.with(TemporalAdjusters.previousOrSame(dow));
if (ChronoUnit.DAYS.between(today, next) < ChronoUnit.DAYS.between(previous, today)) {
return next;
} else {
return previous;
}
}
Alternatively, work out whether the next such day is at most three days away. If it is, then it is closer than the previous such day.
private static LocalDate closestDOW(DayOfWeek dow) {
LocalDate today = LocalDate.now();
int daysDiff = today.getDayOfWeek().getValue() - dow.getValue();
int daysUntilNextDOW = daysDiff >= 0 ? 7 - daysDiff : -daysDiff;
if (daysUntilNextDOW <= 3) {
return today.plusDays(daysUntilNextDOW);
} else {
return today.with(TemporalAdjusters.previousOrSame(dow));
}
}
I have an enum that looks like this
enum Period{DAY, WEEK, MONTH, YEAR}
What i need is a function that adds a specified amout of times the given Period to today while setting the day of month so that it is equal to the start date (if the outcome is valid).
Or maybe it is easier to understand like this:
Imagine you get your salary on the 31st every month (where applicable). The function returns the next valid date (from today) when you will receive your next salary. Where the function can distinguish if you get it Daily, Weekly, Monthly, Yearly and how often in the specified interval.
It also takes care of invalid dates
Lets have a look at an example:
public static Date getNextDate(Date startDate, Period period, int times){
/*
Examples:
getNextDate(31.08.2020, MONTH, 1) -> 30.09.2020
getNextDate(31.08.2020, MONTH, 2) -> 31.10.2020
getNextDate(30.05.2020, MONTH, 2) -> 30.09.2020
getNextDate(30.06.2020, MONTH, 2) -> 30.10.2020 (This is the next valid date after today)
Years are pretty simple i guess (Okay, there is at least one edge case):
getNextDate(28.02.2020, YEAR, 1) -> 28.02.2021
getNextDate(29.02.2020, YEAR, 1) -> 28.02.2021 <- Edge case, as 2020 is a gap year
getNextDate(29.02.2020, YEAR, 4) -> 29.02.2024 <- gap year to gap year
For weeks and days there are no edge cases, are there?
getNextDate(29.02.2020, DAY, 1) -> 03.09.2020
getNextDate(29.02.2020, DAY, 3) -> 05.09.2020
getNextDate(29.02.2020, WEEK, 2) -> 12.09.2020 (Same as DAY,14)
Important: If today is already a payment day, this already is the solution
getNextDate(03.09.2020, MONTH, 1) -> 03.09.2020 (No change here, the date matches today)
*/
}
I actually would prefer to use the modern LocalDate API (Just the input is an old date object at the moment, but will be changed later)
I hope i did not forget any edge cases.
Update with what i did
//This is a method of the enum mentioned
public Date getNextDate(Date baseDate, int specific) {
Date result = null;
switch (this) {
case DAY:
result = DateTimeUtils.addDays(baseDate, specific);
break;
case WEEK:
result = DateTimeUtils.addWeeks(baseDate, specific);
break;
case MONTH:
result = DateTimeUtils.addMonths(baseDate, specific);
break;
case YEAR:
result = DateTimeUtils.addYears(baseDate, specific);
break;
}
return result;
}
public Date getNextDateAfterToday(Date baseDate) {
today = new Date();
while(!baseDate.equals(today ) && !baseDate.after(today)){
baseDate= getNextDate(baseDate,1);
}
return startHere;
}
My getNextDate() Method works. The getNextDateAfterToday() also works, but does not return valid dates for edge cases. Example 31.06.2020, MONTH,1 would immediatly be stuc at 30st of every month and never skip back even if the month has 31 days. For 30.09.2020 it would be correct. But for 31.10.2020 it wouldn't
I finally figured a way (although it seems way, way, way to complicated for what i really wanted to achieve). I changed my getNextDateAfterTodayto this
public Date getNextValidFutureDate(Date entryDate, Date startDate, int specific) {
Date result = new Date(startDate.getTime());
while (!result.equals(entryDate) && !result.after(entryDate)) {
result = getNextDate(result, true, specific);
}
LocalDate ldStart = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate ldResult = result.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if (ldResult.getDayOfMonth() < ldStart.getDayOfMonth() && this != DAY && this != WEEK && this != YEAR) {
if (ldResult.lengthOfMonth() >= ldStart.getDayOfMonth()) {
ldResult = ldResult.with(ChronoField.DAY_OF_MONTH, ldStart.getDayOfMonth());
} else {
ldResult = ldResult.with(ChronoField.DAY_OF_MONTH, ldResult.lengthOfMonth());
}
}
return Date.from(ldResult.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
I did not change the other method to use LocalDate, but will do this in the future.
This works with all test cases i posted above. Though i hope i did not miss essential ones
… (although it seems way, way, way to complicated for what i really
wanted to achieve) …
Your own solution is not bad. I just couldn’t let the challenge rest, so here’s my go. I believe it’s a little bit simpler.
I am going all-in on java.time, the modern Java date and time API. I also skipped your Period enum since the predefined ChronoUnit enum fulfils the purpose. Only it also includes hours, minutes and other units that don’t make sense here, so we need to reject those.
The Date class is poorly designed as well as long outdated. Avoid it if you can (if you cannot avoid it, I am giving you the solution in the end).
public static LocalDate getNextDate(LocalDate startDate, TemporalUnit period, int times) {
if (! period.isDateBased()) {
throw new IllegalArgumentException("Cannot add " + period + " to a date");
}
LocalDate today = LocalDate.now(ZoneId.of("America/Eirunepe"));
if (startDate.isBefore(today)) {
// Calculate how many times we need to add times units to get a future date (or today).
// We need to round up; the trick for doing so is count until yesterday and add 1.
LocalDate yesterday = today.minusDays(1);
long timesToAdd = period.between(startDate, yesterday) / times + 1;
return startDate.plus(timesToAdd * times, period);
} else {
return startDate;
}
}
For demonstrating the method I am using this little utility method:
public static void demo(LocalDate startDate, TemporalUnit period, int times) {
LocalDate nextDate = getNextDate(startDate, period, times);
System.out.format("getNextDate(%s, %s, %d) -> %s%n", startDate, period, times, nextDate);
}
Now let’s see:
demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 1);
demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 2);
demo(LocalDate.of(2020, Month.MAY, 30), ChronoUnit.MONTHS, 2);
demo(LocalDate.of(2020, Month.JUNE, 30), ChronoUnit.MONTHS, 2);
System.out.println();
demo(LocalDate.of(2020, Month.FEBRUARY, 28), ChronoUnit.YEARS, 1);
demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 1);
demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 4);
System.out.println();
demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 1);
demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 3);
demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.WEEKS, 2);
System.out.println();
demo(LocalDate.of(2020, Month.SEPTEMBER, 4), ChronoUnit.MONTHS, 1);
When running just now, the output was:
getNextDate(2020-08-31, Months, 1) -> 2020-09-30
getNextDate(2020-08-31, Months, 2) -> 2020-10-31
getNextDate(2020-05-30, Months, 2) -> 2020-09-30
getNextDate(2020-06-30, Months, 2) -> 2020-10-30
getNextDate(2020-02-28, Years, 1) -> 2021-02-28
getNextDate(2020-02-29, Years, 1) -> 2021-02-28
getNextDate(2020-02-29, Years, 4) -> 2024-02-29
getNextDate(2020-02-29, Days, 1) -> 2020-09-04
getNextDate(2020-02-29, Days, 3) -> 2020-09-05
getNextDate(2020-02-29, Weeks, 2) -> 2020-09-12
getNextDate(2020-09-04, Months, 1) -> 2020-09-04
I should say that it agrees with your examples from the question.
If you cannot avoid having an old-fashioned Date object and an instance of your own Period enum and/or you indispensably need an old-fashioned Date back, you may wrap my method into one that performs the necessary conversions. First I would extend your enum to know its corresponding ChronoUnit constants:
enum Period {
DAY(ChronoUnit.DAYS),
WEEK(ChronoUnit.WEEKS),
MONTH(ChronoUnit.MONTHS),
YEAR(ChronoUnit.YEARS);
private final ChronoUnit unit;
private Period(ChronoUnit unit) {
this.unit = unit;
}
public ChronoUnit getUnit() {
return unit;
}
}
Now a wrapper method may look like this;
public static Date getNextDate(Date startDate, Period period, int times) {
ZoneId zone = ZoneId.of("America/Eirunepe");
LocalDate startLocalDate = startDate.toInstant().atZone(zone).toLocalDate();
LocalDate nextDate = getNextDate(startLocalDate, period.getUnit(), times);
Instant startOfDay = nextDate.atStartOfDay(zone).toInstant();
return Date.from(startOfDay);
}
Not using the decade old date api which is badly written and generally unsafe and painful to use might be the best idea. Using java.time might be in your favor here. Changing your method signature to this, is all you'd have to do:
import java.time.LocalDate;
import java.time.Period;
...
public static LocalDate getNextDate(LocalDate startDate, Period period) {
return startDate.plus(period);
}
And can then be called like:
LocalDate startDate = LocalDate.of(3, 9, 2020);
LocalDate nextDate = getNextDate(startDate, Period.ofDays(20)); // 2020-09-23
Or simply dropping your helper function in the first place and using it directly:
LocalDate nextDate = startDate.plus(Period.ofDays(20));
You can use the class Calendar to resolve your problem like that :
public static Date getNextDate(Date startDate, int period, int times) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.add(period, times);
return calendar.getTime();
}
The period is an int defined in the Calendar class, you can call your function like that :
System.out.println(getNextDate(new Date(), Calendar.MONTH, 1));
System.out.println(getNextDate(new Date(), Calendar.MONTH, 3));
System.out.println(getNextDate(new Date(), Calendar.YEAR, 1));
If you realy need to use your enum, you can do it !
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.
NOTE THIS IS NOT A DUPLICATE OF EITHER OF THE FOLLOWING
Calculating the difference between two Java date instances
calculate months between two dates in java [duplicate]
I have two dates:
Start date: "2016-08-31"
End date: "2016-11-30"
Its 91 days duration between the above two dates, I expected my code to return 3 months duration, but the below methods only returned 2 months. Does anyone have a better suggestion? Or do you guys think this is a bug in Java 8? 91 days the duration only return 2 months.
Thank you very much for the help.
Method 1:
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 2:
long daysBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 3:
I tried to use Joda library instead of Java 8 APIs, it works. it loos will return 3, It looks like Java duration months calculation also used days value. But in my case, i cannot use the Joda at my project. So still looking for other solutions.
LocalDate dateBefore= LocalDate.parse("2016-08-31");
LocalDate dateAfter = LocalDate.parse("2016-11-30");
int months = Months.monthsBetween(dateBefore, dateAfter).getMonths();
System.out.println(months);
Since you don't care about the days in your case. You only want the number of month between two dates, use the documentation of the period to adapt the dates, it used the days as explain by Jacob. Simply set the days of both instance to the same value (the first day of the month)
Period diff = Period.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(diff); //P3M
Same with the other solution :
long monthsBetween = ChronoUnit.MONTHS.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(monthsBetween); //3
Edit from #Olivier Grégoire comment:
Instead of using a LocalDate and set the day to the first of the month, we can use YearMonth that doesn't use the unit of days.
long monthsBetween = ChronoUnit.MONTHS.between(
YearMonth.from(LocalDate.parse("2016-08-31")),
YearMonth.from(LocalDate.parse("2016-11-30"))
)
System.out.println(monthsBetween); //3
Since Java8:
ChronoUnit.MONTHS.between(startDate, endDate);
//Backward compatible with older Java
public static int monthsBetween(Date d1, Date d2){
if(d2==null || d1==null){
return -1;//Error
}
Calendar m_calendar=Calendar.getInstance();
m_calendar.setTime(d1);
int nMonth1=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
m_calendar.setTime(d2);
int nMonth2=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
return java.lang.Math.abs(nMonth2-nMonth1);
}
The documentation of Period#between states the following:
The start date is included, but the end date is not.
Furthermore:
A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
Your end day-of-month 30 is not greater than or equal to your start day-of-month 31, so a third month is not considered.
Note the parameter names:
public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
To return 3 months, you can increment the endDateExclusive by a single day.
In case you want stick to java.time.Period API
As per java.time.Period documentation
Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
where
#param startDateInclusive the start date, inclusive, not null
#param endDateExclusive the end date, exclusive, not null
So it is better to adjust your implementation to make your end date inclusive and get your desired result
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30").plusDays(1));
System.out.println("Months : " + diff.getMonths());
//Output -> Months : 3
You have to be careful, never use LocalDateTime to calculate months between two dates the result is weird and incorrect, always use LocalDate !
here's is some code to prove the above:
package stack.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class TestMonthsDateTime {
public static void main(String[] args) {
/**------------------Date Time----------------------------*/
LocalDateTime t1 = LocalDateTime.now();
LocalDateTime t2 = LocalDateTime.now().minusMonths(3);
long dateTimeDiff = ChronoUnit.MONTHS.between(t2, t1);
System.out.println("diff dateTime : " + dateTimeDiff); // diff dateTime : 2
/**-------------------------Date----------------------------*/
LocalDate t3 = LocalDate.now();
LocalDate t4 = LocalDate.now().minusMonths(3);
long dateDiff = ChronoUnit.MONTHS.between(t4, t3);
System.out.println("diff date : " + dateDiff); // diff date : 3
}
}
My 2%
This example checks to see if the second date is the end of that month. If it is the end of that month and if the first date of month is greater than the second month date it will know it will need to add 1
LocalDate date1 = LocalDate.parse("2016-08-31");
LocalDate date2 = LocalDate.parse("2016-11-30");
long monthsBetween = ChronoUnit.MONTHS.between(
date1,
date2);
if (date1.isBefore(date2)
&& date2.getDayOfMonth() == date2.lengthOfMonth()
&& date1.getDayOfMonth() > date2.getDayOfMonth()) {
monthsBetween += 1;
}
After the short investigation, still not totally fix my question, But I used a dirty solution to avoid return the incorrect duration. At least, we can get the reasonable duration months.
private static long durationMonths(LocalDate dateBefore, LocalDate dateAfter) {
System.out.println(dateBefore+" "+dateAfter);
if (dateBefore.getDayOfMonth() > 28) {
dateBefore = dateBefore.minusDays(5);
} else if (dateAfter.getDayOfMonth() > 28) {
dateAfter = dateAfter.minusDays(5);
}
return ChronoUnit.MONTHS.between(dateBefore, dateAfter);
}
The Java API response is mathematically accurate according to the calendar. But you need a similar mechanism, such as rounding decimals, to get the number of months between dates that matches the human perception of the approximate number of months between two dates.
Period period = Period.between(LocalDate.parse("2016-08-31"), LocalDate.parse("2016-11-30"));
long months = period.toTotalMonths();
if (period.getDays() >= 15) {
months++;
}
This question already has answers here:
Get first Monday after certain date?
(5 answers)
Closed 6 years ago.
Hello at first i would like to note that i have found several posts with the same question here, but NONE of them worked for me. i am creating alarm clock application for android and the last thing i need is: get the date of the nearest certain day in week.
I have found several algorithms here and i will also copy one here :
import java.util.Calendar;
public class NextWednesday {
public static Calendar nextDayOfWeek(int dow) {
Calendar date = Calendar.getInstance();
int diff = dow - date.get(Calendar.DAY_OF_WEEK);
if (!(diff > 0)) {
diff += 7;
}
date.add(Calendar.DAY_OF_MONTH, diff);
return date;
}
public static void main(String[] args) {
System.out.printf(
"%ta, %<tb %<te, %<tY",
nextDayOfWeek(Calendar.WEDNESDAY) // this can contain any of the 7 days in week
);
}
}
Today is tuesday in my country
If i put wednesday in the function it returns me the wednesday that is in the next week, but thats not correct.
This algorithm automatically looks at the following week no matter if its just monday and theres whole week before you, it jumps to the next week and does its job but thats not correct, i need to implement the same behaviour but it must start from today.
Example: Today is Monday, i am looking for wednesday
Correct output: Date of wednesday in this week.
Uncorrect output: Date of wednesday in the next week.
I hope its clear enough.
Okay, algorithm works correctly, i made a simple mistake, i was passing wrong day to the function, i passed the current day, not the one that was chosen by the user
If you wish to keep it simple.
public Date getNextDate(int dayOfWeek) {
Calendar c = Calendar.getInstance();
for ( int i = 0; i < 7; i++ ) {
if ( c.get(Calendar.DAY_OF_WEEK) == dayOfWeek ) {
return c.getTime();
} else {
c.add(Calendar.DAY_OF_WEEK, 1);
}
}
return c.getTime();
}