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.
Related
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.
I wonder if it's possible to parse any string (at least to try) to sql Date without specifing the string format? In other words I want to make a generic method who take as input a string and return an sql Date.
For instance I have:
String date1="31/12/2099";
String date2="31-12-2099";
and call parseToSqlDate(date1) and parseToSqlDate(date2) which will returns sql dates.
Short answer: No
Why: Parsing any string to a valid date is a task you as an intelligent being could not do (there is no "logical" way to determine the correct date), so you cannot "tell" a computer(program) to do that for you (see JGrice's comment, and there we still have 4-digit years).
Long answer: Maybe, if you are willed to either take risks or do not need a high rate of success.
How:
Define your minimal (format) requirements of a date. E.g. "a minimal date contains 1-8 numbers; 01/01/2001 , 01-01-01 , 01.01 (+current year) , 1.1 (+current year), 1 (+current month + current year) and/or "..contains 1-6 numbers and the letters for months"; 01-Jan-2001 and so on.
Split the input along any non-number/non-month-name characters, with a regex like [^0-9a-zA-Z] (quick thought, may hold some pitfalls)
You now have 1 to 3 (actually more if e.g. the time is included) separate numbers + 1 month name which can be aligned for year/month/day any way you like
For this "alignment", there are several possibilities:
Try a fixed format at first, if it "fits", take it, else try another (or fail)
(only of you get more than one entry at a time) guess the format by assuming all entries have the same (e.g. any number block containing values > 12 is not a month and > 31 is not a day)
BUT, and this is a big one, you can expect any such method to become a major PITA at some point, because you can never fully "trust" it to guess correctly (you can never be sure to have missed some special format or introduced some ambiguous interpretation). I outlined some cases/format, but definitely not all of them, so you will refine that method very often if you actually use it.
Appendix to your comment: "May be to add another parameter and in this way to know where goes day , month and so on?" So you are willed to add "pseudo-format-string" parameter specifying the order of day, month and year; that would make it a lot easier (as "simply" filtering out the delimiters can be achieved).
I need to compare time zones such that Asia/Singapore < UTC < Pacific/Honolulu.
I'm working with java.util.TimeZone (which doesn't implement Comparable).
My search for an existing implementation was unsuccessful because of the overwhelming number of questions about comparing dates with different time zones.
Question: What is a correct implementation of Comparator<TimeZone> that will solve this problem (and what makes it better than other solutions, if applicable)?
Note that I'm not able to use Joda Time for this problem, so "use Joda Time" is not a valid answer.
Edit for clarity
The < notation above was not well defined. My particular use case only requires a naive "geographical" ordering from east to west. As the comments have pointed out, a more advanced and generalizable solution would take into account temporal factors like daylight savings time and historical GMT offset changes. So I think there are two orderings we can consider, each requiring a different Comparator<TimeZone> implementation:
Strictly geographical (current UTC) - addressed by my answer.
Sensitive to local or civil time changes - addressed by rgettman's answer.
I rolled my own Comparator<TimeZone> implementation using getRawOffset for the comparison:
#Override
public int compare(TimeZone tz1, TimeZone tz2) {
return tz2.getRawOffset() - tz1.getRawOffset();
}
It seems to have passed a quick test:
final List<TimeZone> timeZones = Arrays.asList(
TimeZone.getTimeZone("UTC"),
TimeZone.getTimeZone("America/Los_Angeles"),
TimeZone.getTimeZone("America/New_York"),
TimeZone.getTimeZone("Pacific/Honolulu"),
TimeZone.getTimeZone("Asia/Singapore")
);
final List<TimeZone> expectedOrder = Arrays.asList(
TimeZone.getTimeZone("Asia/Singapore"),
TimeZone.getTimeZone("UTC"),
TimeZone.getTimeZone("America/New_York"),
TimeZone.getTimeZone("America/Los_Angeles"),
TimeZone.getTimeZone("Pacific/Honolulu")
);
Collections.sort(timeZones, new Comparator<TimeZone>() {
#Override
public int compare(TimeZone tz1, TimeZone tz2) {
return tz2.getRawOffset() - tz1.getRawOffset();
}
});
//Impl note: see AbstractList.equals
System.out.println(timeZones.equals(expectedOrder)); //true
But I'm still wondering whether there are pitfalls to this solution and/or if there's something preferable.
One might be able to create a Comparator<TimeZone> that takes into account time zone differences. The TimeZone may or may not obvserve daylight savings time, which would adjust the raw offset, thus messing up raw-offset-only comparisons. The TimeZone class seems to support the adjustment based on the 2 getOffset methods, but they need a reference date. How about:
public class TimeZoneComparator implements Comparator<TimeZone>
{
private long date;
public TimeZoneComparator(long date)
{
this.date = date;
}
public int compare(TimeZone tz1, TimeZone tz2)
{
return tz2.getOffset(this.date) - tz2.getOffset(this.date);
}
}
Timezones are purely political, so any use of them that is non-conforming will cause LOTS of problems for users, depending on what the app does and who needs it or uses it. You question would be better asked by explaining why you need them ordered like that. There are adjacent timezones where one uses DST the other does not. So 60% of the year, the TZ1 == TZ2, the other 40% TZ1 < TZ2. Or whatever the case may be.
There are geographic (lat long) timezone data sets, and web sites to query for timezone. Even current DST settings. So you may have to settle for a data set that you need to update frequently at least yearly. Or web access.
You probably should not assign magnitude to them. Only geographic ordering - by longitude.
First of if you could tell us what you are trying to do it'd be great. And the answer is not: strict a>b>c order based on local time. I coded calendrics for a while, so I actually used to know this stuff.
What explictly do believe requires this kind of ordering?
What is most efficient way to compare date range in case of open end date i.e. optional end date? I want to compare effective date and optional end date given by user with any existing overlapping effective date and optional end date in database.If they overlap I want to display error message. Effective date is required and end date is optional.
Important:
effective and end date situation can be implemented in two general ways.
1) By having end date as DB column
For example, Mortgage or saving account rate. The rate becomes effective at certain point of time and then it stays in effect till next rate becomes effective and ends previous rate's effect.At given point of time at least one record will be in effect.
2) By not having end date in database
For example, Discount, coupoun, promotion or special offer. These all can become effective and end at certain point of time. It is possible that at given time no special offer or discount is running.
Scenario 1 is easy to implement. Every time you insert or edit the record you have to check there is no equivelent record in db with exact same effective date(and time).
Scenario 2 may have two further flavors.
2.1) End date is always required.(user entered or default with year 9999)
In such case, if you find any record that has (start1 <= end1 and start2 <= end2) then you have overlap.
2.2) End date is optional in that case null means positive infinity. User can enter end date or leave it blank.
This can be tricky to validate as more possible combination. You may need to generate query dynamically based on user has given end date or not
if(userEnd != null) {
query.append(dbStart<=userEnd)
}
query.append(dbEnd is null || dbStart>=userStart && dbEnd>=userStart)
If this query finds any result then you are overlaping range. Going one step further if you have requirement to automatically end date previous record if previous record's end date is null then you may want to modify above query as below to pass validation.
if(userEnd != null) {
query.append(dbStart<=userEnd)
}
query.append((dbEnd is null && dbStart>=userStart) || (dbStart>=userStart && dbEnd>=userStart))
Depending on other requirements you may need deleteDate to mark record invalid. Possible combination can be
Effective Date(Required) | Deleted Date (Optional)
OR
Effective Date(Required) | End Date(Required or Optional) | Deleted Date (Optional)
I made a schematic image about a reference interval, which might be open ended (gradient), and a timespan to compare:
The 5 basic cases a-e are without open end. Let's consider the timespan to compare not being open ended in the beginning.
Let's further define, that no two dates matches exactly - maybe because they are measured in microseconds. It doesn't really matter, because you will just switch from < to <= or not, whatever you consider to be a valid assumption.
From the basic cases, we see, they always overlap, except the sample.end is < reference.start or sample.start > ref.end.
Who would have thought it is that easy?
Well - let's see, what happens, if ref.end is open. Case a is not affected, but case e will overlap then too.
That was an easy one, wasn't it?
Now we get difficult: What, if the sample is open ended? Case a will now overlap, but e not being affected.
Hardcore experience: Both dates are open ended: Then an overlap occured.
Conclusion: If in doubt, do an image. Here it was inkscape.
table structure:
sysdurationtimeday , sysdurationtimehour, sysdurationtimeminute
1, 12,10
3, 23,10
0, 0,10
i have these 3 fields from database, after getting these 3 values, what is the technique that i can use do cast to which Java Object? (maybe Calendar.class, TimeStamp.class) ?
and use it to compared with record is spent less than 1 day, more than 1 day + less than 3 days. etc?
As long as you're talking durations and not absolute times, this is pretty easy. Just express the time in a convenient unit, say seconds:
time_in_seconds = 86400*sysdurationtimeday +
3600*sysdurationtimehour +
60*sysdurationtimeminute
In Java the standard way to represent this is actually as a long value in milliseconds, ala System.currentTimeMillis().
All the standard Java classes are intended to handle absolute times and need to deal with daylight savings, leap years, and all that crap. At least with the data you gave us, you don't have the required info anyway: there's no way to tell if the day was a daylight savings day and therefore took 23 or 25 hours instead of 24.
I would prefer my own class, overriding the "essential" methods.
public class SysDuration implements Comparable {
int day;
int hour;
int min;
public SysDuration(int day,int hour,int min) {
}
public boolean equals(Object obj) {
}
public int hashCode() {
}
public int compareTo(Object obj) {
}
public boolean spendLess(SysDuration dur) {
}
}
Lots of good answers already.
A sugegstion, perhaps out of scope, if you use durations in java I would prefer to
just calculate and store this in one variable, typically a long in milliseconds
if this resolution is good enough. The splitting in 3 variables usually
make most of the code more complicated.
Calculations are easier and intergartion with libs such as jodatime and similar will be
even more simple.
If you literally want "more than 1 day", that is there's no rounding so d=1, h=23, m=59 gives you 1 day, not 2 days then you can just use sysdurationtimeday and completely ignore hours and minutes. (That assumes you don't have more than 24 in sysdurationtimehour).
Classes such as Calendar don't help, they are for manipulating actual dates, you already are working in durations.
Jodatime supports durations and then you get the operations needed for free.
If you are hesitant to add another dependency and learning curve, I would create a little custom class with a field storing the duration as a number in the desired precision. Then add some methods to do your comparisons or return a Calendar object or a Date aded and subtracted with your duration.
My guess is this will end up being cleaner than using the standard Java API's which always end up in complicated, clunky code when you start manipulating time.