How to efficiently calculate duration between two timestamps including date in java? - java

I have two time stamps which is in UTC with format 2021-05-19T03:03:55.095Z and 2021-05-19T05:10:48.177Z . How can i calculate duration between these two in minutes even when there is date change happened. For example date of second time stamp changed to 2021-05-20T05:10:48.177Z.
Below are the few parser which i have done can anyone help me with this.
private String timeParser (String timestamp){
String output = "";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
try {
Date date1 = df.parse(timestamp);
DateFormat outputFormatter1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
output = outputFormatter1.format(date1); //
} catch (ParseException e) {
logger.error(e);
}
return output;
}
public void calculateDuration(JsonNode aggEvent, JsonNode newEvent){
Timestamp createdTimestamp = Timestamp.valueOf(timeParser("2021-05-19T03:03:55.095Z"));
Timestamp newTimestamp = Timestamp.valueOf(timeParser("2021-05-20T05:10:48.177Z"));
float timeDifference = newTimestamp.getTime() - createdTimestamp.getTime();
float minutesDifference = (timeDifference / (1000 * 60)) % 60;
System.out.println(Math.round(minutesDifference * 100.0) / 100.0);
}
Can someone correct here if im doing anything wrong?

Your issues
There are the following issues with your code:
You are parsing each string into a Date, formatting it back to a different string and finally parsing it again, this time into a Timestamp. It’s a detour that gives you extra complication and no gain.
You are using the old SimpleDateFormat, Date and Timestamp classes. They are very poorly designed and have long been replaced by java.time, the modern and far superior date and time API. You should prefer to use the latter.
You must never ever hardcode Z as a literal in your format pattern string. Z is an offset of zero from UTC and must be parsed as an offset, or you will get incorrect results since otherwise SimpleDateFormat will assume the default time zone of your JVM. You may think that in this case, where we are calculating the difference between the two times, the errors of wrong time zone will even out. Not so. For example summer time (DST) transitions in your time zone will influence the calculation that you thought you were doing in UTC, leaving you and those maintaining your code baffled.
Lower case hh in the format pattern string is for hour within morning (AM) or afternoon (PM). You need upper case HH for hour of day.
You should not do the time math yourself in your code. Leave it to the Duration class of java.time for code that is clearer to read and less error-prone.
How to do instead
Edit: My shot at a simple and good way to obtain the time difference with days, hours, minutes and seconds:
Instant createdInstant = Instant.parse("2021-05-19T03:03:55.095Z");
Instant newInstant = Instant.parse("2021-05-20T05:10:48.177Z");
Duration timeDifference = Duration.between(createdInstant, newInstant);
System.out.println(timeDifference);
The output this far looks a bit funny if you’re not used to the ISO 8601 format for durations:
PT26H6M53.082S
It’s straightforward to read when you know how, though. Think of the output as a period of time of 26 hours 6 minutes 53.082 seconds. Only the 24 hours were not converted to a full day, which we had wanted, and which a Duration can do too:
System.out.println("Days: " + timeDifference.toDays());
System.out.println("Hours: " + timeDifference.toHoursPart());
Days: 1
Hours: 2
There are similar toMinutesPart, toSecondsPart and toMillisPart methods.
Link: Wikipedia article: ISO 8601

Single line of code need to be updated.
float minutesDifference = (timeDifference / (1000 * 60));
Here doing %60 means you are covering all difference in hour, minute format
like (timeDifference / (1000 * 60)) / 60 = noOfHours
and (timeDifference / (1000 * 60)) % 60 = reminder of above (Minutes)
See output of this code you will understand better.

Related

Add 30 minutes to the current time

