How to manipulate Ranges of Time of a Day - java

I'm working with an agenda in Java. I have stored in my database the day of the week, the start and end time of some labs availability.
Now I need to provide a service for a schedule system by showing only the unavailable times of the day. For example, if day one has start time 13:00 and end time 19:00, I need to return a range just like this:
[00:00 - 13:00, 19:00 - 23:59] . Remembering that a day can have more than a range available.
Is there any Java Class or API that could help me on subtracting these ranges?

My lib Time4J offers following solution for the subtraction problem:
ClockInterval fullDay = ClockInterval.between(PlainTime.of(0), PlainTime.of(24));
ClockInterval slot = ClockInterval.between(PlainTime.of(13, 0), PlainTime.of(19, 0));
IntervalCollection<PlainTime> icoll = IntervalCollection.onClockAxis().plus(fullDay);
List<ChronoInterval<PlainTime>> result = icoll.minus(slot).getIntervals();
The resulting list of half-open intervals (with open end) can be easily iterated through and gives the expected result {[T00:00/T13:00), [T19:00/T24:00)}. Every result interval can be converted to a standard ClockInterval, too. There are also various methods to print such intervals in a localized way. Furthermore, you might find the class DayPartitionBuilder interesting which allows to connect weekdays and time schedules in streaming, see the example given in the documentation.
About compatibility with java.time:
The between()-methods of ClockInterval also accept instances of java.time.LocalTime.
Every instance of PlainTime can be converted back to LocalTime by help of the method toTemporalAccessor() with the exception of the value 24:00 which exists in Time4J but not in java.time.LocalTime.

Related

How many days from now is the Duration?

I have a Duration, like P3M (3 months). How can I get number of days it is from now?
All I have now is this:
Duration.parseWeekBasedPeriod("P3M")
I know the period parameter will never be shorter than 1 week, so parseWeekBasedPeriod() should be ok. But I'm reading JavaDoc, and I can't figure out how to get those days.
I understand, the problem is that months can has 31, 30, 29 and 28 days.
Using parseWeekBasedPeriod(...) is certainly wrong if you want to apply durations measured in months. This very special method handles week based years which can last either 364 or 371 days (52 or 53 weeks). So I suggest just to use the standard parsing method for calendar-based durations. The following code also strongly simplifies the evaluation of days between two dates (see last line).
Duration<CalendarUnit> duration = Duration.parseCalendarPeriod("P3M");
PlainDate today = PlainDate.nowInSystemTime();
PlainDate later = today.plus(duration);
long days = CalendarUnit.DAYS.between(today, later);
By the way, I have tested the method for weekbased durations once again. It will usually throw an exception if it tries to parse months. You didn't seem to have seen any exception so I assume that the fact that you use untyped constructs like "val" has shadowed the necessary type information in processing the duration string (and Time4J is a strongly typed library). Hence - if technically possible for you -, I strongly recommend to use type-safe code as shown in my solution.
Finaly figured it out:
val period = Duration.parseWeekBasedPeriod("P3M")
val start = PlainDate.nowInSystemTime()
val end = start.plus(period)
val days: Long = Duration.`in`(CalendarUnit.DAYS).between(start, end).getPartialAmount(CalendarUnit.DAYS)

How to specify date and duration in one expression

I am trying to find notation for periodic event in ISO8601 format.
Can it be done as one expression?
For example, event starting at March 31 2017 at 17:25 with periodicity 1 hour:
2017-03-31T17:25:00Z/PT1H
Trying to parse this expression to Duration in Java:
java.time.Duration d = java.time.Duration.parse("2017-03-31T17:25:00Z/PT1H");
results in exception.
In Iso-8601, the expression "2017-03-31T17:25:00Z/PT1H" is not a recurrent interval but just a normal instant/moment-interval. If you look for recurrent intervals then you have to prefix it with "Rn/" where "n" is a positive integer (optional). Standard Java has no support for intervals so you have to either code your own workaround with string processing and partial parsing or you use a 3rd-party library like my one (Time4J):
MomentInterval interval = MomentInterval.parseISO("2017-03-31T17:25:00Z/PT1H");
System.out.println(interval); // [2017-03-31T17:25:00Z/2017-03-31T18:25:00Z)
See also the Javadoc for moment intervals. For recurrent intervals please have a look at this class IsoRecurrence.
Interoperability note: You can easily convert the Moment-components to java.time.Instant via the method toTemporalAccessor().

Print a Duration in human readable format by EL

