calculate an average time with joda time - java

I have many time stamps showing at which time a user entered the room. I want to calculate an average time. The problem occurs when some action happens at night.
I tried to calculate it with milis, but it is wrong.
ArrayList<String> times = new ArrayList<String>();
times.add("00:20:01");
times.add("00:00:01");
times.add("23:40:01");
times.add("23:20:01");
times.add("23:20:01");
times.add("00:20:01");
times.add("23:40:01");
times.add("23:40:01");
times.add("00:00:01");
long commonMillis=0;
for (String date:times){
LocalTime time = new LocalTime(date);
long dayMilis = time.getMillisOfDay();
commonMillis = commonMillis + dayMilis;
}
LocalTime average = new LocalTime(commonMillis/times.size());
This code, for example, returns the value 14:08:54.333. Because the hours 00:00 and 23:00 -- calculated in millis -- are too far from each other.
Please help me to find right way to calculate the average time?

Three things:
You have to define an offset time:
If you want an average of times of different days without knowing the day, you have to define an offset time by yourself. This time is used to decide whether a time is belonging to the next day or not.
This offset time may be derived depending on the values you get.
Without an offset time, you implicitely use 0 o'clock.
Avoid overflows:
If your times list gets longer, you may run into an overflow if a long field is not sufficient to store the accumulated value. You can use a data structure which is overflow resistant like BigInteger or use the (culmulative) moving average approach.
Wrong result constructor:
The constructor LocalTime(long instant) implicitely uses your local DateTimeZone to calculate a local time from an Instant. This causes different times when using the same code between different time zones.
The method you want to use is LocalTime#fromMillisOfDay.
Here is an approach considering the above points:
long movingAverage = 0;
// 1. define offset
LocalTime offset = new LocalTime("12:00:00");
long offsetMillis = offset.getMillisOfDay();
for (String date : times) {
long sampleMillis = new LocalTime(date).getMillisOfDay();
// align to offset
if (sampleMillis < offsetMillis)
sampleMillis += DateTimeConstants.MILLIS_PER_DAY;
long diff = sampleMillis - offsetMillis;
// 2. use moving average
movingAverage = movingAverage + diff / times.size();
}
// 3. avoid LocalTime(long) constructor
LocalTime result = offset.plusMillis((int) movingAverage);
System.out.println(result); // 23:48:54.329

A naive approach would be to gather the long millisecond values in all the dates, add them up and divide them by the number of dates, transforming them back into a LocalDate. You probably need a BigInteger to hold the sum, though.

Related

How can randomize a time between yesterday and today's local DateTime in java?

I need to randomize a time in java.
If now the time is 24/2/2021 13:56:13, then I need to randomize a time between 23/2/2021 13:56:13 and 24/2/2021 13:56:13. I am not familiar to random function in Java so that I maybe need some help. Thank you for your attention.
Take LocalDateTime.now() then go back in time with a random amount of seconds between 0 and 86400
int randomSeconds = new Random().nextInt(3600 * 24);
LocalDateTime anyTime = LocalDateTime.now().minusSeconds(randomSeconds);
System.out.println(anyTime);
General solution
Define the beginning dates and end of the period
Compute the difference in seconds and get a random int in that range
Compute the random date with one of these 2 ways:
Go from the beginning and add the random amount of seconds
Go from the end and remove the random amount of seconds
LocalDateTime periodStart = LocalDateTime.now().minusDays(1);
LocalDateTime periodEnd = LocalDateTime.now();
int randomSeconds = new Random().nextInt((int) periodStart.until(periodEnd, ChronoUnit.SECONDS));
//LocalDateTime anyTime = periodStart.plusSeconds(randomSeconds);
LocalDateTime anyTime = periodEnd.minusSeconds(randomSeconds);

Find the Median Date between two dates using Java 8

