Compare two times in Android - java

I know this is a quite discussed topic but I think I have a very specific problem.
I'm storing opening and closing time of stores on a database as well as GPS coordinates. I'd like to be able to show the stores on a map with a green marker if the store is open and red if close (wrt current time).
My problem is that I'm using the method string.compareTo(string) to know if the store is closed or open. Let's say we are on monday, the store close at 02:00 in the morning (tuesday morning), current time is 23:00. How can I tell the store is still open since 02:00 < 23:00 ?
Thank you for any answer,
Arnaud

edit: I'm editing my answer to give a better idea of what I meant. Also, try to back up a second and re-think the way you store your data structure. This is a very important part of programming and in most cases can be the difference between a bad design and a good design implementation.
Storing the time as a string is not a good idea, but in your case what you should do (I think) is this: (This code assumes that the hours for the store opening and closing time refer to the same day as now)
// Get the current time.
Calendar now = Calendar.getInstance();
// Create the opening time.
Calendar openingTime = Calendar.getInstance();
// Set the opening hours. (IMPORTANT: It will be very useful to know the day also).
openingTime.set(Calendar.HOUR_OF_DAY, storeOpeningTime); // (storeOpeningTime is in 24-hours format so for 10PM use 22).
// Check that the store "was opened" today.
if (now.before(openingTime)) {
return CLOSED;
}
// If the store "was opened" today check that it's not closed already ("tricky" part).
// Create the closing time and closing time for the store.
Calendar closingTime = Calendar.getInstance();
// Check if we are in the AM part.
if (storeClosingTime < 12 // Closing time is in the AM part.
&& storeClosingTime < openingTime // Closing time is before opening time, meaning next day.
// now and closingTime is the same day (edge case for if we passed midnight when getting closingTime)
&& closingTime.get(Calendar.DAY_OF_WEEK) == now.get(Calendar.DAY_OF_WEEK)) {
// Closing time is next day.
closingTime.add(Calendar.DAY_OF_WEEK, 1);
}
// Set the closing hours.
closingTime.set(Calendar.HOUR_OF_DAY, storeClosingTime); // (storeClosingTime is in 24-hours format so for 10PM use 22).
// Check if the store is not closed yet.
if (now.before(closingTime)) {
return OPEN;
}
// Store is closed.
return CLOSED;
I haven't tested this, but I think according to your questions and how to save the opening and closing times, this should work.
Important: There are more edge cases and tweaks you can make to the code to improve it, but I don't have enough time to do it now. I wanted to give a general guiding hand to solve your problem. Handling time and dates is never fun, you need to keep in mind a lot of elements like time zones and clock shifting in winter and summer in different countries. This is not a finished implementation in any way, and getting it to a finished state is not simple.

Related

Java most efficient way using a long epoch timestamp to detect a change in day

