make list full of timeslots avoiding specific timeframe - java

I am trying to make a list that by default contains a list of available timeslots from one timepoint to another timepoint. It has to avoid making timeslots in a specific timeframe.
For example: I have a first timepoint being 09:00 and the end timepoint being 17:00. The duration of the timeslots is 15. Only sometimes I can't have any time timeslots in a specific timeframe. Which,for example, can be from 12:00 to 12:30.
So eventually the slots should be filled with TimeSlots from 09:00 to 12:00. And from 12:30 to 17:00. This is my code so far, I am using java.time.LocalTime. So far, it's not going that well... Thanks for looking!
data class TimeSlot(
val startTime: LocalTime,
val endTime: LocalTime,
)
private fun initializeSlots(
slots: ArrayList<TimeSlot>,
startTimeShift: LocalTime,
appointmentDuration: Long,
amountOfWorkingHours: Long,
breakTime: LocalTime,
breakDuration: Long
) {
slots.add(TimeSlot(startTime = startTimeShift, endTime = startTimeShift.plusMinutes(appointmentDuration)))
val possibleTotalAppointments = (amountOfWorkingHours * appointmentDuration) - 2 // -2 because index starts at 0 and first timeslot is already added.
for (i in 0..(amountOfWorkingHours * appointmentDuration).toInt()) {
if (slots[i].endTime == breakTime) {
val endTimeOfBreak = breakTime.plusMinutes(breakDuration)
val isTargetWithinTimeFrame = (!breakTime.isBefore(slots[i].startTime) && breakTime.isBefore(endTimeOfBreak))
if (isTargetWithinTimeFrame) {
slots.remove(slots[i])
continue
}
} else {
slots.add(TimeSlot(startTime = slots[i].endTime, endTime = slots[i].endTime.plusMinutes(appointmentDuration)))
}
}
}

If your timeslots are static(Always from 09:00 till 17:00 and always 15 min duration) you can actually hardcode them into an array, and operate through indexes further. So for example if every hour have 4 indexes starting from 0 your specific timeframe 12:00 to 12:30. will have indexes of 12, 13, 14. Such code won't be too beautiful, but still a lot more readable.
Any way, LocalTime is overall not the best idea. I would just use Long, with System.currentTimeMillis() This is the best solution for handling timezones(Formatting them only for UI, not for logic). And also it will make your time calculations much easier, because you can actually divide and subtract millis.

I took another approach to finding the answer, so if anyone comes across the same problem, here it is. You can also give multiple timeframes you want to avoid. Feel free to alter it any way. It can also return Pairs instead of 'TimeSlot's.
fun generateTimeSlots(
startTime: LocalTime,
endTime: LocalTime,
duration: Long,
avoid: List<TimeSlot>
): List<TimeSlot> {
val timeSlots = mutableListOf<TimeSlot>()
var currentTime = startTime
while (currentTime < endTime) {
val nextTime = currentTime.plusMinutes(duration)
val timeSlot = TimeSlot(currentTime, nextTime)
if (avoid.none { timeSlot.startTime in it.startTime..it.endTime.minusMinutes(1) }) {
timeSlots.add(timeSlot)
}
currentTime = nextTime
}
return timeSlots
}

Related

How to get days remaining of a date and print hours minutes and seconds Java8