I'm finding it difficult that what it sounds.
So, I have a max date and a min date and I need to find the median date between these two dates. I use Java 8 to find my max and min dates,
LocalDate gerbutsmin = YearMonth.now().plusMonths(2).atDay(1);
LocalDate gerbutsmax = YearMonth.now().plusMonths(15).atDay(1);
How would I go ahead after this? Maybe I need to switch back to Calander?
Try using DAYS.between():
LocalDate gerbutsmin = YearMonth.now().plusMonths(2).atDay(1);
LocalDate gerbutsmax = YearMonth.now().plusMonths(15).atDay(1);
long numDays = ChronoUnit.DAYS.between(gerbutsmin, gerbutsmax);
LocalDate median = gerbutsmin.plusDays(numDays / 2L); // plusDays takes a long
System.out.println(median);
2019-03-17
(output as of today, which is 2019-07-26)
Demo
There is a boundary condition should the difference between your min and max dates be an odd number. In this case, there is no formal median day, but rather the median would fall in between two days.
Note:
If you're wondering what happens exactly in the edge case, if the low date were today (2018-07-26) and the high date three days away (2018-07-29), then the median would be reported as 2018-07-27.
LocalDate gerbutsmin = YearMonth.now().plusMonths(2).atDay(1);
LocalDate gerbutsmax = YearMonth.now().plusMonths(15).atDay(1);
LocalDate median = gerbutsmin.plusDays(ChronoUnit.DAYS.between(gerbutsmin, gerbutsmax) / 2);
"Middle of two dates" is not unambiguously defined - you must decide how to handle dates an odd number of days apart (e.g. what is the middle date between 1st and 4th of a month, or between 1st and 2nd), and what to do with the time portion of the date object.
The concrete problem with your approach is that dates are not numbers, so you cannot add them and divide them by two. To do that, use the getTime() method to obtain the number of seconds since the epoch, and operate on that:
var middate = new Date((startdate.getTime() + enddate.getTime()) / 2.0);
This will give you the middle between two dates, treating them as points in time.
Click here
LocalDateTime startime = LocalDateTime.now();
LocalDateTime endtime = startime.plusDays(4);
long diff = endtime.until(startime, ChronoUnit.MINUTES);
LocalDateTime middleTime = startime.plusMinutes(diff / 2);

Compare database time with current time

I need to compare current time with the time that i am getting from database. I am getting time from database in Java class in java.sql.Time format (16:12:00).
I just need to display a error message if current time matches with the time present in DB.
When dealing with dates and times, you can use one of the many libraries like Joda Time, or you can simply consider a time as a given millisecond since 1/1/1970 (unix epoch), expressed as a normal long.
To convert a java.util.Date, or a java.sql.Time,Date etc.. that extends from java.util.Date, to a simple long, you can call getTime() : http://docs.oracle.com/javase/6/docs/api/java/util/Date.html#getTime()
Current time, expressed as milliseconds from unix epoch so comparable with results of getTime(), can be obtained with System.currentTimeMillis();
Once you have that, comparing it is very easy :
Time dbTime = // the time you obtained from the db
long dbLong = dbTime.getTime();
long now = System.currentTimeMillis();
if (dbLong < now) // data in the db is in the past
if (dbLong > now) // data in the db is in the future
if (dbLong == now) // data in the db is exactly now
Take care of the dbLong == now, cause it's precise to the millisecond, so it will rarely happen in practice, unless you use a range or reduce the precision, say, to the second or minute :
long dbLongSeconds = dbLong / 1000;
long dbLongMinutes = dbLong / (60*1000);
long nowSeconds = now / 1000;
long nowMinutes = now / (60*1000);
if (dbLongSeconds == nowSeconds) // data in the db is in this second
if (dbLongMinutes == nowMinutes) // data in the db is in this minute
If you need more sophisticated comparisons, like day or month, you should use either a library like Joda Time, or built in classes like Calendar, cause the math is way more complex given how western calendar divides the year.
To compare your current time with the time from the database you could simply construct a sql.Time from System.currentTimeMillis() and compare the two toString()s like so:
java.sql.Time serverTime = getServerTime();
java.sql.Time currentTime = new java.sql.Time(System.currentTimeMillis());
if(serverTime.toString().compareTo(currentTime.toString()) == 0)
{
//yay
}
else //nay
You could also compare the two sql.Time's directly using it's compareTo method, but this is trickier.
This is because even though sql.Time's setDate/Year/Month is deprecated and will throw an exception if you use them( which makes sense because they're not a date, only a time) the sql.Time's compareTo uses its superclass implementation, which means it compares not only the time but also the date, which sucks 'cus your database sql.Time object will probably always have the date 1970.01.01 whereas any sql.Time you construct off of System.currentTimeMillis() will have the current date. You can get around this by using a Calendar object as shown.
Calendar tmp = new GregorianCalendar();
tmp.setTimeInMillis(System.currentTimeMillis());
tmp.set(Calendar.YEAR, 1970);
tmp.set(Calendar.MONTH, 0); // 0 == January
tmp.set(Calendar.DATE, 1);
tmp.set(Calendar.MILLISECOND, 0);
java.sql.Time currentTime = new java.sql.Time(c.toInstant().toEpochMilli());
java.sql.Time serverTime = getServerTime();
if(currentTime.compareTo(serverTime) == 0)
{
//yay
}
else //nay
Or you could compare the long times directly as in Simone Gianni's answer, which would probably be the more efficient solution.