I have written the code below but if the current date-time is 2022-07-03 09:48:05.448 and I add 30 minutes, my response returns 2022-07-03 09:79:05.448.
But minutes can never be 79, it is supposed to move to the hours instead...
public static String getExpiryDate(int additionalMinutesToCurrentMinute) {
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
int second = now.getSecond();
int millis = now.get(ChronoField.MILLI_OF_SECOND); // Note: no direct getter available.
String expiryDateAndTime = String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
return expiryDateAndTime;
}
Explanation
The reason your code does not work as expected is because you are not involving javas Date/Time API at all in your "math".
Your adding the minutes with plain int-arithmetic
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
and then you use plain string formatting
String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
Nothing in this chain is "clever" and knows about date/time specifics.
Solution
You have to involve the Date/Time API for your math, then it will be clever and correctly adjust the hours as well. Fortunately, there is a method in LocalDateTime already that does what you want:
LocalDateTime expirationTime = LocalDateTime.now().plusMinutes(30);
and that is pretty much all you need.
For the formatting part, either roll with the default representation:
return expirationTime.toString();
or use a DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd HH:mm:ss.AAA");
and then
return expirationTime.format(formatter);
Notes
Instant
You are actually using the incorrect type for an expiration time. Using LocalDateTime will result in your application failing under certain situations. For example if your computer moves across countries, or your government decides to change its timezone. Or when DST hits (summer vs winter time) or leap seconds are added and more...
The correct type would be Instant, which represents a single moment on the timeline, without interpretation of clock-time or calendar-dates.
The API is the same, so you can just use it the same way.
That said, your method should also return Instant and not a String. Keep the clever date/time type as long as possible, dont go to something as low level and raw as a string.
public static Instant getExpiryDate(int additionalMinutes) {
return Instant.now()
.plus(additionalMinutes, ChronoUnit.MINUTES);
}
Design
Design-wise it would be better if the method would not even take int additionalMinutes but also the unit. Otherwise the call-site is hard to read for users:
getExpiryDate(30) // 30 what? minutes? seconds? days?
with the unit, it would be easier to read and harder to misunderstand
getExpiryDate(30, ChronoUnit.MINUTES)
At which point one could argue that the method is kinda obsolete now.
Instead of editing the amount of minutes manually, try using the plusMinutes method on your LocalDateTime like so:
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusMinutes(30);
This way, the class should increase the hour for you once it passes 60 minutes.

What is a best way to find the number of days and months between two java.time.Instant objects?

NOTE: search Google before marking this question as duplicate. I did search and browse this question and all answers that I found were either for LocalDate, Joda or legacy Java Date.
It took me quite some time to investigate this so I've decided to share this as an answer.
I'd like a way to calculate the (approximate) number of months and days between two Java Instants (objects of java.time.Instant)?
First, what you are asking is not well-defined. For example between the instants 2020-03-01T06:00:00Z and 2020-03-31T05:00:00Z could be:
29 days 23 hours in Australia/Melbourne time zone;
30 days in Europe/Paris time zone;
1 month 1 day in America/Los_Angeles time zone.
Accurate result in a given time zone
ZoneId zone = ZoneId.of("America/Los_Angeles");
Instant start = Instant.parse("2020-03-01T06:00:00Z");
Instant end = Instant.parse("2020-03-31T05:00:00Z");
ZonedDateTime startZdt = start.atZone(zone);
LocalDate startDate = startZdt.toLocalDate();
ZonedDateTime endZdt = end.atZone(zone);
LocalDate endDate = endZdt.toLocalDate();
Period p = Period.between(startDate, endDate);
if (startZdt.plus(p).isAfter(endZdt)) {
// The time of day on the end date is earlier, so don’t count a full date
endDate = endDate.minusDays(1);
p = Period.between(startDate, endDate);
}
System.out.println(p);
Output:
P1M1D
Read as a period of 1 month 1 day.
Approximate result independent of time zone
Prefer to leave as much of the calculation to java.time as possible. This includes the estimate of the length of a month.
Duration diff = Duration.between(start, end);
Duration durationOfAMonth = ChronoUnit.MONTHS.getDuration();
long months = diff.dividedBy(durationOfAMonth);
diff = diff.minus(durationOfAMonth.multipliedBy(months));
long days = diff.toDays();
System.out.println("" + months + " months " + days + " days");
0 months 29 days
I've opted out to approximate solution (it assumes all months have 30.44 days). I've opted out to use something like this:
Duration duration = Duration.between(instant1, instant2).abs(); /* if want negative values remove .abs() */
long hours = duration.toHours();
double daysAndMonthsInDays = hours / 24.0;
int months = daysAndMonthsInDays / 30.44; //average number of days per month
int days = daysAndMonthsInDays - months * 30.44;
Please post another answer if there is a better solution using Duration class or something else. I've decided not to convert Instant to LocalDate and to perform the conversion on that level. That would not use an approximation of 30.44 days in a month, but rather the actual number.

How to get duration by subtracting Arrival time by departing time in Java using Gregorian calendar?

