Passing TimeUnits and converting to milliseconds - java

How can I make an API for a class that the user passes a TimeUnit e.g. minutes, seconds, hours and a number and keep the millisec value internally in the class.
The following seems the only way?
void someMethodDays(int numOfDays) {
this.longValue = TimeUnit.DAYS.toMillis(numOfDays);
}
void someMethodHour(int numOfHours) {
this.longValue = TimeUnit.HOURS.toMillis(numOfDays);
} etc
Is this the only way? A method per value with a descriptive name for argument?

You could model your class after a well-known and tested class: java.time.LocalDate, which provides the plus(long, TemporalUnit) method.
Similarly, you could create a someMethod(long, TimeUnit) method that allows callers to pass in arbitrary amounts of any TimeUnit.
void someMethod(long amount, TimeUnit unit) {
this.longValue = unit.toMillis(amount);
}
Note that LocalDate also provides specialized methods for adding certain common units of time, like plusDays(). That gives the caller the ability to decide which is clearer for the code they're writing:
LocalDate tomorrow = today.plusDays(1);
LocalDate tomorrow = today.plus(1, TimeUnit.DAYS);

It seems to me that you don’t need to develop your own class; that you’ll be reinventing the wheel. I suggest you use the Duration class.
To convert from some time unit to a Duration:
System.out.println(Duration.of(3, ChronoUnit.HOURS));
Or alternatively:
System.out.println(Duration.ofHours(3));
Output is the same in both cases:
PT3H
It prints a little funny; this means a span of time of 3 hours. The format is ISO 8601.
System.out.println(Duration.of(2, ChronoUnit.DAYS));
PT48H
48 hours; that’s correct.
System.out.println(Duration.of(327864523, ChronoUnit.MICROS));
PT5M27.864523S
A span of 5 minutes 27.864523 seconds.
If you need to convert to milliseconds, the method is built in:
Duration dur = Duration.of(284, ChronoUnit.MINUTES);
System.out.println("" + dur.toMillis() + " milliseconds");
17040000 milliseconds
Duration is part of java.time, the modern Java date and time API, and of course works well with the other classes from that API.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation: the Duration class
Wikipedia article: ISO 8601; section on durations

Related

One second difference when comparing two Date() with difference of one day

I wrote simple TimeService with method getDateAfter(int days) and test for it:
#Test
#Order(7)
public void getDateAfterCorrect() throws InterruptedException {
waitIfNeeded();
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = timeService.getDateAfter(1).toInstant()
.atZone(ZoneId.systemDefault()).toLocalDateTime();
long diff = ChronoUnit.SECONDS.between(today, tomorrow);
long secondsAtDay = 86400;
Assertions.assertEquals(secondsAtDay, diff);
}
It should be 86400 seconds at day, but diff is 86399.
I tried to take that one part of code could be executed in another time than other into account by implementing waitIfNeeded() method
private void waitIfNeeded() throws InterruptedException {
var currentMillis = Instant.now().get(ChronoField.MILLI_OF_SECOND);
if (currentMillis > 500) {
Thread.sleep(1000 - currentMillis);
}
}
Do You have any idea why I am not able to make this test and other possible things that can be wrong here (I assume things like how programming languages are dealing with step year, etc...)
I managed to get test simplified and working, now it is OK:
#Test
#Order(7)
public void getDateAfterCorrect() throws InterruptedException {
waitIfNeeded();
long today = timeService.getDate().toInstant().getEpochSecond();
long tommorow = timeService.getDateAfter(1).toInstant().getEpochSecond();
Assertions.assertEquals(86400, tommorow - today);
}
but It is still interesting why using other method of comparing that two dates produced such results, if someone with deep level knowledge can answer it, probably few people will be interested.
The java.util Date-Time API is outdated and error-prone. It is recommended to stop using it completely and switch to the modern Date-Time API*.
Apart from that, instead of performing the calculation (subtraction) yourself, you use Instant#until which returns the duration in the specified ChronoUnit.
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
// This is a sample Instant. In your case, it will be returned by
// timeService.getDate().toInstant()
Instant today = Instant.now();
// This is a sample Instant after one day. In your case, it will be returned by
// timeService.getDateAfter(1).toInstant()
Instant tomorrow = today.plus(1, ChronoUnit.DAYS);
long seconds = today.until(tomorrow, ChronoUnit.SECONDS);
// In your case, you will use Assertions.assertEquals(86400, seconds);
System.out.println(seconds);
}
}
Output:
86400
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
The explanation: why 86399? Lack of precision
I am assuming that timeService.getDateAfter(1) returns an old-fashioned Date object. You shouldn’t be using Date in your code anymore, it’s been replaced by java.time, the modern Java date and time API that you are also using, many years ago. But we’re still curious why your code using Date didn’t give the expected result of 86 400 seconds in a day.
Date has milliseconds precision. java.time has nanosecond precision, and since Java 9 the now methods of many of the classes have microsecond precision on most platforms. So for example LocalDateTime.now() returns 0.001234 seconds past a whole second. Some microseconds later, maybe at 0.001432 past the whole second, your time service returns a Date worth 0.001 seconds past the same whole second. Between today at 0.001234 and tomorrow at 0.001 is not a whole day, not fully 24 hours, so the difference in seconds is truncated to 86 399.
When I ran your code in a loop, the first time I got 86 400. It must be because I passed a full millisecond of the second between the two calls. Maybe because of JVM warm-up. The following times I got 86 399.
A possible fix
One way to obtain consistent precision is to truncate everything to milliseconds. Or even to seconds. With the following change to your code I got 86 400 consistently.
LocalDateTime today = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
I got the same result when using ChronoUnit.SECONDS instead of .MILLIS. I believe that this also somewhat explains why the change in your own answer worked. I think you need to be aware, though, that some time does elapse between your two calls, and you cannot control how much. So you may get 86 401 or even a still higher number on rare occasions. Even if I didn’t observe it in my few runs.
I did once work in a place where some of the very many unit tests failed sporadically. It was quite annoying and a source of distrust in the code even when I made it a habit to type comments into the unit tests in question about their sporadic failures. Please do not put yourself and your co-workers in the same situation.