I'm getting from server an UTC date for instance
"endValidityDate": "2021-11-18T22:59:59Z"
I'd like to know what is the optimal way to calculate remaining days from now.
Here's what I got now :
I'm creating a date for 2 days from now as :
DateTime.now().plusSeconds(172800)
I'm parsing it to a DateTime joda I can use other if you say so.
When doing the diff of days I'm doing this way
val diff = endValidityDate.toDate().time - Date().time
val daysRemaining = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS)
return if (daysRemaining > 1) "$daysRemaining days}"
else TimeUnit.DAYS.convert(diff, TimeUnit.SECONDS).toString()
The scenario that I'm trying to achieve is :
If the days remaining is more than one (24h) then print "2 days remaining" for instance, and instead of showing "1 day remaining" then just add a timer as :
"0h 43m 3s".
To do the timer I'm just subtracting the time remaining with now
val expireDate = LocalDateTime.now()
.plusSeconds(uiState.endValidityDate.timeLeft.toLong())
.toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
And then on every second it happen I print it like this :
val duration = Duration.ofSeconds(it)
binding.myTextView.text = String.format(
"%02dh: %02dm: %02ds",
duration.seconds / 3600,
(duration.seconds % 3600) / 60,
duration.seconds % 60,
)
But I'm not getting the 2 days, I'm just getting as an output :
00h: 33m: 50s
So, I'm having some problems here for instance :
Is this an optimal solution? If not could you describe a better one where I can achieve my goal?
Why my timer is showing with 00h: 13m: 813s? Am I doing the regex incorrectly or it's because of epochSeconds?
To achieve
Given an UTC date from server when trying to print it to the device then it should follow this rules.
1.- If days remaining is > than 1 day then print "N days remaining"
2.- If days remaining is <= 1 then print a timer (it's already done, the thing is how to print it correctly).
Minimum 1 digit (0h 2m 1s)
Maximum 2 digits (1h 23m 3s)
Note :
I'm using Java 8
I can change also the way I'm doing the countdown to use millis instead of epochSeconds if that's the problem.
You could do that using a ZonedDateTime for now and the future datetime, and then calculate a Duration.between instead of calculating remaining seconds first and then use a Duration.ofSeconds().
Here`s a Kotlin example:
fun main() {
val utc = ZoneId.of("UTC")
val now = ZonedDateTime.now(utc)
val twoDaysFromNow = now.plusDays(2)
val remaining = Duration.between(now, twoDaysFromNow)
println(
String.format("%02dh: %02dm: %02ds",
remaining.seconds / 3600,
(remaining.seconds % 3600) / 60,
remaining.seconds % 60
)
)
}
Output: 48h: 00m: 00s
If you are interested in remaining full days only, then consider using ChronoUnit.DAYS.between, maybe like this:
fun main() {
val utc = ZoneId.of("UTC")
val now = ZonedDateTime.now(utc)
val twoDaysFromNow = now.plusDays(2)
val remainingDays = ChronoUnit.DAYS.between(now, twoDaysFromNow)
println(
String.format("%d days", remainingDays)
)
}
Output: 2 days
Additional:
Since it is unclear to me what data type you are trying to use for the calculation of the time left until end of validity, you will have to choose between providing more detailed information in your question or using one of the following funs:
Pass a ZonedDateTime
private fun getRemainingTime(endValidityDate: ZonedDateTime): String {
// get the current moment in time as a ZonedDateTime in UTC
val now = ZonedDateTime.now(ZoneId.of("UTC"))
// calculate the difference directly
val timeLeft = Duration.between(now, endValidityDate)
// return the messages depending on hours left
return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
else String.format("%02dh: %02dm: %02ds",
timeLeft.toHours(),
timeLeft.toMinutes() % 60,
timeLeft.toSeconds() % 60)
}
Pass an Instant
private fun getRemainingTime(endValidityDate: Instant): String {
// get the current moment in time, this time as an Instant directly
val now = Instant.now()
// calculate the difference
val timeLeft = Duration.between(now, endValidityDate)
// return the messages depending on hours left
return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
else String.format("%02dh: %02dm: %02ds",
timeLeft.toHours(),
timeLeft.toMinutes() % 60,
timeLeft.toSeconds() % 60)
}
Pass the String directly
private fun getRemainingTime(endValidityDate: String): String {
// get the current moment in time as a ZonedDateTime in UTC
val now = ZonedDateTime.now(ZoneId.of("UTC"))
// parse the endValidtyDate String
val then = ZonedDateTime.parse(endValidityDate)
// calculate the difference
val timeLeft = Duration.between(now, then)
// return the messages depending on hours left
return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
else String.format("%02dh: %02dm: %02ds",
timeLeft.toHours(),
timeLeft.toMinutes() % 60,
timeLeft.toSeconds() % 60)
}

java timestamps calculate overlap duration with interval

I have many timestamps (start, end) which define an interval and want to efficiently check if they overlap another single interval. If yes, compute overlap duration, otherwise return 0.
interval: 18:00 same day until 08:00 the next day.
start | end
2018-01-02 14:59:18.922|2018-01-02 14:59:38.804
2018-01-02 18:32:59.348|2018-01-02 20:30:41.192
2018-01-02 01:54:59.363|2018-01-02 01:54:59.363
2018-01-03 00:10:38.831|2018-01-03 00:11:53.103
I am unsure how to efficiently define the next day efficiently.
edit
LocalDate
has a method toInterval().overlaps(anotherInterval). I simply am unsure how to get fitting interval (18:00 - 08:00 next day) in a generic way, i.e. without manually reading the YYYMMDD and then creating a new object.
a bit similar is Find if hours ranges overlap regardless of the date
edit 2
toInterval is only present for jodatime - not java.time / JSR-310. What would be a viable way to calculate overlap duration with java.time?
edit3
A solution with jodaTime:
val begin = new DateTime(new java.sql.Timestamp().getTime())
val stop = new DateTime(new java.sql.Timestamp().getTime())
val i1 = new Interval(begin, stop)
val start = new DateTime(begin.year.get , begin.monthOfYear.get, begin.dayOfMonth.get, startHour, 0, 0, 0);
val endIntermediate =stop.toDateTime.plusDays(1)
val end = new DateTime(endIntermediate.year.get , endIntermediate.monthOfYear.get, endIntermediate.dayOfMonth.get, endHour, 0, 0, 0);
val i2 = new Interval(start, end)
val overlap = i1.overlap(i2)
val overlapDurationOrNull = overlap.toDuration
seems to work, but still is clumsy.
I believe that the following method gives you the equivalent of your Joda-Time solution.
private static final LocalTime START = LocalTime.of(18, 0);
private static final LocalTime END = LocalTime.of(8, 0);
public static Duration overlap(ZonedDateTime currentStart, ZonedDateTime currentEnd) {
ZonedDateTime singleIntervalStart = currentStart.with(START);
ZonedDateTime singleIntervalEnd = currentStart.plusDays(1).with(END);
if (currentEnd.isBefore(singleIntervalStart)) {
// no overlap
return Duration.ZERO;
}
ZonedDateTime overlapStart = currentStart.isBefore(singleIntervalStart)
? singleIntervalStart : currentStart;
ZonedDateTime overlapEnd = currentEnd.isBefore(singleIntervalEnd)
? currentEnd : singleIntervalEnd;
return Duration.between(overlapStart, overlapEnd);
}
For trying it out with the timestamps from your question I am using the following utility method:
private static void demo(String from, String to) {
ZoneId zone = ZoneId.of("Atlantic/Stanley");
Duration overlapDuration = overlap(LocalDateTime.parse(from).atZone(zone),
LocalDateTime.parse(to).atZone(zone));
System.out.println("" + from + " - " + to + ": " + overlapDuration);
}
Now I call it like this:
demo("2018-01-02T14:59:18.922", "2018-01-02T14:59:38.804");
demo("2018-01-02T18:32:59.348", "2018-01-02T20:30:41.192");
demo("2018-01-02T01:54:59.363", "2018-01-02T01:54:59.363");
demo("2018-01-03T00:10:38.831", "2018-01-03T00:11:53.103");
And the output is:
2018-01-02T14:59:18.922 - 2018-01-02T14:59:38.804: PT0S
2018-01-02T18:32:59.348 - 2018-01-02T20:30:41.192: PT1H57M41.844S
2018-01-02T01:54:59.363 - 2018-01-02T01:54:59.363: PT0S
2018-01-03T00:10:38.831 - 2018-01-03T00:11:53.103: PT0S
In the first example 14:59 is before 18:00, so the result is an overlap of 0. In the second example the whole interval is counted as overlap (nearly 2 hours). Note that in the last two examples no overlap is reported because the the times are many hours before 18:00. I am unsure whether this is what you wanted since the times are also before 08:00.
You can simply use LocalDate.plusDays to add a day.
Assuming an iteration where the following are to be compared:
LocalDateTime d1 = LocalDateTime.parse("2018-01-02T14:59:18"),
d2 = LocalDateTime.parse("2018-01-02T14:59:38");
You can create the 18:00 and 08:00 date/time objects using:
LocalDateTime start = LocalDateTime.of(d1.toLocalDate(), LocalTime.of(18, 0));
LocalDateTime end = LocalDateTime.of(d1.toLocalDate().plusDays(1),
LocalTime.of(8, 0));
I've assumed that 18:00 is on the same day as d1.

Loop through time difference logic

I am using Joda DateTime and have 2 dates :
DateTime old //which is 1:46PM
DateTime new //which is 6:46PM
note: excluded the dates.
How may i be able to loop through the difference in this order :
(print a message for the first half hour, another message for the next 30 minutes and another message per subsequent hour) ?
I was thinking of Subtracting the old date from the new date then make a loop but i don't get the logic. Any pointers will be helpful.
example
If i subtract both times above, i will have an elapsed time of 5 hours.
Loop 5 hours
{
for the first hour (print this)
next 30minutes (print that)
every subsequent hour (print ....)
}
I would use the type LocalTime instead. If you have DateTime as input then please convert it using the method toLocalTime() (with same time and chronology and timezone).
LocalTime start = new LocalTime(13, 46);
LocalTime end = new LocalTime(18, 46);
LocalTime current = start;
for (int i = 0; current.isBefore(end); i++) {
// code your print action here
current = current.plusMinutes((i < 2) ? 30 : 60);
}
Then you get an action for following times:
13:46
14:16
14:46
15:46
16:46
17:46

Test a date within a day intervall range

I have a date and a number and want to check if this date and this number occurs in a list of other dates within:
+-20 date intervall with the same number
so for example 1, 1.1.2013 and 1,3.1.2013 should reuturn false.
I tried to implement the method something like that:
private List<EventDate> dayIntervall(List<EventDate> eventList) throws Exception {
List<EventDate> resultList = new ArrayList<EventDate>();
for (int i = 0; i < eventList.size(); i++) {
String string = eventList.get(i).getDate();
Date equalDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string);
for (int j = 0; j < eventList.size(); j++) {
String string1 = eventList.get(i).getDate();
Date otherDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string1);
if (check number of i with number of j && check Date) {
//do magic
}
}
}
return resultList;
}
The construction of the iteration method is not that hard. What is hard for me is the date intervall checking part. I tried it like that:
boolean isWithinRange(Date testDate, Date days) {
return !(testDate.before(days) || testDate.after(days));
}
However that does not work because days are not takes as days. Any suggestions on how to fix that?
I really appreciate your answer!
You question is difficult to follow. But given its title, perhaps this will help…
Span Of Time In Joda-Time
The Joda-Time library provides a trio of classes to represent a span of time: Interval, Period, and Duration.
Interval
An Interval object has specific endpoints that lie on the timeline of the Universe. A handy contains method tells if a DateTime object occurs within those endpoints. The beginning endpoint in inclusive while the last endpoint is exclusive.
Time Zones
Note that time zones are important, for handling Daylight Saving Time and other anomalies, and for handling start-of-day. Keep in mind that while a java.util.Date seems like it has a time zone but does not, a DateTime truly does know its own time zone.
Sample Code
Some code off the top of my head (untested)…
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Berlin" );
DateTime dateTime = new DateTime( yourDateGoesHere, timeZone );
Interval interval = new Interval( dateTime.minusDays( 20 ), dateTime.plusDays( 20 ) );
boolean didEventOccurDuringInterval = interval.contains( someOtherDateTime );
Whole Days
If you want whole days, call the withTimeAtStartOfDay method to get first moment of the day. In this case, you probably need to add 21 rather than 20 days for the ending point. As I said above, the end point is exclusive. So if you want whole days, you need the first moment after the time period you care about. You need the moment after the stroke of midnight. If this does not make sense, see my answers to other questions here and here.
Note that Joda-Time includes some "midnight"-related methods and classes. Those are no longer recommended by the Joda team. The "withTimeAtStartOfDay" method takes their place.
DateTime start = dateTime.minusDays( 20 ).withTimeAtStartOfDay();
DateTime stop = dateTime.plusDays( 21 ).withTimeAtStartOfDay(); // 21, not 20, for whole days.
Interval interval = new Interval( start, stop );
You should avoid java.util.Date if at all possible. Using the backport of ThreeTen (the long awaited replacement date/time API coming in JDK8), you can get the number of days between two dates like so:
int daysBetween(LocalDate start, LocalDate end) {
return Math.abs(start.periodUntil(end).getDays());
}
Does that help?
You can get the number of dates in between the 2 dates and compare with your days parameter. Using Joda-Time API it is relatively an easy task: How do I calculate the difference between two dates?.
Code:
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN);
Date startDate = format.parse("1.1.2013");
Date endDate = format.parse("3.1.2013");
Days d = Days.daysBetween(new DateTime(startDate), new DateTime(endDate));
System.out.println(d.getDays());
Gives,
2
This is possible using Calendar class as well:
Calendar cal = Calendar.getInstance();
cal.setTime(startDate);
System.out.println(cal.fieldDifference(endDate, Calendar.DAY_OF_YEAR));
Gives,
2
This 2 can now be compared to your actual value (20).

Joda - Determine if current time is in a specified weekly time period

I have orders that come in it at various times during the week and I have a set of rules for when we can and cannot accept orders. For example, we don't accept orders between 4:45pm and 5:15pm on Wednesday. So if an order came in at 3pm, everything is fine. If an order came in at 5pm we would need to reject it. These rules are based on the day of the week and change daily.
My question, is using joda time, what is the best way to check if the current time is in this time window?
I'm open to other technologies, however I'm currently using joda time through the scala tools time wrapper.
I'm currently working with something like this:
val now = DateTime.now
val wednesdayNoTradeInterval:Interval = ??
now.getDayOfWeek match {
case WEDNESDAY => wednesdayNoTradeInterval.contains(now)
}
You might try something like this:
implicit def dateTimeToLocalTime(dateTime: DateTime) = dateTime.toLocalTime
type Interval[T] = (T, T)
def during(interval: Interval[LocalTime])(time: LocalTime) = interval match {
case (start, end) => start < time && time < end
}
def day(day: Int)(time: DateTime) = time.dayOfWeek.get == day
val fourish = new LocalTime(16, 45)
val afternoon = during((fourish, (fourish + (30 minutes)) )) _
val wednesday = day(WEDNESDAY) _
val thursday = day(THURSDAY) _
def acceptingOrders = !{
val now = DateTime.now
wednesday(now) && afternoon(now)
thursday(now) && afternoon(now)
}
And use it like this:
acceptingOrders // ?
For each day of the week, store a list of pairs of LocalTime instances between which orders are rejected.
Then when an order is placed at a given DateTime, get the pairs associated with the dateTime's day of week, transform the DateTime into a LocalTime, and check if this LocalTime is contained between the start and end of at least one pair.
Check out the Span class in JChronic: https://github.com/samtingleff/jchronic/blob/master/src/main/java/com/mdimension/jchronic/utils/Span.java
It would be trivial to create something similar and do a !<> check on the time in millis.

Categories