Reputation: 358
I am looking for a way to get the difference between two dates, as the title says, in Java. For example, if d1 = 2017-01-23 05:25:00
and d2 = 2018-03-20 07:29:50
, some method would print
Years: 1
Months: 1
Days: 25
Hours: 2
Minutes: 4
Seconds: 50
I have tried to do this using LocalDateTime objects, but I cannot find a way to get the months between them. I don't see a need to include that code, but I can if it is needed. I don't care what type the dates are, but I would like to not use Joda time or other external libraries. Thank you in advance for your help.
Upvotes: 2
Views: 935
Reputation: 338584
We must parse your strings. The java.time classes use standard ISO 8601 formats by default when parsing/generating strings. Your inputs are close to complying; we just need to replace the SPACE in the middle with a T
.
String inputStart = "2017-01-23 05:25:00".replace ( " " , "T" );
String inputStop = "2018-03-20 07:29:50".replace ( " " , "T" );
LocalDateTime
Your inputs lack any indication of offset-from-UTC or time zone. So they do not represent actual moments on the timeline, only vague idea of possible moments on the timeline. So we will parse this as LocalDateTime
objects. The math we will perform to determine spans of time will be based on generic 24-hour days, and will ignore anomalies such as Daylight Saving Time (DST).
LocalDateTime start = LocalDateTime.parse ( inputStart );
LocalDateTime stop = LocalDateTime.parse ( inputStop );
Period
& Duration
The java.time classes define a span of time unattached to the timeline as two pieces. The Period
class represents years-months-days, while the Duration
class represents hours-minutes-seconds.
Breaking a span of time into these two pieces avoids certain issues, issues which we must address to solve your request. How exactly do you want to handle the possibly partial day on the first day, and the possibly partial day at the end? Is noon on one day to noon on the next day a single day or 24 hours? If we sum the hours of the first day with the hours of the last day, should we squeeze out a whole day if that number is more than 24 hours?
Here I take the approach that we count the number of whole days, ignoring the first day and the last day. Then I sum the number of hours in the first day with the number of hours in the last day. As we see with your example data, this may result in more than 24 hours in our Duration
. This approach may or may not meet your unstated intentions/definitions.
Period p = Period.between ( start.toLocalDate () , stop.toLocalDate () ).minusDays ( 1 ); // Subtract one, as we account for hours of first day.
// Get the Duration of first day's hours-minutes-seconds.
LocalDateTime startNextDay = start.toLocalDate ().plusDays ( 1 ).atStartOfDay ();
Duration dStart = Duration.between ( start , startNextDay );
// Get the Duration of first day's hours-minutes-seconds.
LocalDateTime stopStartOfDay = stop.toLocalDate ().atStartOfDay ();
Duration dStop = Duration.between ( stopStartOfDay , stop );
// Combine the pair of partial days into a single Duration.
Duration d = dStart.plus ( dStop );
Dump to console. The toString
methods on the Period
& Duration
class generates strings in the standard durations format of ISO 8601: PnYnMnDTnHnMnS
where P
marks the beginning and T
separates the years-month-days from the hours-minutes-seconds.
System.out.println ( "start/stop: " + start + "/" + stop );
System.out.println ( "p: " + p );
System.out.println ( "dStart: " + dStart + " and dStop: " + dStop );
System.out.println ( "d: " + d );
start/stop: 2017-01-23T05:25/2018-03-20T07:29:50
p: P1Y1M24D
dStart: PT18H35M and dStop: PT7H29M50S
d: PT26H4M50S
To get your desired textual output, you need to ask for each part of the Period
and each part of the Duration
.
int years = p.getYears ();
int months = p.getMonths ();
int days = p.getDays ();
Java 8 inexplicably lacks methods to conveniently get each hours or minutes or seconds part from a Duration
. Java 9 adds such methods. See this Question for more discussion.
long durationDays = d.toDaysPart();
long hours = d.toHoursPart();
long minutes = d.toMinutesPart();
long seconds = d.toSecondsPart();
int nanos = d.toNanosPart();
See this example code working live in IdeOne.com.
ZonedDateTime
The discussion above was for generic 24-hour days, ignoring the issue of offset-from-UTC/time zone.
If instead you meant to track actual moments on the timeline, you must apply the time zone intended for those date-time strings.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = start.atZone( z );
…
The rest of the code would be similar, but pass the ZoneId
to the atStartOfDay
calls to generate a ZonedDateTime
object rather than a LocalDateTime
object.
Keep in mind that you may well get different results when using ZonedDateTime
objects rather than LocalDateTime
objects.
See the example code live in IdeOne.com.
String inputStart = "2017-01-23 05:25:00".replace ( " " , "T" );
String inputStop = "2018-03-20 07:29:50".replace ( " " , "T" );
LocalDateTime ldtStart = LocalDateTime.parse ( inputStart ); // LocalDateTime used only briefly here, to parse inputs. Then we switch to `ZonedDateTime`.
LocalDateTime ldtStop = LocalDateTime.parse ( inputStop );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = ldtStart.atZone( z ); // Invalid values may be adjusted, such as during the hour of a "Spring-forward" Daylight Saving Time (DST) switch-over.
ZonedDateTime zdtStop = ldtStop.atZone( z );
Period p = Period.between ( zdtStart.toLocalDate () , zdtStop.toLocalDate () ).minusDays ( 1 ); // Subtract one, as we account for hours of first day.
// Get the Duration of first day's hours-minutes-seconds.
ZonedDateTime zdtStartNextDay = zdtStart.toLocalDate ().plusDays ( 1 ).atStartOfDay ( z );
Duration dStart = Duration.between ( zdtStart , zdtStartNextDay );
// Get the Duration of first day's hours-minutes-seconds.
ZonedDateTime zdtStopStartOfDay = zdtStop.toLocalDate ().atStartOfDay ( z );
Duration dStop = Duration.between ( zdtStopStartOfDay , zdtStop );
// Combine the pair of partial days into a single Duration.
Duration d = dStart.plus ( dStop );
System.out.println ( "zdtStart: " + zdtStart );
System.out.println ( "zdtStop: " + zdtStop );
System.out.println ( "p: " + p );
System.out.println ( "dStart: " + dStart + " and dStop: " + dStop );
System.out.println ( "d: " + d );
int years = p.getYears ();
int months = p.getMonths ();
int days = p.getDays ();
System.out.println( "Years: " + years + "\nMonths: " + months + "\nDays: " + days );
// Enable if in Java 9 or later (but not in Java 8)
// long durationDays = d.toDaysPart();
// long hours = d.toHoursPart();
// long minutes = d.toMinutesPart();
// long seconds = d.toSecondsPart();
// int nanos = d.toNanosPart();
Tip: The ThreeTen-Extra library offers a class Interval
to track a pair of Instant
objects. An Instant
is moment on the timeline in UTC. You may extract an Instant
from a ZonedDateTime
by calling toInstant
.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Upvotes: 3
Reputation: 1
package com.mkyong.date;
import java.text.SimpleDateFormat; import java.util.Date;
public class DateDifferentExample {
public static void main(String[] args) {
String dateStart = "01/14/2012 09:29:58";
String dateStop = "01/15/2012 10:31:48";
//HH converts hour in 24 hours format (0-23), day calculation
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
java.util.Date d1 = null;
java.util.Date d2 = null;
try {
d1 = format.parse(dateStart);
d2 = format.parse(dateStop);
//in milliseconds
long diff = d2.getTime() - d1.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);
System.out.print(diffDays + " days, ");
System.out.print(diffHours + " hours, ");
System.out.print(diffMinutes + " minutes, ");
System.out.print(diffSeconds + " seconds.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Result
1 days, 1 hours, 1 minutes, 50 seconds.
Upvotes: -1
Reputation: 158
I would just take the two dates and times that you want and put them into a SimpleDateFormat. Then you can just do date.getTime() and return the distance in milliseconds, which can then be converted back into a number of days, years, or months. Here's an example I wrote:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.text.ParseException;
public class DateTest{
public static void main(String[] args) throws ParseException{
System.out.println("Current time in milliseconds: " + System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
String dateAndTime1 = "2:30:00 1/23/2017";
String dateAndTime2 = "1:45:00 5/23/2015";
Date date1 = format.parse(dateAndTime1);
Date date2 = format.parse(dateAndTime2);
long difference = date1.getTime() - date2.getTime();
System.out.println("The difference in milliseconds is " + difference);
}
}
Upvotes: -1