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

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.

Related

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());

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.

Converting epoch time to the hour

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.

Converting duration to years in Java8 Date API?

I have a date in the far past.
I found out what the duration is between this date and now.
Now I would like to know - how much is this in years?
I came up withthis solution using Java8 API.
This is a monstrous solution, since I have to convert the duration to Days manually first, because there will be an UnsupportedTemporalTypeException otherwise - LocalDate.plus(SECONDS) is not supported for whatever reason.
Even if the compiler allows this call.
Is there a less verbous possibility to convert Duration to years?
LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6);
Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(),
LocalDate.now().plus(
TimeUnit.SECONDS.toDays(
durationSinceGuss1.getSeconds()),
ChronoUnit.DAYS) );
/*
* ERROR -
* LocalDate.now().plus(durationSinceGuss1) causes an Exception.
* Seconds are not Supported for LocalDate.plus()!!!
* WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss) );
/*
* ERROR -
* Still an exception!
* Even on explicitly converting duration to seconds.
* Everything like above. Seconds are just not allowed. Have to convert them manually first e.g. to Days?!
* WHY OR WHY CAN'T YOU CONVERT SECONDS TO DAYS OR SOMETHING AUTOMATICALLY, JAVA?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss.getSeconds(), ChronoUnit.SECONDS) );
Have you tried using LocalDateTime or DateTime instead of LocalDate? By design, the latter does not support hours/minutes/seconds/etc, hence the UnsupportedTemporalTypeException when you try to add seconds to it.
For example, this works:
LocalDateTime dateOne = LocalDateTime.of(1415, Month.JULY, 6, 0, 0);
Duration durationSinceGuss1 = Duration.between(dateOne, LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDateTime.now(), LocalDateTime.now().plus(durationSinceGuss1) );
System.out.println(yearsSinceGuss); // prints 600
Although the accepted answer of #Matt Ball tries to be clever in usage of the Java-8-API, I would throw in following objection:
Your requirement is not exact because there is no way to exactly convert seconds to years.
Reasons are:
Most important: Months have different lengths in days (from 28 to 31).
Years have sometimes leap days (29th of February) which have impact on calculating year deltas, too.
Gregorian cut-over: You start with a year in 1415 which is far before first gregorian calendar reform which cancelled full ten days, in England even 11 days and in Russia more. And years in old Julian calendar have different leap year rules.
Historic dates are not defined down to second precision. Can you for example describe the instant/moment of the battle of Hastings? We don't even know the exact hour, just the day. Assuming midnight at start of day is already a rough and probably wrong assumption.
Timezone effects which have impact on the length of day (23h, 24h, 25h or even different other lengths).
Leap seconds (exotic)
And maybe the most important objection to your code:
I cannot imagine that the supplier of the date with year 1415 has got the intention to interprete such a date as gregorian date.
I understand the wish for conversion from seconds to years but it can only be an approximation whatever you choose as solution. So if you have years like 1415 I would just suggest following very simple approximation:
Duration d = ...;
int approximateYears = (int) (d.toDays() / 365.2425);
For me, it is sufficient in historic context as long as we really want to use a second-based duration for such an use-case. It seems you cannot change the input you get from external sources (otherwise it would be a good idea to contact the duration supplier and ask if the count of days can be supplied instead). Anyway, you have to ask yourself what kind of year definition you want to apply.
Side notes:
Your complaint "WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?" does not match the character of new java.time-API.
You expect the API to be type-safe, but java.time (JSR-310) is not designed as type-safe and heavily relies on runtime-exceptions. The compiler will not help you with this API. Instead you have to consult the documentation in case of doubt if any given time unit is applicable on any given temporal type. You can find such an answer in the documentation of any concrete implementation of Temporal.isSupported(TemporalUnit). Anyway, the wish for compile-safety is understandable (and I have myself done my best to implement my own time library Time4J as type-safe) but the design of JSR-310 is already set in stone.
There is also a subtile pitfall if you apply a java.time.Duration on either LocalDateTime or Instant because the results are not exactly comparable (seconds of first type are defined on local timeline while seconds of Instant are defined on global timeline). So even if there is no runtime exception like in the accepted answer of #Matt Ball, we have to carefully consider if the result of such a calculation is reasonable and trustworthy.
Use Period to get the number of years between two LocalDate objects:
LocalDate before = LocalDate.of(1415, Month.JULY, 6);
LocalDate now = LocalDate.now();
Period period = Period.between(before, now);
int yearsPassed = period.getYears();
System.out.println(yearsPassed);

Formatting a Duration in Java 8 / jsr310

I am transitioning a project from Joda-Time to java8's native time libraries, and I have run into a snag.
I have been unable to find a formatter for Duration. I would like to have a custom String format of, for instance, HHH+MM, where a Duration of 75 hours and 15 minutes would format as "75+15".
This was easy to do with Joda-Time by converting to period, and using a PeriodFormatter, but I have been unable to find this type of class in Java8. Am I missing something?
Java 9 and later: Duration::to…Part methods
In Java 9 the Duration class gained new to…Part methods for returning the various parts of days, hours, minutes, seconds, milliseconds/nanoseconds. See this pre-release OpenJDK source code.
Given a duration of 49H30M20.123S…
toNanosPart() = 123000000
toMillisPart() = 123
toSecondsPart() = 20
toMinutesPart() = 30
toHoursPart() = 1
toDaysPart() = 2
Remember that “days” here means chunks of 24-hours, ignoring dates on a calendar. If you care about dates, use Period class instead.
I do not know if any additional formatter features are added. But at least you will be able to more conveniently generate your own strings from numbers obtained via these new getter methods.
Java 8
Oddly enough, no convenient getter methods for these values were included in the first edition release of java.time in Java 8. One of very few oversights in the otherwise excellent design of the java.time framework.
See the related Question: Why can't I get a duration in minutes or hours in java.time?.
There is no period/duration-formatter in jsr-310, different from JodaTime. Not every feature of JodaTime was ported to JSR-310 (for example also not PeriodType). And in reverse JSR-310 has some features which are not available in JodaTime (for example localized weekday numbers or the strategy pattern approach with adjusters).
It might happen that Java 9 will introduce some kind of built-in period formatting (read something about this from S. Colebourne).
Conclusion: JSR-310 and JodaTime are not fully compatible to each other, so a lot of work can be required. I would not be so keen on migration as soon as possible. Do you need special features of JSR-310 which are not offered by JodaTime?
Additional note: You should also be aware of the fact that joda period (which includes all units from years to seconds) is not fully compatible with jsr310-period (only years, months, days) or jsr310-duration (only hours, minutes, seconds and fraction seconds).
There is no built-in method but you can access the number of hours/minutes without having to calculate them manually. Your specific format could look like:
Duration d = Duration.of(75, HOURS).plusMinutes(15);
long hours = d.toHours(); //75
long minutes = d.minusHours(hours).toMinutes(); //15
String HH_PLUS_MM = hours + "+" + minutes; //75+15
System.out.println(HH_PLUS_MM);
If the duration is guaranteed to be less than 24 hours, you can also use this trick:
String hhPlusMm = LocalTime.MIDNIGHT.plus(d).format(DateTimeFormatter.ofPattern("HH+mm"));
you can use the DurationFormatUtils from commons-lang3-time (for minutes you have to use "mm" as the format is the same as in SimpleDateFormat):
DurationFormatUtils.formatDuration(interval.toMillis(), "HHH+mm")
Sadly I found no way to exclude empty parts, like in my case days or hours could be 0, so I still had to roll my own.
Update: I have opened an issue for this on Apache commons.lang.time.DurationFormatUtils JIRA.
I know this is an old question but I recently ran into the same thing. There really should be a better solution than this, but this worked for me:
public static String millisToElapsedTime(long millis){
DateFormat fmt = new SimpleDateFormat(":mm:ss.SSS");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
return (millis/3600000/*hours*/)+fmt.format(new Date(millis));
}
Then, you could add this:
public static String durationToElapsedTime(Duration d){
return millisToElapsedTime(d.toMillis());
}

Categories

Resources