How to calculate elapsed time from now with Joda-Time?
Asked Answered
S

6

72

I need to calculate the time elapsed from one specific date till now and display it with the same format as StackOverflow questions, i.e.:

15s ago
2min ago
2hours ago
2days ago
25th Dec 08

Do you know how to achieve it with the Java Joda-Time library? Is there a helper method out there that already implements it, or should I write the algorithm myself?

Sivan answered 1/2, 2010 at 19:59 Comment(5)
"25th Dec 08" isn't a "time elapsed from one specific date till now" (which you wrote in bold ;)Ralli
I know. But SO is displaying question asked time like that. If the period is long enough, then it displays the exact date.Sivan
I am quite unhappy with the StackOverflow kind of handling dates. You get very precise numbers at first (37 seconds ago), but they soon become very vague (2 days ago). Only after the time is displayed in absolute format, you get to see the precise (at least to minutes) date and time again. I believe that this kind of relative information is only useful in addition to the absolute one, but cannot replace it.Countercheck
You can get the exact datetime in tooltip. Just hover the datetime a bit while :)Wilderness
Joda-Time is not well designed for printing relative (elapsed) time when you need extra features. The accepted answer is okay, but not localizable (only for English). If you need localization support then there are better 3rd-party libraries. ocpsoft/PrettyTime is such a better option, but works with old class java.util.Date only. However, my lib Time4J is IMHO the best lib for printing either relative times (ago-format) or times in format like "3 months, 4 days". It is also localizable for actually 72 languages.Biplane
W
115

To calculate the elapsed time with JodaTime, use Period. To format the elapsed time in the desired human representation, use PeriodFormatter which you can build by PeriodFormatterBuilder.

Here's a kickoff example:

DateTime myBirthDate = new DateTime(1978, 3, 26, 12, 35, 0, 0);
DateTime now = new DateTime();
Period period = new Period(myBirthDate, now);

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendSeconds().appendSuffix(" seconds ago\n")
    .appendMinutes().appendSuffix(" minutes ago\n")
    .appendHours().appendSuffix(" hours ago\n")
    .appendDays().appendSuffix(" days ago\n")
    .appendWeeks().appendSuffix(" weeks ago\n")
    .appendMonths().appendSuffix(" months ago\n")
    .appendYears().appendSuffix(" years ago\n")
    .printZeroNever()
    .toFormatter();

String elapsed = formatter.print(period);
System.out.println(elapsed);

This prints by now

3 seconds ago
51 minutes ago
7 hours ago
6 days ago
10 months ago
31 years ago

(Cough, old, cough) You see that I've taken months and years into account as well and configured it to omit the values when those are zero.

Wilderness answered 1/2, 2010 at 20:29 Comment(8)
Thx. I used your code as a basis for my algorithm. I basically display elapsed time with 1 field (or 2 maximum only). If someone is interested into the code, I can post it here as an answer. It is also i18 compliant.Sivan
I tested with the code above and discovered that the formatter is missing the weeks field - the days wrap after day 6. So I added: .appendWeeks().appendSuffix(" weeks ago\n")Tropopause
@Sivan How did you adapt it to only show one field? Please do post it as an answer.Muticous
@Sivan really how did you adapt it to only show one field ?Georgeanngeorgeanna
Is it possible to use it for future events as well ? i.e. 2 hours from now, tomorrow at 9:00 am etc ?Greathearted
I found there is a static method PeriodFormat.wordBased() that returns a pre-built word based formatter. It handles singular/plural time units unlike this example, but uses negatives instead of the relative 'ago...'Yarvis
Check this gist for showing only one field.Stoppage
When using durations, you can use Duration.asPeriod().Dib
A
20

Use PrettyTime for Simple Elapsed Time.

I tried HumanTime as @sfussenegger answered and using JodaTime's Period but the easiest and cleanest method for human readable elapsed time that I found was the PrettyTime library.

Here's a couple of simple examples with input and output:

Five Minutes Ago

DateTime fiveMinutesAgo = DateTime.now().minusMinutes( 5 );

new PrettyTime().format( fiveMinutesAgo.toDate() );

// Outputs: "5 minutes ago"

Awhile Ago

DateTime birthday = new DateTime(1978, 3, 26, 12, 35, 0, 0);

new PrettyTime().format( birthday.toDate() );

// Outputs: "4 decades ago"

CAUTION: I've tried playing around with the library's more precise functionality, but it produces some odd results so use it with care and in non-life threatening projects.