First of all I'm new to java.time package.
I'm writing a webapp that need to work with specific times of the day and with several durations of events.
So I wrote my code using LocalTime and Duration classes of java.time package.
When I need to render their value in JSP it is very simple for LocalTime object (because .toString() returns a human readable vale), so I can just write ${startTime} and everything goes in the right way (e.g. it is rendered as 9:00).
The same approach doesn't work for Duration, because its representation is something like PT20M (in this case for 20 minutes).
Does it exist an elegant way to perform a human-readable conversion in JSP directly by EL?
Yes, I know I can convert the object in a string in my classes (before JSP), but I'm searching for the suggested approach (that I'm not able to find)... another point is that I not see an official "convert()" (or whatever else) method in Duration object... so I'm thinking I'm using the wrong object to map a duration of time (to add or subtract on LocalTimes).
Thank you.
Unfortunately there exists no elegant builtin way to format a Duration in Java 8. The best i have found is to use the method bobince describes in this answer:
Duration duration = Duration.ofHours(1).plusMinutes(20);
long s = duration.getSeconds();
System.out.println(String.format("%d:%02d:%02d", s/3600, (s%3600)/60, (s%60)));
Which prints:
1:20:00
The code will have to be tuned if you need longer durations.
I'm not sure what you mean that you are missing a convert method, but Duration is well suited for adding/subtracting on LocalTime. The methods LocalTime.plus() and LocalTime.minus() accepts Duration as argument.
If you're interested in words, apache commons will do the trick:
DurationFormatUtils.formatDurationWords(System.currentTimeMillis() - start, true, false))
2 days 1 hour 5 minutes 20 seconds
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/time/DurationFormatUtils.html#formatDurationWords-long-boolean-boolean-

"office hours" with jodatime, how store in database

I'm looking into storing a set of "working/office hours" for each day of the week, i.e.
mon - 9 to 5
tue - 8.30 to 16.30
...
sat 10.00 to 14.00
etc.
Using Jodatime in the service layer, I'm going to use these values to calculate which parts of certain Intervals that fall within and without these office hours.
The best way to calculate that seems to me to be by using the interval's Overlap-function, as this speedily put together example shows:
LocalTime startOfficeHours = new LocalTime(9, 30);
LocalTime endOfficeHours = new LocalTime(17, 30);
Interval officeHoursToday = new Interval(startOfficeHours.toDateTimeToday(), endOfficeHours.toDateTimeToday());
DateTime start = new DateTime().withHourOfDay(8).withMinuteOfHour(30);
DateTime end = start.plusHours(7).plusMinutes(45);
Interval workShift = new Interval(start, end);
Period withinOfficeHours = officeHoursToday.overlap(workShift).toPeriod();
assertEquals(withinOfficeHours.getHours(), 6);
assertEquals(withinOfficeHours.getMinutes(), 45);
Now, i need to find a way to read and write those office hours intervals to and from the database in a speedy fashion...
If i just use integers, that'd be 28 columns - but that might be quicker than writing and reading PersistentLocalTimes that might contain a lot of redundant info. But in both those cases i'd end up having to create intervals manually all the time.
Is there a way to store a "date-agnostic" interval in the database that i then can change the date for when i make my calculations as per above?
if anybody's been through this and has any pointers, i'd be much obliged.
You could save the two values workShift.getStartMillis() and workShift.getEndillis() (which return long in Java) to your database as two fields of type big int.
You could save the two values as a ; separated text field if you want to separate them in your code later.
You could also store workShift.toDurationMillis() in the database as big int if you want to apply the addition of that number of milliseconds yourself later rather than creating a Joda Time Interval (since there is no constructor that takes the duration as long).

Java Compare dates to check if in range

ok not as simple as title may make it sound. I tried this in a very primal way with c# and it worked, but I have a feeling a better job could be achieved with Java and Oracle as database. So the thing is:
I have a reservation system. multiple bookings could be made on the same day for period between date X and date Y as long as each day in the range can accommodate the requested number. Maximum number of clusters to reserve is 46. Hence logically you would look at each day as a holder of 46 cluster reservation and deduce from that.
Now what I have difficulty working out is:
when there are n number of bookings stored and valid in database, then I want to make new booking. So how do I check if this new date range falls within any of the previously booked days or not. Not talking simply here about x falling in y (as ranges). More like:
X_______________________________________Y
X________________________________y
X________________________________Y
X________________________________Y
as u can see the overlap is happening.
Please let me know how could I do this as it will affect early design of objects
Regards
Assume your date has two methods: isBefore(Date other) and isAfter(Date other). Obviously if they don't you can cure this with an external method or wrapping or something. Edit: java.util.Date has compareTo method you could use.
You do this:
public boolean overlapsWithExisting(Booking booking) {
final Date early = booking.getStart();
final Date late = booking.getEnd();
for(Booking existing : existingBookings) {
if(!(early.isAfter(existing.getEnd()) || late.isBefore(existing.getStart()))
return true;
}
return false;
}
We compare this booking to all existing bookings. If this booking ends before the existing booking even starts, or if this booking starts after the existing booking ends, then it doesn't conflict. Any other condition and they will overlap.
Do this to each booking.
Joda-Time – Interval
Rather than roll your own, use the Interval class in the Joda-Time library. An Interval is a pair of specific points along the timeline, each defined as a DateTime instance.
The Interval class offers overlap, gap, and abuts methods.
Half-Open
Those methods wisely use the Half-Open approach to spans of time where the beginning is inclusive while the ending is exclusive. Search StackOverflow for more info.

Categories