Reputation: 3092
This is how I call getTimeBetween
function:
getTimeBetween(ZonedDateTime.now().minusHours(4).minusMinutes(1).minusSeconds(40), ZonedDateTime.now());
And I expect this output:
4 hours, 1 minute, 40 seconds ago
This is my getTimeBetween
function:
private String getTimeBetween(ZonedDateTime zonedDateTime1, ZonedDateTime zonedDateTime2) {
Duration timeDifference = Duration.between(zonedDateTime1, zonedDateTime2);
if (timeDifference.getSeconds() == 0) return "now";
String timeDifferenceAsPrettyString = "";
Boolean putComma = false;
if (timeDifference.toDays() > 0) {
if (timeDifference.toDays() == 1) timeDifferenceAsPrettyString += timeDifference.toDays() + " day";
else timeDifferenceAsPrettyString += timeDifference.toDays() + " days";
putComma = true;
}
if (timeDifference.toHours() > 0) {
if (putComma) timeDifferenceAsPrettyString += ", ";
if (timeDifference.toHours() == 1) timeDifferenceAsPrettyString += timeDifference.toHours() + " hour";
else timeDifferenceAsPrettyString += timeDifference.toHours() % 24 + " hours";
putComma = true;
}
if (timeDifference.toMinutes() > 0) {
if (putComma) timeDifferenceAsPrettyString += ", ";
if (timeDifference.toMinutes() == 1) timeDifferenceAsPrettyString += timeDifference.toMinutes() + " minute";
else timeDifferenceAsPrettyString += timeDifference.toMinutes() % 60 + " minutes";
putComma = true;
}
if (timeDifference.getSeconds() > 0) {
if (putComma) timeDifferenceAsPrettyString += ", ";
if (timeDifference.getSeconds() == 1) timeDifferenceAsPrettyString += timeDifference.getSeconds() + " second";
else timeDifferenceAsPrettyString += timeDifference.getSeconds() % 60 + " seconds";
}
timeDifferenceAsPrettyString += " ago";
return timeDifferenceAsPrettyString;
}
This function works as expected but is it really necessary to do it like this? Perhaps there is a better way to achieve this?
I'm using Java 8.
Upvotes: 0
Views: 242
Reputation: 78935
You can use java.time.Duration
which is modelled on ISO-8601 standards and was introduced as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.
Demo:
public class Main {
public static void main(String[] args) {
Duration duration = Duration.between(
ZonedDateTime.now().minusHours(4).minusMinutes(1).minusSeconds(40),
ZonedDateTime.now()
);
System.out.println(formatDuration(duration));
// Example for '... later'
duration = Duration.between(
ZonedDateTime.now().plusHours(4).plusMinutes(1).plusSeconds(40),
ZonedDateTime.now()
);
System.out.println(formatDuration(duration));
}
static String formatDuration(Duration duration) {
//################# Java 9 onwards ###################
String formatted = List.of(fmtHr(Math.abs(duration.toHoursPart())),
fmtMin(Math.abs(duration.toMinutesPart())),
fmtSec(Math.abs(duration.toSecondsPart()))
).stream().collect(Collectors.joining(", "));
//#####################################################
return formatted += duration.toHoursPart() >= 0 ? " ago." : " later.";
}
static String fmtHr(long hr) {
return String.format("%d %s", hr, hr <= 1 ? "hour" : "hours");
}
static String fmtMin(long min) {
return String.format("%d %s", min, min <= 1 ? "minute" : "minutes");
}
static String fmtSec(long sec) {
return String.format("%d %s", sec, sec <= 1 ? "second" : "seconds");
}
}
Output:
4 hours, 1 minute, 40 seconds ago.
4 hours, 1 minute, 40 seconds later.
Note: List#of
and parts API for Duration
e.g. Duration#toHoursPart
are available since Java 9. If you are working with Java 8, you can use duration.toHours()
, duration.toMinutes() % 60
and duration.toSeconds() % 60
with the Duration
class and add elements to the ArrayList
individually.
Learn about the modern date-time API from Trail: Date Time.
Upvotes: 0
Reputation: 14887
How about this?
static String getTimeBetween(ZonedDateTime from, ZonedDateTime to) {
StringBuilder builder = new StringBuilder();
long epochA = from.toEpochSecond(), epochB = to.toEpochSecond();
long secs = Math.abs(epochB - epochA);
if (secs == 0) return "now";
Map<String, Integer> units = new LinkedHashMap<>();
units.put("day", 86400);
units.put("hour", 3600);
units.put("minute", 60);
units.put("second", 1);
boolean separator = false;
for (Map.Entry<String, Integer> unit : units.entrySet()) {
if (secs >= unit.getValue()) {
long count = secs / unit.getValue();
if (separator) builder.append(", ");
builder.append(count).append(' ').append(unit.getKey());
if (count != 1) builder.append('s');
secs %= unit.getValue();
separator = true;
}
}
return builder.append(epochA > epochB ? " ago" : " in the future").toString();
}
You could probably store the LinkedHashMap
instead of instantiating it every method call, but this should work.
Upvotes: 1