Lets say I have to subtract Arrival time by departing time of specific days. How would I do this in Java using Gregorian calendar?
public Duration getDuration(){
SimpleDateFormat date = new SimpleDateFormat("YYYYMMDD");
SimpleDateFormat time = new SimpleDateFormat("HHMM");
String ArivalTime = "1720"
String DepartingTime = "1100"
String ArivalDate = "20200220"
String DepartingDate = "20200211"
Calendar departureDateCalc = new GregorianCalendar();
//return duration;
}
the code is barely anything. But I need to subtract an Arrival date by Departing date
java.time
Don’t use GregorianCalendar for this. Use java.time, the modern Java date and time API.
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmm");
String arrivalTime = "1720";
String departingTime = "1100";
String arrivalDate = "20200220";
String departingDate = "20200211";
String arrivalTimeZone = "Africa/Niamey";
String departingTimeZone = "America/Mendoza";
ZonedDateTime departure = LocalDate.parse(departingDate, DateTimeFormatter.BASIC_ISO_DATE)
.atTime(LocalTime.parse(departingTime, timeFormatter))
.atZone(ZoneId.of(departingTimeZone));
ZonedDateTime arrival = LocalDate.parse(arrivalDate, DateTimeFormatter.BASIC_ISO_DATE)
.atTime(LocalTime.parse(arrivalTime, timeFormatter))
.atZone(ZoneId.of(arrivalTimeZone));
Duration difference = Duration.between(departure, arrival);
System.out.println(difference);
This outputs:
PT218H20M
So a period of time of 218 hours 20 minutes. If you want it more readable:
String diffString = String.format(
"%d days %d hours %d minutes", difference.toDays(),
difference.toHoursPart(), difference.toMinutesPart());
System.out.println(diffString);
9 days 2 hours 20 minutes
The conversion to days assumes that a day is always 24 hours, which may not be the case in either of the departure or the arrival time zone, for example when they go from standard to summer time (DST) or vice versa.
Don’t use GregorianCalendar
The GregorianCalendar class is poorly designed and long outdated. Don’t use it. For anything. If you had wanted to use it for finding a duration, the way would have been to add one day at a time to the departure time until you reach the arrival time. If you’re past it, subtract one day again and start counting hours, again by adding one at a time. Same with minutes. It’s way more complicated and also more error-prone than using the Duration class directly.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Display Java 8 Time api in this format "HH:mma" (05:53PM) and add set Time with only minutes

I want to display / print out time like
02:15PM
and be able to set the time just based on minutes(possibly larger than 59).
This doesn't seem to work:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mma");
String text = time.format(formatter);
LocalTime parsedTime = LocalTime.parse(text, formatter);
it shows time like 16:15 so its the wrong 12/24 format(and am/pm missing of course)
so far I set the time like this:
void setTime(int minutes) {
time = LocalTime.of(minutes / 60, minutes % 60);
}
is there a way to do it in a more elegant way (read: without dividing)?
I do not want to use Joda Time.
As discussed, use hh instead of HH for an AM/PM clock. The list of patterns is detailed in the javadoc. Example:
LocalTime time = LocalTime.of(18, 0);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("hh:mma");
System.out.println(fmt.format(time)); //06:00PM
As for creating a time from a number of minutes, you can use:
LocalTime.MIDNIGHT.plusMinutes(minutes);
Replace the "HH" with "hh": DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mma");
Slighty more elegant way of setting time by minutes: LocalTime.ofSecondOfDay(minutes * 60)

Java, get days between two dates

