I have an array of java.util.Date objects. I am trying to find the average.
For example, if I have 2 date objects with 7:40AM and 7:50AM. I should get an average date object of 7:45AM.
The approach I am thinking of is inefficient:
for loop through all dates
find difference between 0000 and time
add that time diff to a total
divide that by the total count
convert that time to a date object
Is there an easier function to do this?
Well fundamentally you can just add up the "millis since the Unix epoch" of all the Date objects and find the average of those. Now the tricky bit is avoiding overflow. Options are:
Divide by some known quantity (e.g. 1000) to avoid overflow; this reduces the accuracy by a known amount (in this case to the second) but will fail if you have more than 1000 items
Divide each millis value by the number of dates you're averaging over; this will always work, but has hard-to-understand accuracy reduction
Use BigInteger instead
An example of approach 1:
long totalSeconds = 0L;
for (Date date : dates) {
totalSeconds += date.getTime() / 1000L;
}
long averageSeconds = totalSeconds / dates.size();
Date averageDate = new Date(averageSeconds * 1000L);
An example of approach 3:
BigInteger total = BigInteger.ZERO;
for (Date date : dates) {
total = total.add(BigInteger.valueOf(date.getTime()));
}
BigInteger averageMillis = total.divide(BigInteger.valueOf(dates.size()));
Date averageDate = new Date(averageMillis.longValue());
With a lot of dates, taking the sum of all dates together will certainly go into an overflow. If you want to prevent that you should do it like this (in pseudo code):
var first = dates.getFirst
var sumOfDifferences = 0
loop over all dates
for each date sumOfDifferences += date - first
var averageDate = first + sumOfDifferences/countOfDates
This will never make you run in an overflow.
There's already an answer here: Sum two dates in Java
You just need to sum up all your date objects using getTime() and then divide it by the number of objects and convert it back to a Date object. Done.
Try this. In here each date convert to long value my getTime(). This will return mil-second value. Then we can proceed.
SimpleDateFormat sdf = new SimpleDateFormat("HH:mma");
Date date1=sdf.parse("7:40AM");
Date date2=sdf.parse("7:50AM");
long date1InMilSec=date1.getTime();
long date2InMilSec=date2.getTime();
System.out.println("Average "+sdf.format((date1InMilSec+date2InMilSec)/2));
To avoid overflow in caluclation of average time:
first sort by date;
store value of first date in time0;
Caluclate the average of the deltaTimes by subtractiong first time0 from all times.
Then sum up and divide.
The result = new Date(time0 + avgDeltas);
Once java 8 is published, you can use
Date avgDate = new Date( LongStream.of(datesAsLongArray).sum() / datesAsLongArray.length * 1000);
Related
I have been trying to figured out an algorithm to return a list of time based on a start time and end time and how many loop. For example the start time at 6 am and the end time at 10 pm and the looping number is 5 so I need to return 22-6 = 16 and 16/5 = 3.2 so I need to return
6:00:00
9:20:00
12:40:00
15:60:00
18:20:00
21:40:00
I need to return such these values. (Note, the return value I wrote are not accurate but just for the purpose of demonstration)
The current code:
// List<Time> times(int looping){
long test(){
List<Time> result = new ArrayList<Time>();
String start = "06:00:00";
String finish = "22:00:00";
Time startTime = Time.valueOf(start);
Time endTime = Time.valueOf(finish);
long totalHours = endTime.getTime() - startTime.getTime();
return totalHours;
// return result;
}
Note: the long totalHours return a strange number not 16 and I'm not sure how to loop throw time and return the wanted values.
java.time
This is one of the places where java.time, the modern Java date and time API, excels. The method Duration::dividedBy does just want you want, dividing a span of time into a certain number of chunks.
List<LocalTime> result = new ArrayList<>();
String start = "06:00:00";
String finish = "22:00:00";
LocalTime startTime = LocalTime.parse(start);
LocalTime endTime = LocalTime.parse(finish);
Duration totalTime = Duration.between(startTime, endTime);
int subintervalCount = 5;
Duration subintervalLength = totalTime.dividedBy(subintervalCount);
LocalTime currentTime = startTime;
for (int i = 0; i < subintervalCount; i++) {
result.add(currentTime);
currentTime = currentTime.plus(subintervalLength);
}
System.out.println(result);
This outputs:
[06:00, 09:12, 12:24, 15:36, 18:48]
Where did the strange number of total hours come from?
the long totalHours return a strange number not 16 and I'm not sure
how to loop throw time and return the wanted values.
The Time class doesn’t define a getTime method. Instead you are calling the getTime method of the superclass java.util.Date, another poorly designed and long outdated class that we should no longer use. This getTime retunrs the count of milliseconds since the epoch of 1970-01-01T00:00:00 UTC, something that does not make sense for a time of day. I consider it likely that your subtraction yielded the number of milliseconds rather than the number of hours between your two times.
Edit: In case you’re curious and want to check: 16 hours equals 57 600 000 milliseconds. I obtained the number from TimeUnit.HOURS.toMillis(16).
Link
Oracle tutorial: Date Time explaining how to use java.time.
NOTE THIS IS NOT A DUPLICATE OF EITHER OF THE FOLLOWING
Calculating the difference between two Java date instances
calculate months between two dates in java [duplicate]
I have two dates:
Start date: "2016-08-31"
End date: "2016-11-30"
Its 91 days duration between the above two dates, I expected my code to return 3 months duration, but the below methods only returned 2 months. Does anyone have a better suggestion? Or do you guys think this is a bug in Java 8? 91 days the duration only return 2 months.
Thank you very much for the help.
Method 1:
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 2:
long daysBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 3:
I tried to use Joda library instead of Java 8 APIs, it works. it loos will return 3, It looks like Java duration months calculation also used days value. But in my case, i cannot use the Joda at my project. So still looking for other solutions.
LocalDate dateBefore= LocalDate.parse("2016-08-31");
LocalDate dateAfter = LocalDate.parse("2016-11-30");
int months = Months.monthsBetween(dateBefore, dateAfter).getMonths();
System.out.println(months);
Since you don't care about the days in your case. You only want the number of month between two dates, use the documentation of the period to adapt the dates, it used the days as explain by Jacob. Simply set the days of both instance to the same value (the first day of the month)
Period diff = Period.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(diff); //P3M
Same with the other solution :
long monthsBetween = ChronoUnit.MONTHS.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(monthsBetween); //3
Edit from #Olivier Grégoire comment:
Instead of using a LocalDate and set the day to the first of the month, we can use YearMonth that doesn't use the unit of days.
long monthsBetween = ChronoUnit.MONTHS.between(
YearMonth.from(LocalDate.parse("2016-08-31")),
YearMonth.from(LocalDate.parse("2016-11-30"))
)
System.out.println(monthsBetween); //3
Since Java8:
ChronoUnit.MONTHS.between(startDate, endDate);
//Backward compatible with older Java
public static int monthsBetween(Date d1, Date d2){
if(d2==null || d1==null){
return -1;//Error
}
Calendar m_calendar=Calendar.getInstance();
m_calendar.setTime(d1);
int nMonth1=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
m_calendar.setTime(d2);
int nMonth2=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
return java.lang.Math.abs(nMonth2-nMonth1);
}
The documentation of Period#between states the following:
The start date is included, but the end date is not.
Furthermore:
A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
Your end day-of-month 30 is not greater than or equal to your start day-of-month 31, so a third month is not considered.
Note the parameter names:
public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
To return 3 months, you can increment the endDateExclusive by a single day.
In case you want stick to java.time.Period API
As per java.time.Period documentation
Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
where
#param startDateInclusive the start date, inclusive, not null
#param endDateExclusive the end date, exclusive, not null
So it is better to adjust your implementation to make your end date inclusive and get your desired result
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30").plusDays(1));
System.out.println("Months : " + diff.getMonths());
//Output -> Months : 3
You have to be careful, never use LocalDateTime to calculate months between two dates the result is weird and incorrect, always use LocalDate !
here's is some code to prove the above:
package stack.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class TestMonthsDateTime {
public static void main(String[] args) {
/**------------------Date Time----------------------------*/
LocalDateTime t1 = LocalDateTime.now();
LocalDateTime t2 = LocalDateTime.now().minusMonths(3);
long dateTimeDiff = ChronoUnit.MONTHS.between(t2, t1);
System.out.println("diff dateTime : " + dateTimeDiff); // diff dateTime : 2
/**-------------------------Date----------------------------*/
LocalDate t3 = LocalDate.now();
LocalDate t4 = LocalDate.now().minusMonths(3);
long dateDiff = ChronoUnit.MONTHS.between(t4, t3);
System.out.println("diff date : " + dateDiff); // diff date : 3
}
}
My 2%
This example checks to see if the second date is the end of that month. If it is the end of that month and if the first date of month is greater than the second month date it will know it will need to add 1
LocalDate date1 = LocalDate.parse("2016-08-31");
LocalDate date2 = LocalDate.parse("2016-11-30");
long monthsBetween = ChronoUnit.MONTHS.between(
date1,
date2);
if (date1.isBefore(date2)
&& date2.getDayOfMonth() == date2.lengthOfMonth()
&& date1.getDayOfMonth() > date2.getDayOfMonth()) {
monthsBetween += 1;
}
After the short investigation, still not totally fix my question, But I used a dirty solution to avoid return the incorrect duration. At least, we can get the reasonable duration months.
private static long durationMonths(LocalDate dateBefore, LocalDate dateAfter) {
System.out.println(dateBefore+" "+dateAfter);
if (dateBefore.getDayOfMonth() > 28) {
dateBefore = dateBefore.minusDays(5);
} else if (dateAfter.getDayOfMonth() > 28) {
dateAfter = dateAfter.minusDays(5);
}
return ChronoUnit.MONTHS.between(dateBefore, dateAfter);
}
The Java API response is mathematically accurate according to the calendar. But you need a similar mechanism, such as rounding decimals, to get the number of months between dates that matches the human perception of the approximate number of months between two dates.
Period period = Period.between(LocalDate.parse("2016-08-31"), LocalDate.parse("2016-11-30"));
long months = period.toTotalMonths();
if (period.getDays() >= 15) {
months++;
}
I am trying to calculate the difference between two times, which are represented as longs in the Format HHmm 24 hour time. E.g 4:30pm is represented by the long 0430.
I am happy for the difference to be in minutes.
Is there a simple calculation that can be done to achieve this? I am aware of Java's Date class, however I want to avoid having to store dummy date information just for a calculation on time.
Thanks!
Putting aside the fact that this is a really, really bad way to store times, the easiest way to do this is to convert the HHMM time to minutes since the start of the day:
long strangeTimeFormatToMinutes(long time) {
long minutes = time % 100;
long hours = time / 100;
return minutes + 60 * hours;
}
Then just use plain old subtraction to get the difference.
You may also want to add validation that minutes and hours are in the ranges you expect, i.e. 0-59 and 0-23.
You mentioned that you didn't want to use the Date class because it required you to use a dummy date. The LocalTime class does not require that.
LocalTime start = LocalTime.of(6,15,30,200); // h, m, s, nanosecs
LocalTime end = LocalTime.of(6,30,30,320);
Duration d = Duration.between(start, end);
System.out.println(d.getSeconds()/60);
Pad zeros
First convert your integer to a 4-character string, padding with leading zeros.
For example, 430 becomes 0430 and parsed as 04:30. Or, 15 becomes 0015 and parsed as quarter past midnight, 00:15.
String input = String.format( "%04d", yourTimeAsInteger );
LocalDate
The LocalTime class represents a time-of-day value with no date and no time zone.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "HHmm" );
LocalTime ld = LocalTime.parse( input , f ) ;
This question already has an answer here:
How to calculate difference between two dates in years...etc with Joda-Time
(1 answer)
Closed 8 years ago.
I have a long-variable which represents an amount of delay in milliseconds. I want to transform this long to some kind of Date where it says how many hours, minutes, seconds, days, months, years have passed.
When using Date toString() from Java, as in new Date(5).toString, it says 5 milliseconds have passed from 1970. I need it to say 5 milliseconds have passed, and 0 minutes, hours, ..., years.
you cannot get direct values , without any reference date for your requirements, you need define first reference value like below:
String dateStart = "01/14/2012 09:29:58";
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
Date d1 = format.parse(dateStart);
the above is your reference date , now you need to find the current date and time using following.
long currentDateTime = System.currentTimeMillis();
Date currentDate = new Date(currentDateTime);
Date d2.format(currentDate)
and the difference of these values like long diff=d2-d1 will gives values in milliseconds.
then
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);
and similarly for months and years.
you can also refer the example given on this link for more information http://javarevisited.blogspot.in/2012/12/how-to-convert-millisecond-to-date-in-java-example.html
From what I understand from your question you could achieve your goal by writing a method that will suit your needs i.e.:
static public String dateFromMili (long miliseconds) {
// constants that will hold the number of miliseconds
// in a given time unit (year, month etc.)
final int YEAR_IN_MILISECONDS = 12*30*24*60*60*1000;
final int MONTH_IN_MILISECONDS = 30*24*60*60*1000;
final int DAY_IN_MILISECONDS = 24*60*60*1000;
final int HOUR_IN_MILISECONDS = 60*60*1000;
final int MINUTE_IN_MILISECONDS = 60*1000;
final int SECONDS_IN_MILISECONDS = 1000;
// now use those constants to return an appropriate string.
return miliseconds +" miliseconds, "
+miliseconds/SECONDS_IN_MILISECONDS+" seconds, "
+miliseconds/MINUTE_IN_MILISECONDS+" minutes, "
+miliseconds/HOUR_IN_MILISECONDS+" hours, "
+miliseconds/DAY_IN_MILISECONDS+" days, "
+miliseconds/MONTH_IN_MILISECONDS+" months, "
+miliseconds/YEAR_IN_MILISECONDS+" years have passed";
}
Than you will have to pas the number of miliseconds as a parameter to your new function that will return the desired String (i.e for two seconds):
dateFromMili (2000);
You could also print your answer:
System.out.println(dateFromMili(2000));
The result would look like this:
2000 miliseconds, 2 seconds, 0 minutes, 0 hours, 0 days, 0 months, 0 years have passed
Note that this method will return Strings with integer value (you will not get for example "2.222333 years" but "2 years"). Furthermore, it could be perfected by changing the noun from plural to singular, when the context is appropriate ("months" to "month").
I hope my answer helped.
This is how I solved the problem:
I used a library called Joda-Time (http://www.joda.org/joda-time/) (credits to Keppil!)
Joda-Time has various data-structures for Date and Time. You can represent a date and time by a DateTime-object.
To represent the delay I was looking for, I had two options: a Period data-structure or a Duration data-structure. A good explanation of the difference between those two can be found here: Joda-Time: what's the difference between Period, Interval and Duration? .
I thus used a Duration-object, based on the current date of my DateTime-object. It has all the methods to convert the amount of milliseconds to years, months, weeks, days, hours, minutes and seconds.
How can I learn how many days passed from a spesific date? Which package i need to use and how?
Just for the protocol - i love java.util.concurrent.TimeUnit for that things.
Date d1 = ...
Date d2 = ...
long dif = d1.getTime() - d2.getTime();
long days = TimeUnit.MILLISECONDS.toDays(dif);
So basically exactly what the answer from morja is, but using TimeUnit for calculating time things around. Having values like 24, 60 etc. directly in your code violates Java Code Conventions (which only allow -1, 0 and 1 directly in code) and is harder to read.
EDIT My previous answer was only valid within a year.
You can use the milliseconds difference like this:
Date date1 = // some date
Date date2 = // some other date
long difference = date2.getTime() - date1.getTime();
long differenceDays = difference / (1000 * 60 * 60 * 24);
Basically the same as timbooo answered, just a shorter way.
Check out this example by kodejava.org
Jodatime makes such calculations a lot simpler:
Date now = // some Date
Date then = // some Date
int days = Days.daysBetween(new DateTime(now), new DateTime(then)).getDays();
In fact, you should create instances of Calendar from both of the dates, getTimeInMillis() from both of them (that is, time in milliseconds since 1970), substract one from the other, divide by 1000/seconds-a-minute/minute-an-hour/hour-a-day. There is your answer ;)