Problem situation: I have an incredibly high number of records all marked with a timestamp. I'm looping through all of them to do this and that but I need to detect when the day has changed.
Right now for each loop I'm doing:
cal.setTimeInMillis(record.time);
int currentDay = cal.get(Calendar.DAY_OF_WEEK);
Is this as slow as I imagine it is when it's running hundreds of thousands of times?
I imagine I'm missing a really simple modulo answer or something.
Edit: Time zone does not matter, the information I'm collecting more resolves around a consumable report for someone. 24 hours per report is more accurate, so realistically I don't have to worry about whether or not that's 5am - 5am or 3pm - 3pm, just that I was able to gather 24H worth of info.
Thanks all
After Andy Turner’s time test I am not necessarily convinved that you need any optimized solution. In any case, timsmelik’s suggestion is pretty straightforward: convert the time when the day changes to a count of milliseconds since the epoch so you only need to compare long values. I don’t find that it hurts readability very badly. So here it is in code. I am using and warmly recommending java.time, the modern Java date and time API, if only for the conversion from hours to milliseconds and for printing the results. Even when such a conversion seems trivial, it’s always best to leave to the standard library to do it. It’s more self-explanatory and less error-prone, and it’s easier for the reader to convince oneself that it’s correct.
final long twentyfourHoursAsMillis = Duration.ofHours(24).toMillis();
// Times are already sorted descending (from newest to oldest)
long[] times = { 1_611_718_370_000L, 1_611_632_000_000L,
1_611_631_970_000L, 1_611_459_150_000L };
List<List<Long>> chunks = new ArrayList<>();
List<Long> currentChunk = new ArrayList<>();
// Process first time separately to get started
currentChunk.add(times[0]);
long timeOfNextChunk = times[0] - twentyfourHoursAsMillis;
// Process remaining times
for (int i = 1; i < times.length; i++) {
long currentTime = times[i];
if (currentTime <= timeOfNextChunk) {
chunks.add(currentChunk);
currentChunk = new ArrayList<>();
do {
timeOfNextChunk -= twentyfourHoursAsMillis;
} while (currentTime <= timeOfNextChunk);
}
currentChunk.add(currentTime);
}
// Save last chunk, why not?
chunks.add(currentChunk);
// Print result
for (List<Long> chunk : chunks) {
String chunkAsString = chunk.stream()
.map(Instant::ofEpochMilli)
.map(Instant::toString)
.collect(Collectors.joining(", "));
System.out.println(chunkAsString);
}
Output is:
2021-01-27T03:32:50Z, 2021-01-26T03:33:20Z
2021-01-26T03:32:50Z
2021-01-24T03:32:30Z
I am printing Instant objects. They always print in UTC. For your situation you may want to do otherwise if you need to print the times at all.
You should add a check of your assumption that the times come in sorted order.
I have taken your word for it and broken into chunks at 24 hours. 24 hours may not even mean 5am - 5am but could mean for instance from 5 AM EST on March 13 to 6 AM EDT on March 14 because summer time (DST) has begun in the meantime. If you prefer to split at the same clock hour, the code can be modified to do that.

Check date range in java without time AND using jodatime

I have a date supplied by the user and of course today's date.
I'm attempting to verify that the difference between the 2 days is at least 2 weeks. I've done this using standard libraries - but I'm attempting to do this using jodaTime and I'm having some difficulty.
// BAD CODE - doesn't work
// NOTE: getUserSuppliedDate() returns an instance of java.sql.Date
// Also assume that validation prior to this call has been run that
// validates that the userSuppliedDate comes AFTER today's date - not sure if
// that is relevant in the context I'm attempting to use these particular jodaTime APIs.
DateTime jodaStartDate = new DateTime(getUserSuppliedDate());
if (Days.daysBetween(jodaStartDate, DateTime.now()).isLessThan(Days.days(14))) {
System.out.println("Bad user. You have chosen...poorly.");
}
else {
System.out.println("Well done user. You have supplied wisely.");
}
// GOOD CODE ---- ? Help =)
Your code gives you the wrong result because the dates supplied to Days.daysBetween() are in the wrong order. Since you specified that the user supplied date comes after the current date, your approach will result in a negative number of days.
It will work correctly if you switch the order, putting the earliest date first.
Compare the following two:
DateTime jodaStartDate = new DateTime().withYear(2018)
.withMonthOfYear(7)
.withDayOfMonth(5); // 15 days from now
System.out.println(Days.daysBetween(jodaStartDate, DateTime.now())); // "P-15D"
System.out.println(Days.daysBetween(DateTime.now(), jodaStartDate)); // "P15D"
In the first case, -15 days will evaluate to less than 14 days.
Using weeks instead of days, you'd run into the same problem:
System.out.println(Weeks.weeksBetween(jodaStartDate, DateTime.now())); // "P-2W"
System.out.println(Weeks.weeksBetween(DateTime.now(), jodaStartDate)); // "P2W"

Java: addToDay function

