How to calculate "time ago" in Java?
Solution 1
Take a look at the PrettyTime library.
It's quite simple to use:
import org.ocpsoft.prettytime.PrettyTime;
PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
// prints "moments ago"
You can also pass in a locale for internationalized messages:
PrettyTime p = new PrettyTime(new Locale("fr"));
System.out.println(p.format(new Date()));
// prints "à l'instant"
As noted in the comments, Android has this functionality built into the android.text.format.DateUtils
class.
Solution 2
Have you considered the TimeUnit enum? It can be pretty useful for this kind of thing
try {
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date past = format.parse("01/10/2010");
Date now = new Date();
System.out.println(TimeUnit.MILLISECONDS.toMillis(now.getTime() - past.getTime()) + " milliseconds ago");
System.out.println(TimeUnit.MILLISECONDS.toMinutes(now.getTime() - past.getTime()) + " minutes ago");
System.out.println(TimeUnit.MILLISECONDS.toHours(now.getTime() - past.getTime()) + " hours ago");
System.out.println(TimeUnit.MILLISECONDS.toDays(now.getTime() - past.getTime()) + " days ago");
}
catch (Exception j){
j.printStackTrace();
}
Solution 3
I take RealHowTo and Ben J answers and make my own version:
public class TimeAgo {
public static final List<Long> times = Arrays.asList(
TimeUnit.DAYS.toMillis(365),
TimeUnit.DAYS.toMillis(30),
TimeUnit.DAYS.toMillis(1),
TimeUnit.HOURS.toMillis(1),
TimeUnit.MINUTES.toMillis(1),
TimeUnit.SECONDS.toMillis(1) );
public static final List<String> timesString = Arrays.asList("year","month","day","hour","minute","second");
public static String toDuration(long duration) {
StringBuffer res = new StringBuffer();
for(int i=0;i< TimeAgo.times.size(); i++) {
Long current = TimeAgo.times.get(i);
long temp = duration/current;
if(temp>0) {
res.append(temp).append(" ").append( TimeAgo.timesString.get(i) ).append(temp != 1 ? "s" : "").append(" ago");
break;
}
}
if("".equals(res.toString()))
return "0 seconds ago";
else
return res.toString();
}
public static void main(String args[]) {
System.out.println(toDuration(123));
System.out.println(toDuration(1230));
System.out.println(toDuration(12300));
System.out.println(toDuration(123000));
System.out.println(toDuration(1230000));
System.out.println(toDuration(12300000));
System.out.println(toDuration(123000000));
System.out.println(toDuration(1230000000));
System.out.println(toDuration(12300000000L));
System.out.println(toDuration(123000000000L));
}}
which will print the following
0 second ago
1 second ago
12 seconds ago
2 minutes ago
20 minutes ago
3 hours ago
1 day ago
14 days ago
4 months ago
3 years ago
Solution 4
public class TimeUtils {
public final static long ONE_SECOND = 1000;
public final static long SECONDS = 60;
public final static long ONE_MINUTE = ONE_SECOND * 60;
public final static long MINUTES = 60;
public final static long ONE_HOUR = ONE_MINUTE * 60;
public final static long HOURS = 24;
public final static long ONE_DAY = ONE_HOUR * 24;
private TimeUtils() {
}
/**
* converts time (in milliseconds) to human-readable format
* "<w> days, <x> hours, <y> minutes and (z) seconds"
*/
public static String millisToLongDHMS(long duration) {
StringBuilder res = new StringBuilder();
long temp = 0;
if (duration >= ONE_SECOND) {
temp = duration / ONE_DAY;
if (temp > 0) {
duration -= temp * ONE_DAY;
res.append(temp).append(" day").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_HOUR;
if (temp > 0) {
duration -= temp * ONE_HOUR;
res.append(temp).append(" hour").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_MINUTE;
if (temp > 0) {
duration -= temp * ONE_MINUTE;
res.append(temp).append(" minute").append(temp > 1 ? "s" : "");
}
if (!res.toString().equals("") && duration >= ONE_SECOND) {
res.append(" and ");
}
temp = duration / ONE_SECOND;
if (temp > 0) {
res.append(temp).append(" second").append(temp > 1 ? "s" : "");
}
return res.toString();
} else {
return "0 second";
}
}
public static void main(String args[]) {
System.out.println(millisToLongDHMS(123));
System.out.println(millisToLongDHMS((5 * ONE_SECOND) + 123));
System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR));
System.out.println(millisToLongDHMS(ONE_DAY + 2 * ONE_SECOND));
System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR + (2 * ONE_MINUTE)));
System.out.println(millisToLongDHMS((4 * ONE_DAY) + (3 * ONE_HOUR)
+ (2 * ONE_MINUTE) + ONE_SECOND));
System.out.println(millisToLongDHMS((5 * ONE_DAY) + (4 * ONE_HOUR)
+ ONE_MINUTE + (23 * ONE_SECOND) + 123));
System.out.println(millisToLongDHMS(42 * ONE_DAY));
/*
output :
0 second
5 seconds
1 day, 1 hour
1 day and 2 seconds
1 day, 1 hour, 2 minutes
4 days, 3 hours, 2 minutes and 1 second
5 days, 4 hours, 1 minute and 23 seconds
42 days
*/
}
}
more @Format a duration in milliseconds into a human-readable format
Solution 5
This is based on RealHowTo's answer so if you like it, give him/her some love too.
This cleaned up version allows you to specify the range of time you might be interested in.
It also handles the " and " part a little differently. I often find when joining strings with a delimiter it's ofter easier to skip the complicated logic and just delete the last delimiter when you're done.
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class TimeUtils {
/**
* Converts time to a human readable format within the specified range
*
* @param duration the time in milliseconds to be converted
* @param max the highest time unit of interest
* @param min the lowest time unit of interest
*/
public static String formatMillis(long duration, TimeUnit max, TimeUnit min) {
StringBuilder res = new StringBuilder();
TimeUnit current = max;
while (duration > 0) {
long temp = current.convert(duration, MILLISECONDS);
if (temp > 0) {
duration -= current.toMillis(temp);
res.append(temp).append(" ").append(current.name().toLowerCase());
if (temp < 2) res.deleteCharAt(res.length() - 1);
res.append(", ");
}
if (current == min) break;
current = TimeUnit.values()[current.ordinal() - 1];
}
// clean up our formatting....
// we never got a hit, the time is lower than we care about
if (res.lastIndexOf(", ") < 0) return "0 " + min.name().toLowerCase();
// yank trailing ", "
res.deleteCharAt(res.length() - 2);
// convert last ", " to " and"
int i = res.lastIndexOf(", ");
if (i > 0) {
res.deleteCharAt(i);
res.insert(i, " and");
}
return res.toString();
}
}
Little code to give it a whirl:
import static java.util.concurrent.TimeUnit.*;
public class Main {
public static void main(String args[]) {
long[] durations = new long[]{
123,
SECONDS.toMillis(5) + 123,
DAYS.toMillis(1) + HOURS.toMillis(1),
DAYS.toMillis(1) + SECONDS.toMillis(2),
DAYS.toMillis(1) + HOURS.toMillis(1) + MINUTES.toMillis(2),
DAYS.toMillis(4) + HOURS.toMillis(3) + MINUTES.toMillis(2) + SECONDS.toMillis(1),
DAYS.toMillis(5) + HOURS.toMillis(4) + MINUTES.toMillis(1) + SECONDS.toMillis(23) + 123,
DAYS.toMillis(42)
};
for (long duration : durations) {
System.out.println(TimeUtils.formatMillis(duration, DAYS, SECONDS));
}
System.out.println("\nAgain in only hours and minutes\n");
for (long duration : durations) {
System.out.println(TimeUtils.formatMillis(duration, HOURS, MINUTES));
}
}
}
Which will output the following:
0 seconds
5 seconds
1 day and 1 hour
1 day and 2 seconds
1 day, 1 hour and 2 minutes
4 days, 3 hours, 2 minutes and 1 second
5 days, 4 hours, 1 minute and 23 seconds
42 days
Again in only hours and minutes
0 minutes
0 minutes
25 hours
24 hours
25 hours and 2 minutes
99 hours and 2 minutes
124 hours and 1 minute
1008 hours
And in case anyone ever needs it, here's a class that will convert any string like the above back into milliseconds. It's pretty useful for allowing people to specify timeouts of various things in readable text.
jts
Updated on July 08, 2022Comments
-
jts almost 2 years
In Ruby on Rails, there is a feature that allows you to take any Date and print out how "long ago" it was.
For example:
8 minutes ago 8 hours ago 8 days ago 8 months ago 8 years ago
Is there an easy way to do this in Java?
-
David Blevins about 13 yearsI ended up using a revised version of this. Posted my revisions for you.
-
Somatik over 12 yearsIn case you are working on android you can use this: android.text.format.DateUtils#getRelativeTimeSpanString()
-
Ajay S about 10 yearsCan you please add some more description to your answer, link only answer is not good for now.
-
zakmck almost 10 yearsDavid Blevins, more examples about PrettyTime: stackoverflow.com/questions/3859288/… Big -1 for reinventing the wheel once more and not recommending a 3rd party library :-p
-
Piotr almost 10 yearsReally cool. And it is really easy to add other time units such as week(s)
-
Weblance over 9 years@Somatik if you need to get this on a non-android platform, you can view that class on AOSP.
-
PhiLho over 9 yearsIn most cases, you want a "smart" display, ie. instead of 5125 minutes ago, you tell x days ago.
-
Hardik Parmar over 8 years@ataylor how this use in Android ??
-
fangzhzh over 8 yearsthis one deserves more upvotes. First of all, no library needed. It's still clean, elegant and easy to change.
-
Diogo Gomes about 8 yearssmall typo: inside your code your are referencing the static properties "Lists" instead of "TimeAgo". Lists.times.get(i) should be TimeAgo.get(i)... and so on
-
Riccardo Casatta about 8 yearsStatic variable name typo fixed.
-
Codeversed about 8 yearsgetRelativeTimeSpanString is not ideal for all situations and that is why I created my own class based on lots of the examples here. See my solution below: stackoverflow.com/a/37042254/468360
-
Nativ almost 8 yearsI don't think that this is a complete answer since the time units are independent. E.g - the milliseconds time is just minutes time * 60 * 1000. You need to decrease from each time unit that next bigger time unit(after converting it to the lower time unit) in order to be able to use it in a "time ago" string.
-
BlackPearl almost 8 years@RiccardoCasatta I entered DateTime in this format
2016-06-07 07:20:33
, it always return 16days ago, why? -
Riccardo Casatta almost 8 yearsThe function take a parameter which is a time delta expressed in millisecond. You should pass differences between dates not a date alone. You are probably passing a timestamp (which is second from 1970) to the function.
-
Disapamok over 7 yearsI'm using this library to get ago time in this timestamp : "2016-09-24 09:57:12 AM", But this returns "9 Months Ago". (Current time is : 2016-09-24 10:53 AM).
PrettyTime pt = new PrettyTime(new Locale("en")); System.out.print(pt.format(date_format.parse("2016-09-24 09:57:12 AM")));
Working on simple EE project with glassfish server. @ataylor -
berkus over 7 yearsLittle suggestion: use
.append(temp != 1 ? "s" : "")
instead of.append(temp > 1 ? "s" : "")
because 0 should also haves
suffix -
Swift over 7 years@Benj - Does it right ? above solution ? because one time is in 12 hour format and another time is in 24 hour format. Let me know your feedback for my query. Thank you in advanced.
-
user2590928 about 7 yearsNice solution, very easy to modify if you'd like other text like "Yesterday" or adapt so it can give periods relative to a future date ie "In 3 weeks"
-
Basil Bourque about 7 yearsThose
Duration
methods report the entire duration as a total number of hours and as a total number of minutes. In Java 8 the class oddly lacked any methods to get each part of hour and minutes and seconds. Java 9 brings those methods,to…Part
. -
Shajeel Afzal almost 7 yearsIs
toDuration
method taking the duration parameter as milliseconds? If Yes, then passing 1502531374441 is returning 47 years ago to me but it is my current time. -
Riccardo Casatta almost 7 years@ShajeelAfzal yes, the duration parameter is in millisecond but it's a difference between times not an absolute value. What you are getting is the time which has passed from the 1st of January 1970 the date when the unix timestamp started
-
Jonathan Laliberte over 6 yearsthis is incorrect though... each unit is independent of eachother like already mentioned.
-
silentsudo over 5 yearsThanks A TON for this.
-
Wajid over 5 yearsIn case you are working on android you can use this: android.text.format.DateUtils.getRelativeTimeSpanString(milliseconds)
-
Wajid over 5 yearsIn case you are working on android you can use this: android.text.format.DateUtils.getRelativeTimeSpanString(milliseconds)
-
Wajid over 5 yearsIn case you are working on android you can use this: android.text.format.DateUtils.getRelativeTimeSpanString(milliseconds)
-
Wajid over 5 yearsandroid does it for you In case you are working on android you can use this: android.text.format.DateUtils.getRelativeTimeSpanString(milliseconds)
-
Madbreaks over 5 yearsThat's an Android library, not a Java library.
-
TheRealChx101 about 5 yearsI always get times in the future, like "1 hour from now" when the time passed is actually before.
-
Basil Bourque almost 5 yearsYou are using terrible old date-time classes that were supplanted years ago by the java.time classes.
-
Ticherhaz FreePalestine almost 5 years@BasilBourque I still cannot find the latest way to do this.
-
Ticherhaz FreePalestine almost 5 yearsThank you so much!
-
Ticherhaz FreePalestine almost 5 years@BasilBourque I've added some code for me to understand it. github.com/ticherhaz/tarikhmasa
-
Haya Akkad almost 5 yearsIt's very useful, Thank you very much.
-
user1735921 over 4 yearsfor something so simple there is no point of using a library, can just make one util class inside
-
Basil Bourque over 4 years
-
Basil Bourque over 4 yearsThe terrible
Calendar
class was supplanted years ago by the modern java.time classes with the adoption of JSR 310. Poor advice in 2019. -
Basil Bourque over 4 yearsYou must have meant
var
, notval
. -
Ole V.V. almost 4 yearsThanks for wanting to contribute. It's not exactly what was asked, but maybe someone can use it. Neither that someone nor anyone else should want to use
SimpleDateFormat
andCalendar
, though. Those classes are poorly designed and long outdated. Instead read the answers that are employing java.time, the modern Java date and time API. -
MDT over 2 yearsaccuracy for yesterday should be based on day factor, since 48 hours can be between 3 days and things done on day 1 will be labeled as yesterday which in reality is day 3 or (3 days ago).
-
Bill K over 2 yearsThis is a good answer--rather than adding my own I'll just +1 and mention here that Duration has a "Parse" method that lets you specify values more easily, like "P2D" is 2 days, "P2D2M" is 2 days and 2 minutes. This gives a straight-forward Instance.now().minus(Duraiton.parse("P2H")) for "2 Hours Ago"