I am trying to get the Duration until the next time, depending on a Day of Week in Java.
I want to be able to schedule a Java task for the next day in a list, at a specific time, so for example, with a list of Monday and Thursday, and it is Sunday, I want to get the seconds until the Monday at a specific time based on hours and minutes passed into the method.
This means that if the current day is Sunday at 10:00AM, and I pass in the time 09:00, I want to go to the next day in the list.
Examples:
daysToFire = ['Monday', 'Wednesday', 'Friday', 'Sunday']
hours = 10
minutes = 10
- On Monday 11:00, I want to get the Duration until Wednesday 10:10
- On Monday 09:00 I want to get the Duration until Monday 10:10
- On Sunday 20:00 I want to get the Duration until Monday 10:10
- On Wednesday 13:00 I want to get the Duration until Friday 10:10
My current method always goes out at the last return.
private Duration getDurationUntilNext(int hours, int minutes) {
final Instant instant = Instant.now(Clock.system(timeZone));
final DayOfWeek currentDay = instant.atZone(timeZone).getDayOfWeek();
final ZonedDateTime currentZonedDateTime = instant.atZone(timeZone);
for(DayOfWeek dayOfWeek : daysToFire) {
if(currentDay == dayOfWeek) {
final ZonedDateTime nextTime = currentZonedDateTime
.with(TemporalAdjusters.nextOrSame(currentDay))
.withHour(hours)
.withMinute(minutes)
.withSecond(0);
if(nextTime.isAfter(currentZonedDateTime))
return Duration.between(currentZonedDateTime, nextTime);
}
}
return Duration.ZERO;
}
If anyone can help, that would be very much appreciated. Thank you all for the help already!
CodePudding user response:
By the way, you could shorten Instant.now(Clock.system(timeZone))
to Instant.now()
.
Define the time zone by which you want to recognize date and time.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Capture the current moment as seen in that zone.
ZonedDateTime now = ZonedDateTime.now( z ) ;
Define your list of day-of-week.
List< DayOfWeek > dows = List.of( DayOfWeek.MONDAY , DayOfWeek.THURSDAY ) ;
Specify your target time of day.
LocalTime targetTime = LocalTime.of( 10 , 0 ) ;
For each day of week, adjust the current moment to that day of week at that time.
TemporalAdjuster ta = TemporalAdjusters.nextOrSame( dow ) ;
ZonedDateTime zdt = now.with( ta ).with( targetTime ) ;
Be aware that your target time may not exist on that date in that zone. A further adjustment will be made automatically as needed.
Calculate duration.
Duration d = Duration.between ( now , zdt ) ;
Test to see if negative. A negative duration means we are going backward in time, means zdt
came before now
. When encountering a negative duration, we need to use a temporal adjuster of next
rather than nextOrSame
.
if( d.isNegative() ) { // If zdt is in the past …
zdt = zdt.with( TemporalAdjusters.next( dow ) ).with( targetTime ) ;
}
Notice that java.time uses immutable objects. So zdt.with
generates a new fresh instance of ZonedDateTime
rather than altering (“mutating”) the original.
Recalculate the duration.
d = Duration.between ( now , zdt ) ;
I ignored your desired ten minute gap, as I do not understand what you are doing there exactly, and it seems like a minor distraction. You can add ten minutes where needed along the process I outlined above.
CodePudding user response:
Based on your code you are checking if currentDay is from available list of days. If its not, then you are simply skipping your logic. I am suggesting you below --
for (DayOfWeek dayOfWeek : daysToFire) {
if (currentDay == dayOfWeek) {
final ZonedDateTime nextTime = currentZonedDateTime.with(TemporalAdjusters.nextOrSame(currentDay))
.withHour(hours).withMinute(mins).withSecond(0);
if (nextTime.isAfter(currentZonedDateTime))
return Duration.between(currentZonedDateTime, nextTime);
return Duration.between(nextTime, currentZonedDateTime);
} else if (currentDay.getValue() < dayOfWeek.getValue()) {
final ZonedDateTime nextTime = currentZonedDateTime.with(TemporalAdjusters.nextOrSame(dayOfWeek))
.withHour(hours).withMinute(mins).withSecond(0);
return Duration.between(currentZonedDateTime, nextTime);
} else
continue;
}
return Duration.ZERO;
}
Please let us know if this fulfils your requirement. Thanks.