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.
Related
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
}
So my school needs us to have a lambda expression for an assignment so i decided that lambda'ing a filtered list may be easier the problem is that my code below keeps giving me the next 30 days no matter what i do and below is just an example of what i am trying but i need a way to see if its possible to get this code:
ObservableList<Appointments> appts = AppointmentsDAO.getAppts();
LocalDateTime now = LocalDateTime.now();
LocalDateTime month = now.plusMonths(1);
FilteredList<Appointments> filter = new FilteredList<>(appts);
filter.setPredicate(row -> {
LocalDateTime start = (row.getStartTime().toLocalDateTime());
return start.isAfter(now) && start.isBefore(month);
});
appointmentsTableView.setItems(filter);
to output the same as the sql statement i was using before which is :
SELECT *
from appointments AS a
INNER JOIN contacts AS c ON a.Contact_ID=c.Contact_ID
WHERE MONTH(start) = MONTH(NOW()) AND YEAR(start) = YEAR(NOW());
the sql statement gives me a filtered db list of all matching month-year info but how do i translate the same statement for java? im thinking maybe i cant but figured it was worth a try. Also the lit row im filtering is set to a timestamp throughout my program but casting timestamp to localDateTime hasnt been a problem thus far though could be relevant here.
Your lambda looks good. I think your problem is the date logic. You are asking filtering for rows between the current datetime and the same day next month. You want to filter for rows between the beginning of the current month and the end of the current month. So, with the caveat that this code isn't tested, something like this:
ObservableList<Appointments> appts = AppointmentsDAO.getAppts();
LocalDateTime now = LocalDateTime.now();
LocalDateTime startOfMonth = now.with(firstDayOfMonth());
LocalDateTime endOfMonth = now.with(lastDayOfMonth());
FilteredList<Appointments> filter = new FilteredList<>(appts);
filter.setPredicate(row -> {
LocalDateTime start = (row.getStartTime().toLocalDateTime());
// This next line fails for records falling exactly on the start/end of month...
// return start.isAfter(startOfMonth) && start.isBefore(endOfMonth);
// ... this version should do better in that regard
if ( startOfMonth.isAfter(start) || endOfMonth.isBefore(start) ) {
return false;
}
return true;
});
appointmentsTableView.setItems(filter);
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.
Is there any way to display element of a list (or any other that might do similar function) comparing to current time.
So, I made an arraylist that contains time, which doesn't have any pattern of repetition, using method to increase it for 10 min (for instance) won't to.
I have following:
Date function that has current time:
Date date = new Date();
DateFormat dateformat = new SimpleDateFormat("HH:mm:ss");
ArrayList that has time that I need:
ArrayList<String> time = new ArrayList<String>();
time.add("5:40");
time.add("6:40");
time.add("8:30");
time.add("9:45");
time.add("10:35");
time.add("11:10");
time.add("11:55");
time.add("12:20");
time.add("13:30");
time.add("14:55");
time.add("16:00");
time.add("16:30");
time.add("17:30");
time.add("19:00");
time.add("20:10");
time.add("21:10");
Now I'm curious, and also because I'm new to this, is there a way to do next:
1) Considering there will be a lot of arrays that will have time, is there a way (perhaps with some code examples) to write a method that will have an argument so user can choose which 'time' they want it to be displayed?
2) Will there be need to use loop once (in this case 'time' array) it reaches the end, or can it be written so it automatically goes to beginning based on current time.
3) Code optimisation: can this be written in more practical way?
I am fairly new to this, so any help (with guidance and code examples) is much appreciated so I can use it in this project and as learning material for future ones.
java.time
Use the LocalTime class rather than String to represent your values. A LocalTime object is for time-of-day without a date and without a time zone.
Create your List.
List<LocalTime> list = new ArrayList<>();
Parsing
Populate the List by parsing the input strings. If your input strings comply with the ISO 8601 standard including a padded zero on single-digit hour, then you need not define any formatting pattern as LocalTime can directly parse such values.
list.add( LocalTime.parse( "09:45" ) );
list.add( LocalTime.parse( "10:35" ) );
list.add( LocalTime.parse( "11:10" ) );
If your input strings lack the padding zero, then define and use a DateTimeFormatter.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "h:m" );
…
LocalTime localTime = LocalTime.parse( input , formatter );
Sorting
Sort the List. The Collections.sort static method is one way.
Collections.sort( list );
Current time
Get the current time. For this a time zone is required (ZoneId), as the wall-clock time for any moment various around the world by time zone. Use a proper time zone name, never the 3-4 letter abbreviations commonly seen such as EST or IST.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalTime now = LocalTime.now( zoneId );
Loop the list
Loop the List comparing each element in the list to now using whatever rules you have in mind for defining “nearest”. The Duration class tracks a span of time as a total number of seconds plus a fractional second in nanoseconds.
Note that Duration class has various methods to assist with comparing. The abs method gives an absolute value (converting negatives to positives). The compareTo method tells you if one duration is larger, smaller, or equal to another.
…
for ( LocalTime localTime : list ) {
Duration duration = Duration.between( now , localTime );
…
}
NavigableSet
If you don't mind losing any possible duplicate time values, use a NavigableSet and its ceiling method as shown in the correct answer by Assyrians.
Like #BasilBourque, I suggest storing the times as LocalTimes - but I would also suggest using a NavigableSet such as TreeSet instead of a List (I'm assuming that the times in your list are all unique).
The idea is that TreeSet has a ceiling method that does exactly what you need:
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
You can then write:
TreeSet<LocalTime> times = new TreeSet<> ();
times.add(LocalTime.parse("05:40"));
times.add(LocalTime.parse("06:40"));
times.add(LocalTime.parse("08:30"));
//...
LocalTime ceiling = times.ceiling(LocalTime.now());
if (ceiling != null) //do something with it
It would probably be best to use actual Date objects and just compare them. However, if you need to do it using strings, you could write a compare function that takes two dates and outputs the difference between them in minutes.
int compare(String d1, String d2) {
//split the strings based on the ":"
string d1Split[] = d1.split(":");
string d2Split[] = d2.split(":");
//convert each one to minutes - note this assumes the string will always be formatted like "hours:minutes:anything else is irrelevant"
d1Mins = d1Split[0] * 60 + d1Split[1];
d2Mins = d2Split[0] * 60 + d2Split[1];
//return the absolute value of the difference between the times in minutes - note this assumes the times will be part of the same day
return Math.abs(d1Mins - d2Mins);
}
Once you have a compare function, all you need to do is go through the array and compare each element to the current date, storing the closest one.
int leastDifference = 999999;
String now = dateformat.format(date);
String closest = "";
for (String toCompare : time) {
int comparison = compare(toCompare, now);
if (comparison < leastDifference) {
leastDifference = comparison;
closest = toCompare;
}
}
And closest will contain the string you are looking for.
get the HH:MM part of current time;
sort your time list
use binary search:
String search(String currHrs, List time, int start, int end) {
if (start == end && time.get(start) > currHrs)
return time.get(start);
else
return currHrs;
int mid = (start + end) >> 1;
// discard one half at a time.
return (time.get(mid) > currHrs) ? search(currHrs, time, start, mid): search(currHrs, time, mid + 1, end);
}
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).