JP

Asteria answered 16/8, 2014 at 20:38 Comment(0)
D
11

You can do this with a PeriodFormatter but you don't have to go to the effort of making your own PeriodFormatBuilder as in other answers. If it suits your case, you can just use the default formatter:

Period period = new Period(startDate, endDate);
System.out.println(PeriodFormat.getDefault().print(period))

(hat tip to this answer on a similar question, I'm cross-posting for discoverability)

Dawkins answered 7/2, 2014 at 11:53 Comment(3)
I just tried this and it gives a breakdown of the period through all fields to milliseconds, so getDefault() isn't that helpful.Aurify
I just tried this and it gives a breakdown of the period through all fields to milliseconds, so getDefault() is helpful. (Thanks @snappieT)Pegmatite
This is concise and sufficient for most use cases.Kersey
D
8

There is a small helper class called HumanTime that I'm pretty happy with.

Diapedesis answered 1/2, 2010 at 20:13 Comment(1)
This is great. Adding a simple example of input and output to your answer would be really helpful.Asteria
E
0

This is using mysql timestamp to get elapsed time to now. Singular and plular is managed. Only display the max time.

NOTE: set your own timezone.

String getElapsedTime(String strMysqlTimestamp) {
    
    DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss.S");
    DateTime mysqlDate = formatter.parseDateTime(strMysqlTimestamp).
                         withZone(DateTimeZone.forID("Asia/Kuala_Lumpur"));
    
    DateTime now = new DateTime();
    Period period = new Period(mysqlDate, now);
    
    int seconds = period.getSeconds();
    int minutes = period.getMinutes();
    int hours = period.getHours();
    int days = period.getDays();
    int weeks = period.getWeeks();
    int months = period.getMonths();
    int years = period.getYears();
    
    String elapsedTime = "";
    if (years != 0)
        if (years == 1)
            elapsedTime = years + " year ago";
        else
            elapsedTime = years + " years ago";
    else if (months != 0)
        if (months == 1)
            elapsedTime = months + " month ago";
        else
            elapsedTime = months + " months ago";
    else if (weeks != 0)
        if (weeks == 1)
            elapsedTime = weeks + " week ago";
        else
            elapsedTime = weeks + " weeks ago";
    else if (days != 0)
        if (days == 1)
            elapsedTime = days + " day ago";
        else
            elapsedTime = days + " days ago";
    else if (hours != 0)
        if (hours == 1)
            elapsedTime = hours + " hour ago";
        else
            elapsedTime = hours + " hours ago";
    else if (minutes != 0)
        if (minutes == 1)
            elapsedTime = minutes + " minute ago";
        else
            elapsedTime = minutes + " minutes ago";
    else if (seconds != 0)
        if (seconds == 1)
            elapsedTime = seconds + " second ago";
        else
            elapsedTime = seconds + " seconds ago";   
    
    return elapsedTime;
} 
Electorate answered 4/1, 2021 at 17:25 Comment(0)
C
0

Here is my solution, using joda time.

private static final int SECOND_MILLIS = 1000;
private static final int MINUTE_MILLIS = 60 * SECOND_MILLIS;
private static final int HOUR_MILLIS = 60 * MINUTE_MILLIS;
private static final int DAY_MILLIS = 24 * HOUR_MILLIS;

public static String getTimeAgo(long time)
{
    if(time < 1000000000000L)
    {
        time *= 1000;
    }

    long now = System.currentTimeMillis();

    if(time > now || time <= 0)
    {
        return null;
    }

    final long diff = now - time;

    if(diff < MINUTE_MILLIS)
    {
        return "just now";
    }
    else if(diff < 2 * MINUTE_MILLIS)
    {
        return "a minute ago";
    }
    else if(diff < 50 * MINUTE_MILLIS)
    {
        return diff / MINUTE_MILLIS + " minutes ago";
    }
    else if(diff < 90 * MINUTE_MILLIS)
    {
        return "an hour ago";
    }
    else if(diff < 24 * HOUR_MILLIS)
    {
        return diff / HOUR_MILLIS + " hours ago";
    }
    else if(diff < 48 * HOUR_MILLIS)
    {
        return "yesterday";
    }
    else
    {
        return diff / DAY_MILLIS + " days ago";
    }
}

How to use the method:

Just call the method and pass in time in milliseconds.

For example:

long now = System.currentTimeMillis();
getTimeAgo(now);
Circumstantial answered 3/6, 2021 at 12:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.