Calculate time difference in minutes given specific date and time [duplicate]

The Java class library has a class named DateTime. DateTime has this method:
int daysBetween(DateTime other)
which returns the number of days between this and the parameter.
It doesn't have a method
int secondsBetween(DateTime other)
which I happen to need.
Is there a class which is similar to DateTime but has such a method?
Not familiar with DateTime...
If you have two Dates you can call getTime on them to get millseconds, get the diff and divide by 1000. For example
Date d1 = ...;
Date d2 = ...;
long seconds = (d2.getTime()-d1.getTime())/1000;
If you have Calendar objects you can call
c.getTimeInMillis()
and do the same
I should like to provide the modern answer. The other answers were fine when this question was asked, but time moves on. Today I recommend you use java.time, the modern Java date and time API.
ZonedDateTime aDateTime = ZonedDateTime.of(2017, 12, 8, 19, 25, 48, 991000000, ZoneId.of("Europe/Sarajevo"));
ZonedDateTime otherDateTime = ZonedDateTime.of(2017, 12, 8, 20, 10, 38, 238000000, ZoneId.of("Europe/Sarajevo"));
long diff = ChronoUnit.SECONDS.between(aDateTime, otherDateTime);
System.out.println("Difference: " + diff + " seconds");
This prints:
Difference: 2689 seconds
ChronoUnit.SECONDS.between() works with two ZonedDateTime objects or two OffsetDateTimes, two LocalDateTimes, etc.
If you need anything else than just the seconds, you should consider using the Duration class:
Duration dur = Duration.between(aDateTime, otherDateTime);
System.out.println("Duration: " + dur);
System.out.println("Difference: " + dur.getSeconds() + " seconds");
This prints:
Duration: PT44M49.247S
Difference: 2689 seconds
The former of the two lines prints the duration in ISO 8601 format, the output means a duration of 44 minutes and 49.247 seconds.
Why java.time?
The Date class used in several of the other answers is now long outdated. Joda-Time also used in a couple (and possibly in the question) is now in maintenance mode, no major enhancements are planned, and the developers officially recommend migrating to java.time, also known as JSR-310.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and there’s a thorough explanation in this question: How to use ThreeTenABP in Android Project.
You should do
org.joda.time.Seconds.secondBetween(date1, date2)
That should do it:
Date a = ...;
Date b = ...;
Math.abs(a.getTime()-b.getTime())/1000;
Here the relevant documentation: Date.getTime(). Be aware that this will only work for dates after January 1, 1970, 00:00:00 GMT
You can use org.apache.commons.lang.time.DateUtils to make it cleaner:
(firstDate.getTime() - secondDate.getTime()) / DateUtils.MILLIS_PER_SECOND
There is no such class as DateTime in the standard Java SE API. Although there is one in joda-time, even that does not have a daysBetween method.
Using the standard Java API, the easiest way to get seconds between two java.util.Date objects would be to subtract their timestamps and divide by 1000:
int secondsBetween = (date1.getTime() - date2.getTime()) / 1000;
It is not recommended to use java.util.Date or System.currentTimeMillis() to measure elapsed times. These dates are not guaranteed to be monotonic and will changes occur when the system clock is modified (eg when corrected from server). In probability this will happen rarely, but why not code a better solution rather than worrying about possibly negative or very large changes?
Instead I would recommend using System.nanoTime().
long t1 = System.nanoTime();
long t2 = System.nanoTime();
long elapsedTimeInSeconds = (t2 - t1) / 1000000000;
EDIT
For more information about monoticity see the answer to a related question I asked, where possible nanoTime uses a monotonic clock. I have tested but only using Windows XP, Java 1.6 and modifying the clock whereby nanoTime was monotonic and currentTimeMillis wasn't.
Also from Java's Real time doc's:
Q: 50. Is the time returned via the
real-time clock of better resolution
than that returned by
System.nanoTime()?
The real-time clock and
System.nanoTime() are both based on
the same system call and thus the same
clock.
With Java RTS, all time-based APIs
(for example, Timers, Periodic
Threads, Deadline Monitoring, and so
forth) are based on the
high-resolution timer. And, together
with real-time priorities, they can
ensure that the appropriate code will
be executed at the right time for
real-time constraints. In contrast,
ordinary Java SE APIs offer just a few
methods capable of handling
high-resolution times, with no
guarantee of execution at a given
time. Using System.nanoTime() between
various points in the code to perform
elapsed time measurements should
always be accurate.
If you're using Joda (which may be coming as jsr 310 in JDK 7, separate open source api until then) then there is a Seconds class with a secondsBetween method.
Here's the javadoc link: http://joda-time.sourceforge.net/api-release/org/joda/time/Seconds.html#secondsBetween(org.joda.time.ReadableInstant,%20org.joda.time.ReadableInstant)
For java 8+ you can use
ChronoUnit.SECONDS.between(temporal1,temporal2)
Which class ? Do you mean the Joda DateTime class ? If so, you can simply call getMillis() on each, and perform the appropriate subtraction/scaling.
I would recommend Joda for date/time work, btw, due to it's useful and intuitive API, and its thread-safety for formatting/parsing options.
Just a pointer:
If you're calculating the difference between two java.util.Date the approach of subtracting both dates and dividing it by 1000 is reasonable, but take special care if you get your java.util.Date reference from a Calendar object.
If you do so, you need to take account of daylight savings of your TimeZone since one of the dates you're using might take place on a DST period.
That is explained on Prasoon's link, I recommend taking some time to read it.
Use this method:
private Long secondsBetween(Date first, Date second){
return (second.getTime() - first.getTime())/1000;
}
Use time unit class.
long timeDifferentInSeconds = TimeUnit.MILLISECONDS.toSeconds(currentDate.getTime()) - TimeUnit.MILLISECONDS.toSeconds(previousDate.getTime());