In java, I want to get the number of days between two dates, excluding those two dates.
For example:
If first date = 11 November 2011 and the second date = 13 November 2011
then it should be 1.
This is the code I am using but doesn't work (secondDate and firstDate are Calendar objects):
long diff=secondDate.getTimeInMillis()-firstDate.getTimeInMillis();
float day_count=(float)diff / (24 * 60 * 60 * 1000);
daysCount.setText((int)day_count+"");
I even tried rounding the results but that didn't help.
How do I get the number of days between dates in java excluding the days themselves?
I've just tested on SDK 8 (Android 2.2) the following code snippet:
Calendar date1 = Calendar.getInstance();
Calendar date2 = Calendar.getInstance();
date1.clear();
date1.set(
datePicker1.getYear(),
datePicker1.getMonth(),
datePicker1.getDayOfMonth());
date2.clear();
date2.set(
datePicker2.getYear(),
datePicker2.getMonth(),
datePicker2.getDayOfMonth());
long diff = date2.getTimeInMillis() - date1.getTimeInMillis();
float dayCount = (float) diff / (24 * 60 * 60 * 1000);
textView.setText(Long.toString(diff) + " " + (int) dayCount);
it works perfectly and in both cases (Nov 10,2011 - Nov 8,2011) and (Nov 13,2011 - Nov 11,2011) gives dayCount = 2.0
Get Days between java.util.Dates, ignoring daylight savings time
Quick and dirty hack:
public int get_days_between_dates(Date date1, Date date2)
{
//if date2 is more in the future than date1 then the result will be negative
//if date1 is more in the future than date2 then the result will be positive.
return (int)((date2.getTime() - date1.getTime()) / (1000*60*60*24l));
}
This function will work 99.99% of the time, except when it surprises you later on in the edge cases during leap-seconds, daylight savings, timezone changes leap years and the like. If you are OK with the calculation being off by 1 (or 2) hours once in a while, this will suffice.
Get Days between Dates taking into account leapseconds, daylight savings, timezones, etc
If you are asking this question you need to slap yourself. What does it mean for two dates to be at least 1 day apart? It's very confusing. What if one Date is midnight in one timezone, and the other date is 1AM in another timezone? Depending on how you interpret it, the answer is both 1 and 0.
You think you can just force the dates you pass into the above function as Universal time format; that will fix some of your problems. But then you just relocate the problem into how you convert your local time to a universal time. The logical conversion from your timezone to universal time may not be what is intuitive. In some cases you will get a day difference when the dates passed in are obviously two days apart.
And you think you can deal with that? There are some simplistic calendar systems in the world which are constantly changing depending on the harvest season and installed political rulers. If you want to convert their time to UTC, java.util.Date is going to fail you at the worst moment.
If you need to calculate the days between dates and it is critical that everything come out right, you need to get an external library called Joda Time: (They have taken care of all the details for you, so you can stay blissfully unaware of them): http://joda-time.sourceforge.net/index.html
java.time
The java.time API, released with Java-8 in March 2014, supplanted the error-prone legacy date-time API. Since then, using this modern date-time API has been strongly recommended.
Solution using modern date-time API
Using Calendar#toInstant, convert your java.util.Calendar instances into java.time.Instant and then into java.time.ZonedDateTime instances and then use ChronoUnit.DAYS.between to get the number of days between them.
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
// Sample start and end dates as java.util.Date
Calendar startCal = Calendar.getInstance();
startCal.set(2011, 10, 11); // 11 November 2011
Calendar endCal = Calendar.getInstance();
endCal.set(2011, 10, 13); // 13 November 2011
// Convert the java.util.Calendar into java.time.ZonedDateTime
// Replace ZoneId.systemDefault() with the applicable ZoneId
ZonedDateTime startDateTime = startCal.toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime endDateTime = endCal.toInstant().atZone(ZoneId.systemDefault());
// The end date is excluded by default. Subtract 1 to exclude the start date
long days = ChronoUnit.DAYS.between(startDateTime, endDateTime) - 1;
System.out.println(days);
}
}
Output:
1
Learn more about the modern Date-Time API from Trail: Date Time.
Don't use floats for integer calculations.
Are you sure your dates are days? The precision of the Date type is milliseconds. So the first thing you need to do is round the date to something which doesn't have hours. Example: It's just one hour from 23:30 2011-11-01 to 00:30 2011-11-02 but the two dates are on different days.
If you are only going to be dealing with dates between the years 1900 and 2100, there is a simple calculation which will give you the number of days since 1900:
public static int daysSince1900(Date date) {
Calendar c = new GregorianCalendar();
c.setTime(date);
int year = c.get(Calendar.YEAR);
if (year < 1900 || year > 2099) {
throw new IllegalArgumentException("daysSince1900 - Date must be between 1900 and 2099");
}
year -= 1900;
int month = c.get(Calendar.MONTH) + 1;
int days = c.get(Calendar.DAY_OF_MONTH);
if (month < 3) {
month += 12;
year--;
}
int yearDays = (int) (year * 365.25);
int monthDays = (int) ((month + 1) * 30.61);
return (yearDays + monthDays + days - 63);
}
Thus, to get the difference in days between two dates, you calculate their days since 1900 and calc the difference. Our daysBetween method looks like this:
public static Integer getDaysBetween(Date date1, Date date2) {
if (date1 == null || date2 == null) {
return null;
}
int days1 = daysSince1900(date1);
int days2 = daysSince1900(date2);
if (days1 < days2) {
return days2 - days1;
} else {
return days1 - days2;
}
}
In your case you would need to subtract an extra day (if the days are not equal).
And don't ask me where this calculation came from because we've used it since the early '90s.
I have two suggestions:
Make sure your float day_count is calculated correctly
float day_count = ((float)diff) / (24f * 60f * 60f * 1000f);
If it's rounding error, try using floor method
daysCount.setText("" + (int)Math.floor(day_count));

Categories