I'm trying to setup my addToDay function. I'm currently stuck on how to proceed with this or even write it correctly. The function itself will take a variable that ranges from -100 to 100. So you would basically add that variable to the current and if it was below the 0 then subtract a month or if it was above the months max day then add a month. Which i have that function setup so all i would have to do is call addToMonth with the correct amount. My problem lies within the amount of days each month has. For example, October has 31 days while November has 30. I have a function that will return the number of days in the current set month so i can call that to get how many max days should be in the current month. I'm thinking maybe a while loop would work but i just wanted to get anyone's thoughts on the best way to set it up.
I have 3 private ints: month, day, year. These are what need to be changed. I have both addTo functions for month and year setup already.
Here are some other functions i have created that can be used in this:
1. addToMonth(int delta) - changes the current month depending on the given parameter
2. getDaysInMonth() - will return the days in a month depending on the month itself
3. validateDay() - Will return true or false if the days fall outside the wanted requirements.
I don't want to use the calendar utility
I also don't want to use any other utilities. Just the base code with Junit for testing
Joda's plusDays() function and Java 8 LocalDate already has the logic that you are trying to achieve
Alright so i ended up just copying my original addToMonth function and modifying it abit to fit with days. So far it works but i do think it'll fail in the cases of different amounth of days not lining up.

How do I use Android's Handler.PostDelayed to make an event happen at a specified time?

I want to have my application execute code at a point in the future.
I want to do:
Date now = new Date();
for (Date beep : scheduledBeeps) {
if (beep.after(now))
{
Logger.i("adding beep");
m_beepTimer.postAtTime(beepNow, beep.getTime());
}
}
In the log I can see 4 beeps added, however they never fire. I'm assuming it has something to do with uptimeMillis, but I'm not sure what to do.
You will have to get the difference between now and beep.gettime() and pass it to postattime function. Since uptime is used as base, it may not be accurate if the phone goes to deep sleep.
beep.gettime - now + SystemCLock.uptimeMillis()
should be passed to postattime function
You are currently passing a very large number equivalent to current milliseconds from jan 1 1970.
You could use the Calendar class to set a certain point in time.
Calendar beepTime = Calendar.getInstance();
beepTime.set(Calendar.DAY_OF_MONTH, 2);
beepTIme.set(Calendar.HOUR_OF_DAY, 01);
beepTime.set(Calendar.MINUTE, 55);
beepTime.set(Calendar.SECOND, 00);
getInstance will set it to the current time, and you can change any variable you like, such as the ones above. For example this would create a time at 1:55 on the 2nd of the current month. You would then set this to be the time to go off with
beepTime.getTimeInMillis()
just pop that into your postAtTime method
Edit: Also I don't know enough about your problem to say for sure, but it may be better to use AlarmManager. I know that that still works even if the program is not running, whereas I don't think PostDelayed does. Feel free to correct me if I'm wrong!

Distinguishing and Parsing Dates in Java