Creating specific time stamps for objects?

I'm not trying to use the current time at all. Im aware of the calendar and date java utils but I'm not sure how to create my OWN timestamps not dependent on the current time. For instance each object would have an "arrival time" variable in hh:mm:ss format. But I have no clue how to do this. Do I have to create my own class?
It’s not perfectly clear, but I would think you’re after something like this:
public class MyObject {
private static final DateTimeFormatter ARRIVAL_TIME_FORMATTER
= DateTimeFormatter.ofPattern("HH:mm:ss");
// Some instance variables
private LocalTime arrivalTime;
public MyObject(LocalTime arrivalTime) {
this.arrivalTime = arrivalTime;
}
public String getFormattedArrivalTime() {
return arrivalTime.format(ARRIVAL_TIME_FORMATTER);
}
}
As has been said in the comments, use a LocalTime for the time of day. Don’t worry about format when storing the arrival time in your object. Only format it when you need to display it. LocalTime.of creates a LocalTime not related to the current time. There are overloaded versions that also take seconds.
So no, don’t create your own timestamp class. Use LocalTime (or depending on requirements some other class) from java.time, the modern Java date and time API.
Let’s see a brief example of using the above MyObject class:
MyObject obj = new MyObject(LocalTime.of(23, 45));
System.out.println("Object arrived at " + obj.getFormattedArrivalTime());
Output:
Object arrived at 23:45:00
In the output (and there only) the time has been formatted to hh:mm:ss as you requested.
Edit: LocalTime has a precision of nanoseconds. Basil Bourque is correct in his comment that if you know that you don’t want to store any finer precision than whole seconds, it’s easy to truncate the incoming value:
public MyObject(LocalTime arrivalTime) {
this.arrivalTime = arrivalTime.truncatedTo(ChronoUnit.SECONDS);
}
On one hand I’d hesitate to throw information away like this, and time stamps tend to benefit from precision. On the other hand it may also be confusing if your object stores a fraction of second that no one ever sees. You will have to decide yourself.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation of LocalTime

More precision in comparing two dates, that are two hours appart

