Problem: I have a list containg hours, for example:
08:15:00
08:45:00
09:00:00
12:00:00
...
application is allowing user to make an appointment for a specific hour let'say: 8:15:00, each meeting takes half an hour.
Question: How to determine if there is a slot needed for appointment like this? I know that Calendar class have methods before() nad after(), but it doesn'solve my problem. I mean if there is appointment at 12:00 and another one at 12:00, how to prevent before making another one at 12:15?
edit:
I've tried using methods I mentioned before, like:
Calendar cal1 = Calendar.getInstance(); // for example 12:00:00
Calendar cal2 = Calendar.getInstance(); // for exmaple 12:30:00
Calendar userTime = Calendar.getInstance(); // time to test: 12:15:00
if(user.after(cal1)&& user.before(cal2)){
... // do sth
}
Check if the date to check is between the two provided:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
Date before = sdf.parse("07/05/2012 08:00");
Date after = sdf.parse("07/05/2012 08:30");
Date toCheck = sdf.parse("07/05/2012 08:15");
//is toCheck between the two?
boolean isAvailable = (before.getTime() < toCheck.getTime()) && after.getTime() > toCheck.getTime();
To book for a determinate hour, I would do a class with two dates and a method to check this:
public class Appointment{
private Date start;
private Date end;
public boolean isBetween(Date toCheck){....}
}
Then you can simply do an Schedule class extending ArrayList, adding a method isDateAvailable(Date toCheck), iterating the list of Appointments and checking that there is no one conflicting.
I'd have some kind of appointment class with either a start timestamp and a duration or a start time and an end time. Then when adding new appointments to the schedule, check that the appointment with the start time before the new appointment doesn't run over the start time of the proposed new appointment.
Well how you would do it specifically depends on how you are storing your data, format, etc., but generally what you would do is simply check if there is an appointment for any time between the requested time to the requested time + requested length.
// Example (using int time(1 = 1 minute), assuming that appointments can only be at 15min intervals)
boolean isHalfHourTimeSlotAvaliable(int time) {
for (int i = 0; i < appointments.size(); i++) {
if (appointments.get(i).time == time || appointments.get(i).time == time + 15) {
return false;
}
}
return true;
}
Related
This question already has answers here:
Java - Check if array contains 3 consecutive dates
(4 answers)
Closed 5 years ago.
I have an array of unique dates from each time the user completes a task. I want to check if the dates within the array are consecutive from and including todays date.
If the array contains dates: "2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5" then based on today's date being 2017/6/5 the function would return 4 as there are 4 consecutive dates from and including today.
If the array contains dates "2017/6/2, 2017/6/3, 2017/6/4" then it would return 0 as the array does not include today's date. Otherwise the count would be broken upon a non consecutive date.
List<Date> dateList = new ArrayList<Date>();
int count = 0;
Date todayDate = new Date();
for (int i=0; i<dateList.size(); i++){
// Check if dates within the array are consecutive from todayDate, if so then increment count by 1.
}
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
Although you can also use JodaTime, it's being discontinued and replaced by the new APIs, do I don't recommend start a new project with joda. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
As you want to compare just the date (day/month/year), and not the time (hour/minute/second), the best choice is to use the LocalDate class. For java 8, this class is in java.time package, and in ThreeTen Backport, the package is org.threeten.bp. But the classes and methods names are the same.
The code would be like this:
public int count(List<LocalDate> dateList, LocalDate today) {
if (!dateList.contains(today)) { // today is not in the list, return 0
return 0;
}
int count = 0;
LocalDate prev = dateList.get(0); // get first date from list
for (int i = 1; i < dateList.size(); i++) {
LocalDate next = dateList.get(i);
if (prev.plusDays(1).equals(next)) {
// difference between dates is one day
count++;
} else {
// difference between dates is not 1
// Do what? return 0? throw exception?
}
prev = next;
}
return count + 1; // didn't count the first element, adding 1
}
Testing this method:
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
dateList.add(LocalDate.of(2017, 6, 5));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 4
Another test (when today is not in the list)
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 0
Notes:
As it wasn't specified what to do when the days are not consecutive (return 0 or throw exception), I left this part commented. But it should be straightforward to add this to the code
If you want to convert java.util.Date to LocalDate, you can do as follows (using the code of this answer, full explanation is in this link in case you have any questions):
public LocalDate convert(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
// if your Date has no toInstant method, try this:
public LocalDate convert(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
I understood that you want to check for consecutive days (so, a 1-day difference between the dates). But if you want to check if the previous date is before the next (no matter how many days), you can change the if (prev.plusDays(1).equals(next)) to if (prev.isBefore(next))
I'm not sure if that's the case, but if you want, you can also parse a String directly to a LocalDate (so you don't need to create lots of Date objects), using a DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d");
LocalDate d = LocalDate.parse("2017/6/2", formatter); // 2017-06-02
There are a lot of ways to write it more clear:
Use new Date API;
Use libraries;
But, in such case, with usage of old Date classes, I would do that in such a way:
public static void main(String[] args) {
long millisInDay = TimeUnit.DAYS.toMillis(1);
List<Date> dates = Arrays.asList(new Date("2017/6/2"), new Date("2017/6/3"), new Date("2017/6/4"), new Date("2017/6/5"));
System.out.println(getSequentialNumber(millisInDay, dates));
}
private static int getSequentialNumber(long millisInDay, List<Date> dates) {
int count = 0;
Date now = setMidnight(Calendar.getInstance().getTime());
for (int i = dates.size() - 1; i >= 0; i--) {
Date date = setMidnight(dates.get(i));
if (date.getTime() == now.getTime()) {
count++;
}
now.setTime(now.getTime() - millisInDay);
}
return count;
}
private static Date setMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
return calendar.getTime();
}
If I understand the requirement correctly, you have an array of Date objects, ordered by date, and guaranteed not to have two Date objects for the same day, but possibly with gaps between the days. Your goal is to return the length of the maximum sub-array that contains only consecutive days and also includes the current day, or to return 0 if there is no such sub-array. The current day may fall anywhere inside that sub-array, not necessarily at the beginning or end.
It's not clear if you need to support crossing year boundaries, but I'll assume so. I also assume that all the Date objects in the list are for the same time zone which is also the time zone for the device on which you are running. If that's not the case, you should refer to this answer for more information on testing whether two Date objects refer to the same day.
It's fairly simple to do this if you work with Calendar objects instead of Date objects. You don't need any third-party libraries, as both Date and Calendar are parts of the standard Android API. I suggest doing this in two phases: first search for the current date in the array and then scan in both directions for either a gap in the dates or an array boundary. Then just count how far you could go in each direction.
public int getDateSpanCount(List<Date> dateList) {
final int n = dateList.size();
final Calendar today = Calendar.getInstance();
final Calendar other = Calendar.getInstance();
int count = 0;
// First search for today in the date array
int posToday = -1;
for (int i=0; i<n; i++) {
other.setTime(dateList.get(i));
if (areSameDay(today, other)) {
posToday = i;
break;
}
}
// If today is in the list, count the size of the sub-array containing today
if (posToday >= 0) {
count++; // count today, at least
final Calendar probe = Calendar.getInstance();
// scan backwards from position of today's date
for (int prevPos = posToday - 1; prevPos >= 0; prevPos--) {
final Date prev = dateList.get(prevPos);
probe.setTime(prev);
other.add(Calendar.DAY_OF_YEAR, -1);
if (areSameDay(probe, other)) {
count++;
other.setTime(prev);
} else {
break;
}
}
// reset the other time
other.setTime(today.getTime());
// scan forward from position of today's date
for (int nextPos = posToday + 1; nextPos < n; nextPos++) {
final Date next = dateList.get(nextPos);
probe.setTime(next);
other.add(Calendar.DAY_OF_YEAR, 1);
if (areSameDay(probe, other)) {
count++;
other.setTime(next);
} else {
break;
}
}
}
return count;
}
/** Test whether two Calendar objects are set to the same day */
private static boolean areSameDay(Calendar c1, Calendar c2) {
// see discussion above if dates may not all be for the local time zone
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}
I have a game that loops. I need to check the current number of milliseconds. I don't want to create a new Date object just to get the number of milliseconds that has passed. Is there a way to get the current time without creating a new Date object every iteration of my game loop?
Example:
Date d = new Date();
while(true)
{
long currentTime = d.getCurrentTime();
}
In the above code, the value of the currentTime variable would continuously change.
Rather than using Date, you can access the static method
long currentTime = System.currentTimeMillis()
This returns the current time of the given system in milliseconds.
I am trying to use the Date class to get the current time each time my loop executes. So far I have:
Date timeNow = new Date();
while(true){
System.out.println(timeNow.getTime()); //prints current time
Thread.sleep(10000);//sleep 10 secounds
}
When the time is reprinted it just shows the same time every print instead of being 10 seconds later. What am I doing wrong? Thanks for the help.
You created timeNow outside of the while loop. The time for this object is captured at construction time. When you move it within the scope of the while loop, you'll get a new object every time which represents the time it was created.
while(true) {
Date timeNow = new Date();
System.out.println(timeNow.getTime()); //prints current time
Thread.sleep(10000);//sleep 10 secounds
}
Note that the default constructor of Date is equivalent to..
Date timeNow = new Date(System.currentTimeMillis());
When you execute Date myDate = new Date() you create a Date object with the current system time.
Anytime you call myDate.getTime() you will get the same output, the time as at whenever you created the object.
To see a different time, you would need to create a new Date object after each Thread.sleep(10000), like this:
while(true) {
Date myDate = new Date()
System.out.println(myDate.getTime()); //prints current time
Thread.sleep(10000);//sleep 10 seconds
}
I am working on a project that will run many thousands of comparisons between dates to see if they are in the same month, and I am wondering what the most efficient way of doing it would be.
This isn't exactly what my code looks like, but here's the gist:
List<Date> dates = getABunchOfDates();
Calendar month = Calendar.getInstance();
for(int i = 0; i < numMonths; i++)
{
for(Date date : dates)
{
if(sameMonth(month, date)
.. doSomething
}
month.add(Calendar.MONTH, -1);
}
Creating a new Calendar object for every date seems like a pretty hefty overhead when this comparison will happen thousands of times, soI kind of want to cheat a bit and use the deprecated method Date.getMonth() and Date.getYear()
public static boolean sameMonth(Calendar month, Date date)
{
return month.get(Calendar.YEAR) == date.getYear() && month.get(Calendar.MONTH) == date.getMonth();
}
I'm pretty close to just using this method, since it seems to be the fastest, but is there a faster way? And is this a foolish way, since the Date methods are deprecated? Note: This project will always run with Java 7
I can't comment on whether to use the deprecated methods, but if you choose not to there's no need to instantiate a new Calendar for every Date you check. Just use one other Calendar and call setTime(date) before the check (or one Calendar for every thread if you parallelize it).
As a side note, I do have to agree with ChristopheD's comment that this is something worthy of a database.
I think you can define a static DateFormat to extract the month and year from Date and use both objects as date only.
public static DateFormat formatter= new SimpleDateForm("MMyyyy");
public static boolean sameMonth(Date date1, Date date2)
{
return formatter.format(date1).equals(formatter.format(date2));
}
I have a list of objects called Activity:
class Activity {
public Date activityDate;
public double amount;
}
I want to iterate through List, group them by date and return a new list . Here's what I currently do:
private List<Activity> groupToList(List<Activity> activityList) {
SimpleDateFormatter sdf = new SimpleDateFormatter("YYYY-MM-DD");
Map<String,Activity> groupMap = new HashMap<String,Activity>();
for (Activity a in activityList) {
String key = sdf.format(a.getActivityDate());
Activity group = groupMap.get(key);
if (group == null) {
group = new Activity();
groupMap.add(key, group);
}
group.setAmount(group.getAmount() + a.getAmount());
}
return new ArrayList<Activity>(groupMap.values());
}
Is it a WTF to use the DateFormatter in this way?
I'm using the DateFormatter because each activityDate could have time information.
I would just use the date object itself as the key. If it it bothers you because the date object is mutable, then use its toString() value. No reason to go making formats.
If the issue is that you want to normalize the date by removing the time component, it would be much better to do that withing the Activity object and remove the time component. If the issue is still further that there are potential time zone issues, I would use JodaTime, but there is no object in the JDK currently that represents a pure date without time, so going with a string isn't outrageous, but it should be hidden behind a method in the Activity object and the fact that it is a date formatted string without a time component should be an implementation detail.
java.util.Date is a quite poor abstraction for your need; it is IMO fair to stick to strings if nothing better is around, HOWEVER Joda-time provides a good datatype for you: DateMidnight or alternatively LocalDate if Activity is strictly timezome-independant.
other than that, the code looks good to me, you might be able to shorten it a bit using an implementation of Multimap, to avoid messy null-checking code. to be honest, it doesn't get much shorter than your solution:
public List<Activity> groupedByDate(List<Activity> input) {
//group by day
final Multimap<DateMidnight, Activity> activityByDay
= Multimaps.index(input, new Function<Activity, DateMidnight>() {
#Override
public DateMidnight apply(Activity from) {
return new DateMidnight(from.activityDate);
}
});
//for each day, sum up amount
List<Activity> ret = Lists.newArrayList();
for (DateMidnight day : activityByDay.keySet()) {
Activity ins = new Activity();
ins.activityDate = day.toDate();
for (Activity activity : activityByDay.get(day)) {
ins.amount+=activity.amount;
}
}
return ret;
}
Why not simply create a HashMap<Date, Activity>() instead of the roundabout way with Strings?
Sorry, I didn't answer the question. The answer is: yes, unless I am an idiot ;)
You could do this using the Date as the key if you used a TreeMap and provided a Comparator that only compared the year, month and day and not the time.
As already mentioned the best solution is to represent your date with day precission. If this is not possible joda is nice library.
If you can ignore daylight saving time then grouping by date can be accomplished much easier. A unix time day is 86 400 s long. The timestamp does ignore leap seconds. (Your timer stops for one second or the leap second is distributed in some way.) All date values were day is equal are the same day:
int msPerDay = 86400 * 1000;
long day = new Date().getTime() / msPerDay
One minor point is to adjust the timezone. For my timezone CET (UTC/GMT +1 hour) the GMT day starts one our later:
new GregorianCalendar(2009, 10, 1, 1, 0).getTime().getTime() / msPerDay) ==
new GregorianCalendar(2009, 10, 2, 0, 59).getTime().getTime() / msPerDay) ==
new Date().getTime() / msPerDay
If the daylight saving time is significant the best way is to use joda. The rules are just to complicated and locale specific to implement.