i know this topic isn't new, though i have to dig it up again.
I already searched the Web numerous times (including some Threads here on stackoverflow) but haven't found a satisfying answer so far.
(Amongst others I checked
Parsing Ambiguous Dates in Java and
http://www.coderanch.com/t/375367/java/java/Handling-Multiple-Date-Formats-Elegantly
I am currently writing a Dateparser in Java, which takes a date and generates a format-String which can be used by SimpleDateFormat for parsing the date.
The dates are parsed via regex (yes, it's an ugly one xD) from Logfiles (IBM Websphere, Tomcat, Microsoft Exchange, ....). Because we have customers in (at least 2) different Locales, there is no way to simply "throw" the String against the parse-method of SimpleDateFormat and expect it to work properly.
Furthermore, there is the problem with the position of day and month (i.e. formats "dd/MM/yyyy" or "MM/dd/yyyy") which cannot be solved if i don't have at least two datasets where the day-digit has changed..
So my current approach would be storing the dateformats for a specific software installed at a specific customer's systems in a database (mysql / xml / ... ) and forcing the user to at least specify customername and softwarename so there is enough context to break down the amount of possibilites the format may be given in.
This "subset" then would be used to try to parse the logfiles of the specified software.
(The subset is stored in a HashMap in a HashMap in the form
HashMap> map;
The Integer-Key is the length of the formatstring and the String Key of the second Hashmap specifies a datesignature only containing the separating characters.
(i.e. ".. ::." for a date with format "dd.MM.yyyy 11:11:11.111")
I also take into account the value of the digits, i.e. a digit > 12 has to be a day because there is no 13th month. But this only works reliably for Date-Strings later than the 12th of a month..
Is there any chance to avoid implementing prior knowledge about the environment out of which the logfile came, thus enabling the parser to reliably parse one date without having to refer a second datestring for comparison?
I'm stuck on that for almost 3 months now -.-
Any suggestions would be very welcome =)
Edit:
Okay guys this thread can be closed. I now came up with a different solution for my specific problem. For those who are interested:
I am writing a Logreader in Java. As we have regular maintenance I have to read many logfiles.
But it's not just the plain text information that's written in the file.
Imagine a server just having crashed, it's sunday night and the next person to notice is the head of the IT dpt of the customer. Then on the following day I have to to maintenance and check the logfiles. Judging by content, everything seemed okay, nothing unusual. Half an hour after sending the maintenance report I receive a mail with the above mentioned head of it dpt ranting, that the server had crashed and it seemed to go unnoticed.
The point is, you can't keep track over content and the timestamps for logfiles with several thousand lines. So i developed a component which reads a logfile and calculates the time between two different log-entrys. Each logline got parsed into a java.util.Date to later get the Date as Timestamp for high resolution regarding the log-intervals. The differences i then threw onto a linegraph, which makes longer timeouts between two loglines visible as a big spike relating to the rest of the file.
My solution now will be to completely throw away the date-half of the String and insert a dummy-Date with a predefined format. The date only has to change if the Hour and minute approach 23:59.
The original date later is presented on the graph with the "fake-data" lying beneath.
I thank all of you for your suggestions and feedback =)
(And I hope my English has been understandable so far ;) )
My suggestion is to store all dates as 'ambiguous' until such time that the ambiguity can be resolved. (This assumes that a particular customer will always supply data in the same format.) As soon as you get a log from a customer for which you can unambiguously identify the date format, you would then be able to retrospectively apply this format to previously files.
To do this, you would need a table mapping each customer to their date format with some marker (e.g. NULL) to indicate that format is not yet established. You will probably also need to create your own date representation such that you can model these ambiguous dates.
So, as an example, if the possible date formats are:
dd/mm/yyyy
mm/dd/yyyy
yyyy/mm/dd
yyyy/dd/mm
Given dates, you should always be able to identify the year (permitting two digit years would make this problem considerably harder). So you should be able to map dates as follows:
25/01/2011 -> UNAMBIGUOUS_DD_MM_YYYY
12/01/2011 -> AMBIGUOUS_XX_XX_YYYY
2011/03/03 -> AMBIGUOUS_YYYY_XX_XX
03/30/2011 -> UNAMBIGUOUS_MM_DD_YYYY
If possible, you can ask the customers to pass the dateformat string also along with their actual date strings.
i.e. in their log files, they would need to have one more column
..... , '03/11/2011' , 'MM/DD/YYYY' , ...
I think the strategy you are going for (i.e. analysing a bigger set of data) is the best you can get.
From a single line of logfile you will never know if 3/5/11 is the 3rd of may in 2011 or the 5th of march in 2011. (I guess there might also be locales that might interpret this as 11th of may in 2003...)
I had these problems myself some time ago, and i also could only try to introduce some sort of context by either looking at numbers>12, or what changes quickest (must be "day"). But you already stated that yourself...

Categories

Resources