Can this be tested with more precision? This is a simple function that calculates executionStartTime as two hours behind currentTime.
Function below works, but I would like a function that is more precise.
#Test
public void testCalculateExecutionStartTime() {
Date currentTime = new Date(ZonedDateTime.now(ZoneOffset.UTC).toEpochSecond() * 1000);
Date executionStartTime = proxy.calculateExecutionStartTime(properties.getMessageConfiguration().getExecutionStartTime());
Assert.assertTrue(currentTime.after(executionStartTime));
}
I am assuming that calculateExecutionStartTime is returning a time that is two hours ago compared to the time you call the method, and this is what you want to test. In my first suggestion I am further assuming that you can change the method to return a modern Instant instead of an outdated Date. Since you can use java.time, the modern Java date and time API, this would immediately look like an easy improvement.
The challenge is that the call may take some milliseconds, even some seconds, and we don’t know at what point in time during the call it is reading the clock. So there is no testing using assertEquals with the time that we expect. Instead we read the clock before and after the call. Then our test can rely on the method reading the clock at some point between our two calls. In most cases this will allow us to test the returned time with just a small margin. The idea was already presented by Dawood ibn Kareem in a comment.
#Test
public void testCalculateExecutionStartTime() {
Instant timeBefore = Instant.now();
Instant executionStartTime = proxy.calculateExecutionStartTime(
properties.getMessageConfiguration().getExecutionStartTime());
Instant timeAfter = Instant.now();
Duration expectedTimeAgo = Duration.ofHours(2);
Assert.assertFalse(executionStartTime.isBefore(timeBefore.minus(expectedTimeAgo)));
Assert.assertFalse(executionStartTime.isAfter(timeAfter.minus(expectedTimeAgo)));
}
Note the use of Instant.now() for reading the clock. You don’t need ZonedDateTime nor ZoneOffset.
If you cannot change the return type of calculateExecutionStartTime, just convert the Date you get from it:
Instant executionStartTime = proxy.calculateExecutionStartTime(
properties.getMessageConfiguration().getExecutionStartTime())
.toInstant();
The rest is exactly as before.

Joda-Time getMillisOfDay() seems to be advancing more rapidly than java.util.Date's getTime() milliseconds value

I've just started using Joda-Time and I ran into something that looks quite strange.
When I sleep for 1 ms and call getMillisOfDay() before and after the sleep, Joda-Time is telling me that 119 milliseconds elapsed. Using java.util.Date (creating an instance before and after the sleep, and taking the difference of the getTime() value returned by each) reports a much smaller elapsed time value (2 milliseconds). which is longer than the 1 milliseconds actual sleep time, but I'm guessing the object overhead of creating the Date had an impact, in any case the elapsed time reported for java.util.Date was far less than that reported by Joda-Time.)
Here is my little test method:
#Test(enabled = true)
public void testsleep() {
LocalDateTime now = new LocalDateTime();
System.out.println("jodatime before millisecs:" + now.getMillisOfDay());
Thread.sleep(1);
LocalDateTime now2 = new LocalDateTime();
System.out.println("jodatime after millisecs:" + now2.getMillisOfDay());
System.out.println("java.util.DATE before call:" + new Date().getTime());
Thread.sleep(1);
Date after = new Date();
System.out.println("java.util.DATE after call:" + after.getTime());
println "done"
}
output:
jodatime before millisecs:76672505
jodatime after millisecs:76672624
java.util.DATE before call:1396671472633
java.util.DATE after call:1396671472635
analysis:
jodatime after millisecs:76672624
jodatime before millisecs:76672505
----------------
119 millisecs elapsed according to joda time
java.util.DATE after call:1396671472635
java.util.DATE before call:1396671472633
----------------
2 millisecs elapsed according to java util Date
I thought Joda-Time was better that java.util.Date. Well, I'm sure it is, and I’m just using it wrong. but I don’t see how. Any advice much appreciated !
I wouldn't use neither Date nor LocalDateTime for measuring short intervals of time. The main reason for this in written in your question, there is a considerable overhead for object creation (check out LocalDateTime source code). Also keep in mind that the JVM need some time to warm up (let JVM optimizations kick in and do its magic).
While you shouldn't benchmark like this (there are lot of good benchmark tools for Java), a good low level alternative is System.nanoTime(). StopWatch classes might also come in handy (like Guava, Commons Lang and Spring implementations).
Finally, remember that Thread.sleep is itself subject to precision problems.
// Run several times to *warm up*
for (int i = 0; i < 100; i++) {
double init = System.nanoTime();
Thread.sleep(1);
long end = System.nanoTime();
System.out.printf("Elapsed time: %.2f milliseconds\n", (end - init) * 1e-6);
}

Categories

Resources