Avoid Rounding Error on Double -> Date conversion - java

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.

Related

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

Converting DateTime from ISO_INSTANT to seconds from midnight

I'm new to Java and DateTime formats in general, and I'm supposed to convert GPS data from CSV files. My question is how this is done for the format I'm working with. The data points I'm getting from the GPS data is in the format as follows:
time,lat,lon,elevation
I have split up the different data points and put them in a string array, and I'm supposed to convert the data points to either integer or double arrays, as follows:
public void convert() {
int n = timesstr.length; //Amount of data points
times = new int[n]; //Array for time in seconds (int)
latitudes = new double[n]; //Array for latitudes (double)
longitudes = new double[n]; //Array for longitudes (double)
elevations = new double[n]; //Array for elevations (double)
}
I'm planning to do a for-loop, converting the elements in the arrays one after one. Now the problem is how I'm supposed to convert the time from the data point to seconds from midnight, as the data point for time is formatted as ISO_INSTANT, e.g.
2017-08-13T08:52:26.000Z
How should I go about parsing and converting the data points from ISO_INSTANT to seconds from midnight? According to the information I got, the data point
2017-08-13T08:52:26.000Z
Should give the integer 31946 after conversion, as in 31956 seconds past midnight.
You can do it in the following way:
LocalDateTime.parse("2017-08-13T08:52:26.000Z", DateTimeFormatter.ISO_DATE_TIME).get(ChronoField.SECOND_OF_DAY);
Or as pointed out by #Andreas you can do:
ZonedDateTime.parse("2017-08-13T08:52:26.000Z").get(ChronoField.SECOND_OF_DAY);
is shorter, and more lenient. It even supports Daylight Savings Time, if a time zone other that Z is given. - Andreas

calculate an average time with joda time

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.

Java change double to Date

There is a problem when I tried to transform double to Date.
This is my code:
double itemDouble = 1370437809.00;
long itemLong = (long) (itemDouble * 1000);
Date itemDate = new Date(itemLong);
String itemDateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS").format(itemDate);
When the itemDouble is 1370437809.00, itemDateStr is 2013-06-05 21:10:09.00,
but when the itemDouble's decimal places is not zero, such as 1370437809.66, the itemDateStr is 2013-06-05 21:10:09.660. The formatted date string is not right.
How this happened?
According to your code, 1370437809.66 is a number of seconds since 1970. The decimal part represents 660 milliseconds. When you convert to a Date, you ask to display the milliseconds (the .SSS in the pattern). The result you get is correct.
you have 0.66*1000=660 that's correct

Time and Calendar

How do I add/subtract two time objects. I have two time objects (arrival and departure) in format of "yyyy/MMM/dd HH:mm:ss". I need to print the difference between departure and arrival time. I am generating time ad below:
public String getTime() {
Calendar currentDate = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MMM/dd HH:mm:ss");
return formatter.format(currentDate.getTime());
}
Can I get time in mills and than format it when I needed to print ?
Take a look at Joda Time library.
You can easily subtract and add DateTime and find out interval easily :
// interval from start to end
DateTime start = new DateTime(2004, 12, 25, 0, 0, 0, 0);
DateTime end = new DateTime(2005, 1, 1, 0, 0, 0, 0);
Interval interval = new Interval(start, end);
something like this.....
public long getTimeDiff() throws Exception {
String arrival = "2011/Nov/10 13:15:24";
String departure = "2011/Jan/10 13:15:24";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MMM/dd HH:mm:ss");
java.util.Date date1 = formatter.parse(arrival);
java.util.Date date2 = formatter.parse(departure);
return date2.getTime() - date1.getTime();
}
Convert them to date and then to long and subtract, that would give the time difference in milli seconds,
Date d1 = DateFormat.parse(time1);
Date d2 = DateFormat.parse(time2);
long diffInMilliSeconds = d1.getTime()-d2.getTime();
You can get time in milliseconds for both calendars using getTime method. When you can convert the result of subtraction to measure units that you need. If you're going to work with time/duration seriously when take a look at Joda library
Upd. You should call getTime twice. First object being returned is Date, when you call getTime on Date you get long value.
I would convert the two time/Date objects in milliseconds. Then i would subtract them (we are dealing with longs).
Then i would create a Date object from the resulting long value. After that you can construct a Calendar with Calendar.setDate(Date).
Regards!
Yes, start with your Dates and use getTime() to convert to milliseconds (or getTimeInMillis() for your Calendars). That give you long values you can subtract. That's the easy part.
Then you can convert these milliseconds into a readable format yourself. But it probably makes sense to use a packaged library to do it.
Some folks like the Joda library for these types of date calculations. I find Commons Lang is fantastic. It provides DateUtils which is useful if you find you want to perform calculations like rounding or truncating your dates to the nearest minute or hour etc. The part that will be most useful to you is the DurationFormatUtils class which gives you functions like formatDurationHMS to format into nice Hour:Minute:Second display and formatDurationWords to get text (fancy!) or other similar functions to easily format your milliseconds into a nicely human-readable format.

Categories