Related
This question already has answers here:
Calculate number of weekdays between two dates in Java
(20 answers)
Closed 1 year ago.
Am very beginner and new to Java platform. I have the below 3 simple Java date difference calculation functions. I wanted to exclude weekends on the below calculations in all the 3 methods. Can anyone please help how to exclude weekends for the below dateDiff calculations?
public static String getDatesDiff(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
timeDiff = ""+diff;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDays(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = ""+(diff / (24 * 60 * 60 * 1000));
timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
timeDiff = timeDiff+" days";
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDate(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = (diff / (24 * 60 * 60 * 1000))+" days";
String hours = (diff / (60 * 60 * 1000) % 24)+"h";
String minutes = (diff / 1000 % 60)+"mts";
String seconds = (diff / (60 * 1000) % 60)+"sec";
timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
This code is fundamentally broken. java.util.Date doesn't represent a date, it represents a timestamp. But if you're working with moments in time, you have a problem: not all days are exactly 24 hours long. For example, daylight savings exists, making some days 25 or 23 hours. At specific moments in time in specific places on the planet, entire days were skipped, such as when a place switches which side of the international date line it is on, or when Russia was the last to switch from Julian to Gregorian (the famed October Revolution? Yeah, that happened in November actually!)
Use LocalDate which represents an actual date, not a timestamp. Do not use Date, or SimpleDateFormat – these are outdated and mostly broken takes on dates and times. The java.time package is properly thought through.
When is 'the weekend'? In some places, Friday and Saturday are considered the weekend, not Saturday and Sunday.
If you're excluding weekends, presumably you'd also want to exclude mandated holidays. Many countries state that Jan 1st, regardless of what day that is, counts as a Sunday, e.g. for the purposes of government buildings and services being open or not.
Lessons you need to take away from this:
Dates are incredibly complicated, and as a consequence, are a horrible idea for teaching basic principles.
Do not use java.util.Date, Calendar, GregorianCalendar, or SimpleDateFormat, ever. Use the stuff in java.time instead.
If you're writing math like this, you're probably doing it wrong – e.g. ChronoUnit.DAYS.between(date1, date2) does all that math for you.
You should probably just start at start date, and start looping: check if that date counts as a working day or not (and if it is, increment a counter), then go to the next day. Keep going until the day is equal to the end date, and then return that counter. Yes, this is 'slow', but a computer will happily knock through 2 million days (that covers over 5000 years worth) in a heartbeat for you. The advantage is that you can calculate whether or not a day counts as a 'working day' (which can get incredibly complicated. For example, most mainland European countries and I think the US too mandates that Easter is a public holiday. Go look up and how to know when Easter is. Make some coffee first, though).
If you really insist on going formulaic and defining weekends as Saturday and Sunday, it's better to separately calculate how many full weeks are between the two dates and multiply that by 5, and then add separately the half-week 'on the front of the range' and the half-week at the back. This will be fast even if you ask for a hypothetical range of a million years.
That is not how you handle exceptions. Add throws X if you don't want to deal with it right now, or, put throw new RuntimeException("unhandled", e); in your catch blocks. Not this, this is horrible. It logs half of the error and does blindly keeps going, with invalid state.
Almost all interesting questions, such as 'is this date a holiday?' are not answerable without knowing which culture/locale you're in. This includes seemingly obvious constants such as 'is Saturday a weekend day?'.
rzwitserloot has already brought up many valid points about problems in your code.
This is an example of how you could count the working days:
LocalDate startDate = ...;
LocalDate endDateExclusive = ...;
long days = startDate.datesUntil(endDateExclusive)
.filter(date -> isWorkingDay(date))
.count();
And, of course, you need to implement the isWorkingDay method. An example would be this:
public static boolean isWorkingDay(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY);
}
I used LocalDate to illustrate the example. LocalDate fits well if you are working with concepts like weekend days and holidays. However, if you want to also include the time component, then you should also take clock adjustments like DST into account; otherwise a "difference" does not make sense.
I assume the user to input an object representing some datetime value, not a String. The parsing of a string does not belong to this method, but should be handled elsewhere.
Already been said, but I repeat: don't use Date, Calendar and SimpleDateFormat. They're troublesome. Here are some reasons why.
If you want to take the time into consideration, it'll get a little more complex. For instance, ChronoUnit.DAYS.between(date1, date2) only supports a single, contiguous timespan. Gaps in the timespan, like excluding certain periods of time, is not. Then you have to walk over each date and get the associated duration of that portion of date.
First, we could create a LocalTimeRange class, which represents a time span at a certain day.
public record LocalTimeRange(LocalTime start, LocalTime endExclusive) {
public static final LocalTimeRange EMPTY = new LocalTimeRange(null, null);
public Duration toDuration(LocalDate date, ZoneId zone) {
if (this.equals(EMPTY)) {
return Duration.ZERO;
}
var s = ZonedDateTime.of(date, Objects.requireNonNullElse(start, LocalTime.MIN), zone);
var e = (endExclusive != null ? ZonedDateTime.of(date, endExclusive, zone) : ZonedDateTime.of(date.plusDays(1), LocalTime.MIN, zone));
return Duration.between(s, e);
}
}
Calculations are not done immediately, because the duration in between the two wall clock times, depends on the date and timezone. The toDuration method calculates this.
Then we'll create a method which defines what times on each day are counted as a non-weekend day. In this example, I have defined a weekend to be from Friday, 12:00 (noon) until Sunday, 23:59 (midnight).
private static Duration nonWeekendHours(LocalDate date, ZoneId zone) {
var result = switch (date.getDayOfWeek()) {
case MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY -> new LocalTimeRange(LocalTime.MIDNIGHT, null);
case FRIDAY -> new LocalTimeRange(LocalTime.MIDNIGHT, LocalTime.NOON);
case SATURDAY,
SUNDAY -> new LocalTimeRange(null, null);
};
return result.toDuration(date, zone);
}
The LocalTimeRange::toDuration method is called with the passed LocalDate and ZoneId arguments.
Note that passing null as LocalTimeRange's second argument means 'until the end of the day'.
At last we could stream over all dates of a certain period and calculate how much time are the non-weekend hours for each day, and then reduce them to get the total amount of time:
LocalDate startDate = ...;
LocalDate endDate = ...;
ZoneId zone = ...;
Duration result = startDate.datesUntil(endDate)
.map(date -> nonWeekendHours(date, zone))
.reduce(Duration.ZERO, Duration::plus);
With the retrieved Duration instance, you can easily get the time parts with the get<unit>Part() methods,
Online demo
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 want to create an interval between the beginning of the week, and the end of the current week.
I have the following code, borrowed from this answer:
private LocalDateTime calcNextSunday(LocalDateTime d) {
if (d.getDayOfWeek() > DateTimeConstants.SUNDAY) {
d = d.plusWeeks(1);
}
return d.withDayOfWeek(DateTimeConstants.SUNDAY);
}
private LocalDateTime calcPreviousMonday(LocalDateTime d) {
if (d.getDayOfWeek() < DateTimeConstants.MONDAY) {
d = d.minusWeeks(1);
}
return d.withDayOfWeek(DateTimeConstants.MONDAY);
}
But now I want the Monday LocalDateTime to be at 00:00:00, and the Sunday LocalDateTime at 23:59:59. How would I do this?
You can use the withTime method:
d.withTime(0, 0, 0, 0);
d.withTime(23, 59, 59, 999);
Same as Peter's answer, but shorter.
also a simple way is
d.millisOfDay().withMaximumValue();
How about:
private LocalDateTime calcNextSunday(LocalDateTime d) {
return d.withHourOfDay(23).withMinuteOfHour(59).withSecondOfMinute(59).withDayOfWeek(DateTimeConstants.SUNDAY);
}
private LocalDateTime calcPreviousMonday(final LocalDateTime d) {
return d.withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withDayOfWeek(DateTimeConstants.MONDAY);
}
With Kotlin you could write an extension function:
fun DateTime.withTimeAtEndOfDay() : DateTime = this.withTime(23,59,59,999)
This would allow you to write:
d.withDayOfWeek(DateTimeConstants.SUNDAY).withTimeAtEndOfDay()
For those coming here looking for the answer for "js-joda", you have two options depending on what you're looking to accomplish
Option 1: You want the start of the day in the same timezone
Since you've chosen to calculate your times based on an instant in time in relation to a timezone, you should use ZonedDateTime:
import { ZonedDateTime, LocalDate, ZoneId, DateTimeFormatter} from "js-joda";
import 'js-joda-timezone';
const nowInNewYorkCity = ZonedDateTime.now(ZoneId.of("America/New_York"))
const startOfTodayInNYC = nowInNewYorkCity.truncatedTo(ChronoUnit.DAYS);
console.log(startOfTodayInNYC.toString()) // Prints "2019-04-15T00:00-04:00[America/New_York]"
// And if you want to print it in ISO format
console.log(startOfTodayInNYC.format(DateTimeFormatter.ISO_INSTANT)) // "2019-04-14T04:00:00Z"
Option 2: You know the exact day that you want to get the time for
Then you can use the following methods off of LocalDate to derive the relative time (i.e. ZonedDateTime) you'd like:
atStartOfDay(): LocalDateTime
atStartOfDay(zone: ZoneId): ZonedDateTime
atStartOfDayWithZone(zone: ZoneId): ZonedDateTime
Option 3: I want just the day the instant occurred on
Notice with this code, you get the day that it would be relative to where you are. So for those in New York City, it's "2019-04-14" and for those in London it would be "2019-04-15" (which is great!) because the instant in time was during the period of time where it's actually tomorrow in London ("2019-04-15T00:00:05Z"). Pretend that you were calling someone in London from NYC, and the Londoner would say, "geez, why are you calling me so early... it's 5 seconds past midnight."
import { ZonedDateTime, LocalDate, ZoneId} from "js-joda";
import 'js-joda-timezone';
const aTimeWhenLondonIsAlreadyInTomorrow = "2019-04-15T00:00:05.000Z";
const inBetweenTimeInLondon = ZonedDateTime.parse(aTimeWhenLondonIsAlreadyInTomorrow);
const inBetweenTimeInNYC = inBetweenTimeInLondon.withZoneSameInstant(ZoneId.of("America/New_York"))
const dayInLondon = inBetweenTimeInLondon.toLocalDate();
const dayInNYC = inBetweenTimeInNYC.toLocalDate();
console.log(inBetweenTimeInLondon.toString()); // "2019-04-15T00:00:05Z"
console.log(dayInLondon.toString()); // "2019-04-15"
console.log(inBetweenTimeInNYC.toString()) // "2019-04-14T20:00:05-04:00[America/New_York]"
console.log(dayInNYC.toString()); // "2019-04-14"
References: https://js-joda.github.io/js-joda/class/src/LocalDate.js~LocalDate.html#instance-method-atStartOfDayWithZone
begin = d
// Go to previous or same Sunday
.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY))
// Beginning of day
.truncatedTo(ChronoUnit.DAYS)
end = d
// Go to next Sunday
.with(TemporalAdjusters.next(DayOfWeek.SUNDAY))
// Beginning of day
.truncatedTo(ChronoUnit.DAYS)
I also think it is a bad idea to represent the end of week interval with small amount of time before the actual, exclusive end. It is better to treat begin as inclusive, and end as exclusive instead (when doing comparisons etc.).
Is there a good way to get the date of the coming Wednesday?
That is, if today is Tuesday, I want to get the date of Wednesday in this week; if today is Wednesday, I want to get the date of next Wednesday; if today is Thursday, I want to get the date of Wednesday in the following week.
Thanks.
The basic algorithm is the following:
Get the current date
Get its day of week
Find its difference with Wednesday
If the difference is not positive, add 7 (i.e. insist on next coming/future date)
Add the difference
Here's a snippet to show how to do this with java.util.Calendar:
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)
);
}
}
Relative to my here and now, the output of the above snippet is "Wed, Aug 18, 2010".
API links
java.util.Calendar
java.util.Formatter - for the formatting string syntax
tl;dr
LocalDate // Represent a date-only value, without time-of-day and without time zone.
.now() // Capture the current date as seen in the wall-clock time used by the people of a specific region (a time zone). The JVM’s current default time zone is used here. Better to specify explicitly your desired/expected time zone by passing a `ZoneId` argument. Returns a `LocalDate` object.
.with( // Generate a new `LocalDate` object based on values of the original but with some adjustment.
TemporalAdjusters // A class that provides some handy pre-defined implementations of `TemporalAdjuster` (note the singular) interface.
.next( // An implementation of `TemporalAdjuster` that jumps to another date on the specified day-of-week.
DayOfWeek.WEDNESDAY // Pass one of the seven predefined enum objects, Monday-Sunday.
) // Returns an object implementing `TemporalAdjuster` interface.
) // Returns a `LocalDate` object.
Details
Using Java8 Date time API you can easily find the coming Wednesday.
LocalDate nextWed = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
next(DayOfWeek dayOfWeek) - Returns the next day-of-week adjuster, which adjusts the date
to the first occurrence of the specified day-of-week after the date
being adjusted.
Suppose If you want to get previous Wednesday then,
LocalDate prevWed = LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));
previous(DayOfWeek dayOfWeek) - Returns the previous day-of-week adjuster, which adjusts
the date to the first occurrence of the specified day-of-week before
the date being adjusted.
Suppose If you want to get next or current Wednesday then
LocalDate nextOrSameWed = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY));
nextOrSame(DayOfWeek dayOfWeek) - Returns the next-or-same day-of-week
adjuster, which adjusts the date to the first occurrence of the
specified day-of-week after the date being adjusted unless it is
already on that day in which case the same object is returned.
Edit:
You can also pass ZoneId to get the current date from the system clock in the specified time-zone.
ZoneId zoneId = ZoneId.of("UTC");
LocalDate nextWed = LocalDate.now(zoneId).with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
For more information refer TemporalAdjusters
Using JodaTime:
LocalDate date = new LocalDate(System.currentTimeMillis());
Period period = Period.fieldDifference(date, date.withDayOfWeek(DateTimeConstants.WEDNESDAY));
int days = period.getDays();
if (days < 1) {
days = days + 7;
}
System.out.println(date.plusDays(days));
Calendar c= Calendar.getInstance();
c.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
c.add(Calendar.DAY_OF_MONTH, 7);
c.getTime();
Use java.util.Calendar. You get the current date/time like this:
Calendar date = Calendar.getInstance();
From there, get date.get(Calendar.DAY_OF_WEEK) to get the current day of week and get the difference to Calendar.WEDNESDAY and add it.
public static void nextWednesday() throws ParseException
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
int weekday = calendar.get(Calendar.DAY_OF_WEEK);
int days = Calendar.WEDNESDAY - weekday;
if (days < 0)
{
days += 7;
}
calendar.add(Calendar.DAY_OF_YEAR, days);
System.out.println(sdf.format(calendar.getTime()));
}
This question already has answers here:
How do I check if a date is within a certain range?
(17 answers)
Closed 7 years ago.
One thing I want to know is how to calculate what date will it be 10 days from today.
Second thing is to check if one Date is between two other Dates.
For example, let's say I have an app that shows what events I need to do in the next 10 days (planner). Now how can I see if the date I assigned to an event is between today and the date that is 10 days from today?
Manipulating and comparing dates using java.util.Date and java.util.Calendar is pretty a pain, that's why JodaTime exist. None of the answers as far have covered the time in question. The comparisons may fail when the dates have a non-zero time. It's also unclear whether you want an inclusive or exclusive comparison. Most of the answers posted so far suggest exclusive comparision (i.e. May 24 is not between May 20 and May 24) while in real it would make more sense to make it inclusive (i.e. May 24 is between May 20 and May 24).
One thing I want to know is how to calculate what date will it be 10 days from today.
With the standard Java SE 6 API, you need java.util.Calendar for this.
Calendar plus10days = Calendar.getInstance();
plus10days.add(Calendar.DAY_OF_YEAR, 10);
With JodaTime you would do like this:
DateTime plus10days = new DateTime().plusDays(10);
Second thing is to check if one Date is between two other Dates. For example, let's say I have an app that shows what events I need to do in the next 10 days (planner). Now how can I see if the date I assigned to an event is between today and the date that is 10 days from today?
Now comes the terrible part with Calendar. Let's prepare first:
Calendar now = Calendar.getInstance();
Calendar plus10days = Calendar.getInstance();
plus10days.add(Calendar.DAY_OF_YEAR, 10);
Calendar event = Calendar.getInstance();
event.set(year, month - 1, day); // Or setTime(date);
To compare reliably using Calendar#before() and Calendar#after(), we need to get rid of the time first. Imagine it's currently 24 May 2010 at 9.00 AM and that the event's date is set to 24 May 2010 without time. When you want inclusive comparison, you would like to make it return true at the same day. I.e. both the (event.equals(now) || event.after(now)) or -shorter but equally- (!event.before(now)) should return true. But actually none does that due to the presence of the time in now. You need to clear the time in all calendar instances first like follows:
calendar.clear(Calendar.HOUR);
calendar.clear(Calendar.HOUR_OF_DAY);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Alternatively you can also compare on day/month/year only.
if (event.get(Calendar.YEAR) >= now.get(Calendar.YEAR)
&& event.get(Calendar.MONTH) >= now.get(Calendar.MONTH)
&& event.get(Calendar.DAY_OF_MONTH) >= now.get(Calendar.DAY_OF_MONTH)
{
// event is equal or after today.
}
Very verbose all.
With JodaTime you can just use DateTime#toLocalDate() to get the date part only:
LocalDate now = new DateTime().toLocalDate();
LocalDate plus10days = now.plusDays(10);
LocalDate event = new DateTime(year, month, day, 0, 0, 0, 0).toLocalDate();
if (!event.isBefore(now) && !event.isAfter(plus10days)) {
// Event is between now and 10 days (inclusive).
}
Yes, the above is really all you need to do.
public static boolean between(Date date, Date dateStart, Date dateEnd) {
if (date != null && dateStart != null && dateEnd != null) {
if (date.after(dateStart) && date.before(dateEnd)) {
return true;
}
else {
return false;
}
}
return false;
}
EDIT: Another suggested variant:
public Boolean checkDate(Date startDate, Date endDate, Date checkDate) {
Interval interval = new Interval(new DateTime(startDate),
new DateTime(endDate));
return interval.contains(new DateTime(checkDate));
}
Use JodaTime calendar replacement classes: http://joda-time.sourceforge.net/
You can use before, after and compareTo methods of Date class.
Here're some examples
http://www.roseindia.net/java/example/java/util/CompareDate.shtml
http://www.javafaq.nu/java-example-code-287.html
http://www.esus.com/javaindex/j2se/jdk1.2/javautil/dates/comparingdates.html
And here's API on Date class
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Date.html
Good Luck!
To add ten days:
Date today = new Date();
Calendar cal = new GregorianCalendar();
cal.setTime(today);
cal.add(Calendar.DAY_OF_YEAR, 10);
To check if between two dates:
myDate.after(firstDate) && myDate.before(lastDate);
To check if date is between two dates, here is simple program:
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
String oeStartDateStr = "04/01/";
String oeEndDateStr = "11/14/";
Calendar cal = Calendar.getInstance();
Integer year = cal.get(Calendar.YEAR);
oeStartDateStr = oeStartDateStr.concat(year.toString());
oeEndDateStr = oeEndDateStr.concat(year.toString());
Date startDate = sdf.parse(oeStartDateStr);
Date endDate = sdf.parse(oeEndDateStr);
Date d = new Date();
String currDt = sdf.format(d);
if((d.after(startDate) && (d.before(endDate))) || (currDt.equals(sdf.format(startDate)) ||currDt.equals(sdf.format(endDate)))){
System.out.println("Date is between 1st april to 14th nov...");
}
else{
System.out.println("Date is not between 1st april to 14th nov...");
}
}
I took the initial answer and modified it a bit. I consider if the dates are equal to be "inside"..
private static boolean between(Date date, Date dateStart, Date dateEnd) {
if (date != null && dateStart != null && dateEnd != null) {
return (dateEqualOrAfter(date, dateStart) && dateEqualOrBefore(date, dateEnd));
}
return false;
}
private static boolean dateEqualOrAfter(Date dateInQuestion, Date date2)
{
if (dateInQuestion.equals(date2))
return true;
return (dateInQuestion.after(date2));
}
private static boolean dateEqualOrBefore(Date dateInQuestion, Date date2)
{
if (dateInQuestion.equals(date2))
return true;
return (dateInQuestion.before(date2));
}