Avoid Rounding Error on Double -> Date conversion

In my program, I receive strings that define a time stamp in milliseconds. Now I need to convert this to a proper date. The solution I found looks something like this:
String aTime = "1365504203.0269";
double t = Double.parseDouble(aTime);
Date date = new Date((long)t*1000);
SimpleDateFormat dateFormatDDMMYYYY = new SimpleDateFormat("dd.MM.yyyy");
SimpleDateFormat dateFormatHHMMssSS = new SimpleDateFormat("HH:mm:ss:SS");
String day = new String(dateFormatHHMMssSS.format(date));
String hour = new String(dateFormatDDMMYYYY.format(date));
System.out.println("The Date: "+day);
System.out.println("The Time: "+hour);
Unfortunately, this removes the accuracy of milliseconds from the time stamp. (I'm not sure if the time is even that accurate as I can hardly think about it anymore.)
Has it gone lost due to double->long conversion, or has it never been there at all? Any way to workaround this problem?
The problem is in this statement:
Date date = new Date((long)t*1000);
It casts the double to a long first, thereby truncating the decimal places, and then multiplies by 1000, which just adds three zeros. Try this:
Date date = new Date((long)(t*1000.0));
It uses double as the data type for multiplication, which moves the decimal places into the integer part, and then makes the decimal place truncating long conversion.
Using 1000.0 instead of 1000 as the constant forces the constant to be of double type as well, adding an extra level of certainty that the multiplication will happen with doubles.

Method to obtain a rand value bigger than value of system.currentTimeInMillis

How to generate a random value bigger than value of System.currentTimeInMillis(). I use Random object.how can I obtain a value that have min range as System.currentTimeInMillis()?
Doesn't
long value = System.currentTimeMillis() + (long)random.nextInt(range);
work?
If you want to enforce a value that is strictly larger than System.currentTimeMillis() add an additional 1 to it. Set the range accordingly to prevent overflow (see assylias's comment).
Edited according to comments.
This is an approach if you want to be able to get numbers distributed across the entire range System.currentTimeMillis()..Long.MAX_VALUE:
long millis = System.currentTimeMillis();
long l = Math.min(Long.MAX_VALUE - millis, Math.abs(random.nextLong())) + millis;
Long.MAX_VALUE will be much more common than other results here though, in case that matters.
For a uniform distribution of times between currentTimeMillis and Long.MAX_VALUE, without overflow, you can use:
long time = System.currentTimeMillis();
long randomFutureTime = Math.abs(random.nextLong()) % (Long.MAX_VALUE - time) + time;
This may is useful to have real time of object creation, and two objects cannot have the same timestamp, so you can order them.
I use as UUID for my objects :
Const.DECAL_BIT = 20;
Const.DECAL_BIT_MASQUE = (Long.size() -1) - next 20;
private final Long timeCreate = (System.currentTimeMillis() << Const.DECAL_BIT)
+ (System.nanoTime() & Const.DECAL_BIT_MASQUE);
So you can have valid dates for 100 years : you multiply by 1M the internal datetime and had one second elapse time in nanosecond precision.
To read the date : Date d = Date((Long) timeCreate>> Const.DECAL_BIT);

Categories