This question already has answers here:
integer giving negative values in java in multiplication using positive numbers [duplicate]
(4 answers)
Closed 3 years ago.
I know this has been asked on here many times previously, but I'm haven't been able to find anything specific to my case. I'm trying to find the difference between the current datetime and a previous datetime, each with the format yyyy-MM-dd HH:mm:ss.s. Based on the answer given here, I've come up with the following code:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.s");
String earliestRunTime = "2017-12-16 01:30:08.0";
Date currentDate = new Date();
log.info("Current Date: {}", format.format(currentDate));
try {
Date earliestDate = format.parse(earliestRunTime);
long diff = currentDate.getTime() - earliestDate.getTime();
long diffSeconds = diff / 1000 % 60;
long diffMinutes = diff / (60 * 1000) % 60;
long diffHours = diff / (60 * 60 * 1000) % 24;
long diffDays = diff / (24 * 60 * 60 * 1000) % 30;
long diffMonths = diff / (30 * 24 * 60 * 60 * 1000) % 12;
long diffYears = diff / (12 * 30 * 24 * 60 * 60 * 1000);
return String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
diffYears, diffMonths, diffDays, diffHours, diffMinutes, diffSeconds);
}
catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
When I run the code, the JSON returns the following result:
lifetime: "41 years, -1 months, 14 days, 9 hours, 42 minutes, 37 seconds"
I have two questions here:
Where am I going wrong in my calculations 41 years and a negative number?
Is there a better way for me to do this? My current setup does not consider leap years or a 365 day year, and I need to take these into account.
Where am I going wrong in my calculations 41 years and a negative number?
Because the denominator will overflow. You need to use Long:
long diffMonths = diff / (30 * 24 * 60 * 60 * 1000L) % 12; //Notice the L at the end
long diffYears = diff / (12 * 30 * 24 * 60 * 60 * 1000L); //Notice the L at the end
Also note that 12 * 30 is a really bad approximation of the number of days in a year.
Is there a better way for me to do this?
Yes. Use Duration api from Java 8. https://www.mkyong.com/java8/java-8-period-and-duration-examples/
It's hard to give precise answer, because the question is a bit vague. For example - If one of the year was a leap year and you were comparing dates 2020/03/28 and 2021/03/28, what should be the result? 1 year or 1 years, 1 days? (2020 is a leap year so after 03/28, there's also 03/29)
Where am I going wrong in my calculations 41 years and a negative number?
Apart from using the notoriously troublesome and long outdated SimpleDateFormat class and the just as outdated Date there are the following bugs in your code:
You are parsing 08.0 as 8 seconds 0 seconds. On my JDK-11 SimpleDateFormat opts for the 0 seconds and discards the 8 seconds that I think are correct. SimpleDateFormat cannot parse one decimal on the seconds (only exactly three decimals), so the solution to this bug is discarding SimpleDateFormat altogether.
As others have said you have an int overflow in your multiplications. For example, 30 * 24 * 60 * 60 * 1000 should give 2 592 000 000, but since an int cannot hold this number, you get -1 702 967 296 instead. Since this is a negative number, the following division gives you a negative number of months.
As Solomon Slow pointed out in a comment, a month may be 28, 29, 30 or 31 days. When setting all months to 30 days you risk incorrect numbers of days and months and in the end also years. When I ran your code today, the correct answer would have been 1 year, 4 months, 13 days, but I got 19 days instead, 6 days too much.
You are not taking summer time (DST) and other time anomalies into account. These may cause a day to be for example 23 or 25 hours, giving an error.
Or to sum up: Your error was that you tried to do the calculation “by hand”. Date and time math is too complex and error-prone to do this. You should always leave it to well-proven library classes instead.
Is there a better way for me to do this? My current setup does not consider leap years or a 365 day year, and I need to take these into
account.
Yes, there is a much better way. The best way may be to use the PeriodDuration class from the ThreeTen Extra project, see the link below. I am not going to install that library in my computer right now, so I will just show the good and modern solution using built-in classes:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
LocalDateTime currentDateTime = LocalDateTime.now(ZoneId.of("Australia/Sydney"));
String earliestRunTime = "2017-12-16 01:30:08.0";
LocalDateTime earliestDateTime = LocalDateTime.parse(earliestRunTime, dtf);
// We want to find a period (years, months, days) and a duration (hours, minutes, seconds).
// To do that we cut at the greatest possible whole number of days
// and then measure the period before the cut and the duration after it.
LocalDateTime cut = earliestDateTime.plusDays(
ChronoUnit.DAYS.between(earliestDateTime, currentDateTime));
Period p = Period.between(earliestDateTime.toLocalDate(), cut.toLocalDate());
Duration d = Duration.between(cut, currentDateTime);
String result = String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
p.getYears(), p.getMonths(), p.getDays(),
d.toHours(), d.toMinutesPart(), d.toSecondsPart());
System.out.println(result);
When I ran the code just now I got:
1 years, 4 months, 13 days, 19 hours, 26 minutes, 7 seconds
In java.time, the modern Java date and time API, a Period is an amount of years, months and days, and a Duration is an amount of hours, minutes, seconds and fraction of second (down to nanoseconds). Since you wanted both, I am using both classes.
The toXxxPart methods of Duration I am using were introduced in Java 9. If you are using Java 8 (or the ThreeTen Backport) printing the minutes and seconds is a little bit more complicated. Search for java format duration or similar to learn how.
I am still not taking summer time into account. To do that we would need to know the time zone of the earliest run time string and then use ZonedDateTime instead of LocalDateTime. The code would otherwise be very similar.
Links
ThreeTen Extra
Documentation of PeriodDuration
Oracle Tutorial: Date Time explaining how to use java.time
Using the same approach you did, you need to explicitly identify the denominator as long values. Currently, it assumes them to be integers, which causes a numeric overflow - meaning the value computed is too large for a integer. This would explain why you get negative/arbitrary values. Fix is simple:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.s");
String earliestRunTime = "2017-12-16 01:30:08.0";
Date currentDate = new Date();
log.info("Current Date: {}" + format.format(currentDate));
try {
Date earliestDate = format.parse(earliestRunTime);
long diff = currentDate.getTime() - earliestDate.getTime();
long diffSeconds = diff / 1000L % 60L;
long diffMinutes = diff / (60L * 1000L) % 60L;
long diffHours = diff / (60L * 60L * 1000L) % 24L;
long diffDays = diff / (24L * 60L * 60L * 1000L) % 30L;
long diffMonths = diff / (30L * 24L * 60L * 60L * 1000L) % 12L;
long diffYears = diff / (12L * 30L * 24L * 60L * 60L * 1000L);
return String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
diffYears, diffMonths, diffDays, diffHours, diffMinutes, diffSeconds);
}
catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
Something doesn't seem right and i just dont know how I can fix it.
I want to know the difference in days between 2 dates. Now I implemented a function, which calculates the differences from milliseconds to days for util.date objects
public long calculateNumberOfDays(Date from, Date to) {
return (to.getTime() - from.getTime()) / (1000*60*60*24);
}
My jUnit test told me, there was an error with this function, so I rewrote it using LocalDate and the ChronoUnit.DAYS.between function. It worked like a charm.
Wanting to know what the differences between those two functions were, I wrote this little test:
for(int numberDays = 1; numberDays<10; numberDays++){
LocalDate fromLD = LocalDate.now();
LocalDate toLD = fromLD.plusDays(numberDays);
Date fromD = Date.valueOf(fromLD);
Date toD = Date.valueOf(toLD);
long diffMS = toD.getTime() - fromD.getTime();
double diffDays = diffMS/(1000*60*60*24);
long numDaysDate = DAYS.between(fromLD, toLD);
System.out.println(numberDays+" = "+diffDays+"/"+numDaysDate);
}
It resulted in the following output:
1 = 1.0/1
2 = 2.0/2
3 = 3.0/3
4 = 4.0/4
5 = 4.0/5
6 = 5.0/6
7 = 6.0/7
8 = 7.0/8
9 = 8.0/9
Can someone explain to me, how this is possible? (1-4 it works, 5-9 util.date has lost a day)
Dates are hard. A java Date is a date and time, so when you set it to an actual date, it means midnight on that date.
Daylight savings time kicks in any day now (at least over here), so midnight on Monday will be 23 hours after midnight on Sunday.
Dividing integers rounds down, so 4 days and 23 hours is 4 days
Casting the result of an integer division to a double is too late; you need to cast either or both of the inputs:
double diffDays = diffMS/(1000*60*60*24);
4.0
double diffDays = diffMS/(1000.0*60*60*24);
4.958333...
This question already has answers here:
How to subtract X days from a date using Java calendar?
(11 answers)
Closed 6 years ago.
I want to subtract days from date in java. But I dont want to use external libraries. I have referred some of questions from stackoverflow but they are suggesting to use external libraries. So I have applied following logic
noOfDays = 24;
Date compareDate = new Date(currentDate - noOfDays * 24 * 60 * 60 * 1000);
System.out.println("compare date " + compareDate);
It is working fine till 24 days.But after 24 days it is giving unexpected result. Is there any solution to this ?
Use java.util.Calendar.
Something like that:
Calendar c = new Calendar()
c.setTime(currentDate);
c.add(Calendar.DAY_OF_MONTH, noOfDays)
compareDate = c.getTime()
You can use a LocalDate (which is part of the JDK since Java 8):
LocalDate today = LocalDate.now();
LocalDate compareDate = today.minusDays(24);
You computation is about integers, which won't fit higher values than the max integer value.
Declare your variable as a long :
long noOfDays = 24;
Date compareDate = new Date(currentDate - noOfDays * 24 * 60 * 60 * 1000);
System.out.println("compare date " + compareDate);
However, as the comments said, this is not the best approach for substracting days, so have a look at better solutions in other answers.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have two date values.
Date now=new Date();
Another date variable.
I want to find out the difference bet them in xxxDays XXXHours XXMin XXXSeconds.How can I achieve this.
Date now=new Date();
Date date2="2013-07-04 18:06:27"; //(I will get this from a DB).
String/Date dateDiff=date2.getTime()-now.getTime();
Some thing like this
The difference you get from two dates is in miliseconds, so your code should look something like this:
var difference = date1 - date2;
var days = difference / (1000*60*60*24);
var hours = (difference - days*1000*60*60*24) / (1000*60*60);
var minutes = (difference - days*1000*60*60*24 - hours*1000*60*60) / (1000*60)
var seconds = (difference - days*1000*60*60*24 - hours*1000*60*60 - minutes*1000*60)/ 1000
With java.util.date:
int diffInDays = (int)((newDate.getTime() - oldDate.getTime()) / (1000*60*60*24))
Note that this works with UTC dates.
Another approach using TimeUnit
final SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy HH:mm");
final Date old = fmt.parse("10/7/2013 10:10");
final Date now = fmt.parse("12/7/2013 12:12");
long dif = now.getTime() - old.getTime();
final long days = TimeUnit.MILLISECONDS.toDays(dif);
dif -= TimeUnit.DAYS.toMillis(days);
final long hours = TimeUnit.MILLISECONDS.toHours(dif);
dif -= TimeUnit.HOURS.toMillis(hours);
long mins = TimeUnit.MILLISECONDS.toMinutes(dif);
System.out.format("%d days, %d hours, %d mins\n", days, hours, mins);
Which correctly prints:
2 days, 2 hours, 2 mins
I am calculating the difference between two sql dates, TripStartDate and TripEndDate.
If TripStartDate= 2011-03-04 09:35:00 and TripEndDate = 2011-03-04 10:35:00 then I should get the number of day is 1 (because trip happened on that day).
Like this:
If TripStartDate = 2011-03-04 09:35:00 and TripEndDate = 2011-03-05 09:35:00 then method should return 2 days (because trip happened on both days).
If TripStartDate = 2011-03-04 09:35:00 and TripEndDate = 2011-04-04 09:35:00 then method should return 32 days. (because 28 days in march and 4 days in April).
Calculation should be based on only dates and month of year (not taking time in consideration). Please help me . Thanks in advance...
In Java I guess you would drop the time and calculate the day difference
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.set(2011, 03, 04);
cal2.set(2011, 04, 04);
long milis1 = cal1.getTimeInMillis();
long milis2 = cal2.getTimeInMillis();
long diff = milis2 - milis1;
long days = diff / (24 * 60 * 60 * 1000);
EDIT Quite surprisingly for me, that ^ code really doesn't work always... apparently there are some 'leap seconds' that mess up the maths. There are quite enough links already proposed to you in comments. I would go with joda time library.
FYI, in Groovy this would be something along the lines of:
fourthMarch = Date.parse( 'yyyy-MM-dd', '2011-03-04' )
fifthMarch = Date.parse( 'yyyy-MM-dd', '2011-03-05' )
fourthApril = Date.parse( 'yyyy-MM-dd', '2011-04-04' )
assert 2 == fifthMarch - fourthMarch + 1
assert 32 == fourthApril - fourthMarch + 1
We need to add 1 as the dates are inclusive
Here's the usual way to do this, in Java 8.
LocalDate start = LocalDate.of(2011, 3, 4); // Or whatever - this is Y, M, D
LocalDate end = LocalDate.of(2011, 4, 4);
return ChronoUnit.DAYS.between(start, end) + 1;
// The +1 is for the inclusive reckoning