I'm having an issue working with time in Java. I don't really understand how to efficiently solve comparing the time of now and 12 hours before and after
I get a set of starting times for a show from an API and then compare that starting time with LocalTime.now(). It looks something like this:
SimpleDateFormat sdt = new SimpleDateFormat("HH:mm:ss");
String temp = sdt.format(Local.time(now));
LocalTime secondTime = LocalTime.parse(parts1[0]);
LocalTime firstTime = LocalTime.parse(temp);
int diff = (int) ((MINUTES.between(firstDay, secondDay) + 1440) % 1440);
if(diff <= 720){
return true;
}
Where my idea is that if the difference between the two times is smaller than 720 minutes (12 hours) I should get the correct output. And this works for the 12 hours before now. I thought I might need to swap the parameters of .between, to get the other side of the day. That counts it completely wrong (If the time now is 15:00:00 it would accept all the times until 22:00:00 the same day). Is this just a really bad way of comparing two times? Or is it just my math that lacks understanding of what I'm trying to do?
Thanks
Using the 'new' (not that new) Java 8 time API:
Instant now = Instant.now();
Instant hoursAfter = now.plus(12, ChronoUnit.HOURS);
Instant hoursBefore = now.minus(12, ChronoUnit.HOURS);
First, doing this kind of operations on java.time.LocalTime won't work! Or at least only if the time is "12:00:00" …
That is because you will have an over-/underflow when you add/substract 12 hours from any other time.
So your starting point should be to go for java.time.LocalDateTime (at least, although I would go for java.time.Instant). Now you can handle the over-/underflow, as you will get another day when adding or subtracting 12 hours.
How this works is shown in this anwswer: LocalDateTime allows nearly the same operations as Instant.
Given an epoch time: eg (1513213212) I should get 1 since its 1 am right now UTC. How would I go about converting it into the hour of the day? Is it possible to do it just using math (division, mod)?
It would be close to impossible to do it by using maths only. (Leap year and all). It's better to use established APIs which will do all the hard work.
You can use following method to do this.
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(1513213212* 1000L);
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(cal.get(Calendar.HOUR));//12 hour clock
System.out.println(cal.get(Calendar.HOUR_OF_DAY));//24 hour clock
Use java.time, the modern Java date and time API also known as JSR-310:
LocalTime timeOfDay = Instant.ofEpochSecond(1513213212L)
.atOffset(ZoneOffset.UTC)
.toLocalTime();
System.out.println(timeOfDay);
int hourOfDay = timeOfDay.getHour();
System.out.println(hourOfDay);
This prints:
01:00:12
1
Even if you just wanted to do the math, I would still prefer to use standard library methods for it:
long epochSeconds = 1513213212L;
// convert the seconds to days and back to seconds to get the seconds in a whole number of days
long secondsInWholeDays = TimeUnit.DAYS.toSeconds(TimeUnit.SECONDS.toDays(epochSeconds));
long hourOfDay = TimeUnit.SECONDS.toHours(epochSeconds - secondsInWholeDays);
System.out.println(hourOfDay);
This too prints 1.
Your intention was “Given an epoch time: eg (1513213212) I should get 1 since it’s 1 AM right now UTC.” Which of the above code snippets in your opinion most clearly expresses this intention? This is what I would use for making my pick.
While MadProgrammer is surely correct in his/her comment that date and time arithmetic is complicated and that you should therefore leave it to the date and time API, I believe that this is one of the rare cases where not too complicated math gives the correct answer. It depends on it being safe to ignore the issue of leap seconds, and if going for the math solution, you should make sure to check this assumption. Personally I would not use it anyway.
everyone.
I'm trying to figure out if Google Datastore is the best option in my case..
I need to store employees with theirs schedules like:
id
name
schedule
Schedule looks like:
Mon 10am-10pm (simple)
Tue 10am-5pm, 5.30pm-8pm (multiple, not even hours)
..
Sun 6pm-4am (start/end are in different days)
one of the APIs returns if employee is working NOW or not
I tried to play with datastore but GQL queries appeared to be very limited, for example you cant compare in one query two different properties (like
.. WHERE current_time>start_time AND current_time<close_time
You cannot use inequality operators (less than, more than, etc.) on
different property names in the same query.
It means that I need to load lots of entities into my backed and parse them, wasting time and resources.. instead of getting results right from database
Is there any way to use datastore for my task? or its better to go sql?
how to design my database to store schedules in datasore/sql?
thanks in advance!
Based on your examples, I'm going to assume work shifts are in 30 min increments. If that is not the case you can adjust the below easily, although you may want to consider some optimizations.
The below solution will give you constant time look-ups for employees, regardless of how many shifts they work. It also seamlessly handles shifts that go from one day to the next.
0. Your entity model
I'm going to use the following straw man entity
Entity Kind: Employee
- id: Auto id
- name: string
- schedule: repeated integer, indexed
2. Break day into 30 minute blocks
24 hours gives you 48 periods of 30 minutes, so let's map integers to time periods:
0 = 12:00 AM to 12:30 AM
30 = 12:30 AM to 1:00 AM
60 = 1:00 AM to 1:30 AM
...
1380 = 11:00 PM to 11:30 PM
1440 = 11:30 PM to 12:00 AM
This integer is easy to derive from your current time. Using java.util.Calendar and java.util.Date:
Date date = new Date(); // Initializes to now.
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(date);
int hour = calendar.get(Calendar.HOUR_OF_DAY); // Hour in 24h format
int minute = calendar.get(Calendar.MINUTE); // Minute of the hour
int period = hour*60 + minute/30*30; // Integer division rounds down to nearest 30
I tested this on compilejava.net, test code here: https://gist.github.com/55a98bbdb9b5eb3eeaee5f8984f11687
3. Account for day of week
Now we have 30 minute period blocks in the day, we should merge in the day of the week (Sunday = 0, Saturday = 6). Since the max 30 min period value is 1440, it is convenient to just multiple the day of the week by 10000 so we can add them together without conflict:
int day = calendar.get(Calendar.DAY_OF_WEEK);
int day_period = day*10000 + period;
Expanded test code here: https://gist.github.com/217221e03b3eb143b4be45bf3f641d25
4. Storing the Schedule
Now, instead of storing your schedule as start and stop times per day, use the same idea above to store each 30 minute period that an Employee is scheduled for. In your example you had:
Mon 10am-10pm (simple)
Tue 10am-5pm, 5.30pm-8pm (multiple, not even hours)
..
Sun 6pm-4am (start/end are in different days)
In the schedule field (repeated integer), this would look like:
10600,10630,10660,10690,10720,10750,10780,10810,10840,10870,10900,10930,10960,10990,11020,
11050,11080,11110,11140,11170,11200,11230,11260,11290,20600,20630,20660,20690,20720,20750,
20780,20810,20840,20870,20900,20930,20960,20990,21050,21080,21110,21140,21170,1080,1110,
1140,1170,1200,1230,1260,1290,1320,1350,1380,1410,0,30,60,90,120,150,180,210
5. Query time!
Querying is extremely easy now and merely an equality. This will give you O(1) performance per Employee entity working right now, or more generally O(n) where n is the number of employees that are working now (as opposed to total employees).
... WHERE schedule=day_period
You can store long representation of the start time and end time. 1 hour = 3600000 milliseconds. So if 00-00 is the reference point 02-00 will be represented by 2 x 3600000 . Another option is to store java.util.Date (one for start time and one for end time)
I need to determine if a time stamp is an exact hour (i.e. it represents a time with no minutes, seconds or milliseconds components) - using primitives only. The code is called several hundred times per second and I don't want the overhead of a Calendar or other object.
I've tried this but rounding errors cause the comparison to fail.
float hours = (time / 3600000f);
boolean isExactHour = hours == (int)hours;
For example, if time = 1373763600470, hours = 381601.03125. That time stamp represents 01:00:00:000 GMT today and hours should be 381601.
Any ideas for a quick and simple way to do this? Thanks!
[EDIT]
It seems that this is more complex than at first sight (when is it not? :)
For clarity, I don't care about time zones, nor leap seconds. The time stamp is generated by a method which returns the previous midnight - i.e. 13 July 2013 00:00:00:000 for today. I am then calculating, for any time stamp in an array of longs which is always this initial time stamp plus/minus an exact multiple of 5 minutes. My aim to to determine if a given stamp is "top of the hour". I might have edge cases where the multiple of 5 minutes overlaps a year end but I can live with those.
(time % 36000000) == 0
Surely this is obvious?
EDIT
To get accuracy w.r.t. leap seconds, assuming a lookup table of leap seconds indexed by year since (say) 1970, something like:
((time-leapSeconds[(int)(time/(1000*60*60*24*365.2425))-1970]*1000) % 3600000) == 0
Programmers needing this level of accuracy should also see all of Einar's comments below.
I want to calculate the number of days from the "beginning of time" to a current date. This could be easily achieved with a simple calculation (timestamp / 24 / 60 / 60 / 1000 = daysFromBeginningOfTime) but the twist is that i need to be aware of time zones as well. The timestamp is the same everywhere in the world but when you parse it with the proper time zone then it reflects the differences between locations so using just the timestamp doesn't work and i don't want to handle all the time zone transitions myself.
Example:
if it's 23:30 in London and the day number is 18843 then in Amsterdam it's 0:30 and the day number should be 18844.
I looked at joda.time but didn't really find what i was looking for.
Anyone have any ideas?
The problem appears due to a wrong initial assumption, I think.
The argument the OP makes in his example is not correct. No matter what the clock shows in London or Amsterdam, the time difference to the start of the epoch is - at every point of time - independent of where you are in the world.
Hence, the solution is to parse a given input date to an UTC timestamp and proceed as before.
(Ignoring the point that zero is not "the beginning of time" ... and that the actual time point for the beginning of time is probably unknowable ...)
Here's how to calculate the number of days since "the local-time UNIX epoch in a given timezone"1.
Get hold of the object that represents the local timezone.
Get the timezone's offset from the object
Convert it to milliseconds and add it to the current UTC time.
Calculate the day number as before.
1 - ... whatever that means.