56

Any suggestions for a good library for Android/Java to display time since?

(e.g. 10 minutes ago, 5 days ago)

dannyroa
  • 5,501
  • 6
  • 41
  • 59
  • I have used [Joda](http://joda-time.sourceforge.net/) and found it very helpful, check out their [documentation](http://joda-time.sourceforge.net/api-release/index.html) on [Interval](http://joda-time.sourceforge.net/api-release/org/joda/time/Interval.html) – jnthnjns Oct 22 '12 at 19:46

10 Answers10

103

From the Google I/O 2012 App:

/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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, Context ctx) {
    if (time < 1000000000000L) {
        // if timestamp given in seconds, convert to millis
        time *= 1000;
    }

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

    // TODO: localize
    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";
    }
}
kaderud
  • 5,457
  • 2
  • 36
  • 49
49

Check out DateUtils.getRelativeTimeSpanString(), it's been around since API level 3.

wsanville
  • 37,158
  • 8
  • 76
  • 101
24

From jquery timeago plugin

This provides you more options and formats than the answers above.

public static Date currentDate() {
    Calendar calendar = Calendar.getInstance();
    return calendar.getTime();
}

public static String getTimeAgo(Date date, Context ctx) {

    if(date == null) {
        return null;
    }

    long time = date.getTime();

    Date curDate = currentDate();
    long now = curDate.getTime();
    if (time > now || time <= 0) {
        return null;
    }

    int dim = getTimeDistanceInMinutes(time);

    String timeAgo = null;

    if (dim == 0) {
        timeAgo = ctx.getResources().getString(R.string.date_util_term_less) + " " +  ctx.getResources().getString(R.string.date_util_term_a) + " " + ctx.getResources().getString(R.string.date_util_unit_minute);
    } else if (dim == 1) {
        return "1 " + ctx.getResources().getString(R.string.date_util_unit_minute);
    } else if (dim >= 2 && dim <= 44) {
        timeAgo = dim + " " + ctx.getResources().getString(R.string.date_util_unit_minutes);
    } else if (dim >= 45 && dim <= 89) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_about) + " "+ctx.getResources().getString(R.string.date_util_term_an)+ " " + ctx.getResources().getString(R.string.date_util_unit_hour);
    } else if (dim >= 90 && dim <= 1439) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_about) + " " + (Math.round(dim / 60)) + " " + ctx.getResources().getString(R.string.date_util_unit_hours);
    } else if (dim >= 1440 && dim <= 2519) {
        timeAgo = "1 " + ctx.getResources().getString(R.string.date_util_unit_day);
    } else if (dim >= 2520 && dim <= 43199) {
        timeAgo = (Math.round(dim / 1440)) + " " + ctx.getResources().getString(R.string.date_util_unit_days);
    } else if (dim >= 43200 && dim <= 86399) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_about) + " "+ctx.getResources().getString(R.string.date_util_term_a)+ " " + ctx.getResources().getString(R.string.date_util_unit_month);
    } else if (dim >= 86400 && dim <= 525599) {
        timeAgo = (Math.round(dim / 43200)) + " " + ctx.getResources().getString(R.string.date_util_unit_months);
    } else if (dim >= 525600 && dim <= 655199) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_about) + " "+ctx.getResources().getString(R.string.date_util_term_a)+ " " + ctx.getResources().getString(R.string.date_util_unit_year);
    } else if (dim >= 655200 && dim <= 914399) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_over) + " "+ctx.getResources().getString(R.string.date_util_term_a)+ " " + ctx.getResources().getString(R.string.date_util_unit_year);
    } else if (dim >= 914400 && dim <= 1051199) {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_almost) + " 2 " + ctx.getResources().getString(R.string.date_util_unit_years);
    } else {
        timeAgo = ctx.getResources().getString(R.string.date_util_prefix_about) + " " + (Math.round(dim / 525600)) + " " + ctx.getResources().getString(R.string.date_util_unit_years);
    }

    return timeAgo + " " + ctx.getResources().getString(R.string.date_util_suffix);
}

private static int getTimeDistanceInMinutes(long time) {
      long timeDistance = currentDate().getTime() - time;
      return Math.round((Math.abs(timeDistance) / 1000) / 60);
}

Add following strings in your string.xml file

<!-- Date Util -->
<string name="date_util_term_less">less than</string>
<string name="date_util_term_a">a</string>
<string name="date_util_term_an">an</string>

<string name="date_util_unit_second">second</string>
<string name="date_util_unit_seconds">seconds</string>
<string name="date_util_unit_minute">minute</string>
<string name="date_util_unit_minutes">minutes</string>
<string name="date_util_unit_hour">hour</string>
<string name="date_util_unit_hours">hours</string>
<string name="date_util_unit_day">day</string>
<string name="date_util_unit_days">days</string>
<string name="date_util_unit_month">month</string>
<string name="date_util_unit_months">months</string>
<string name="date_util_unit_year">year</string>
<string name="date_util_unit_years">years</string>

<string name="date_util_prefix_lt">less than a</string>
<string name="date_util_prefix_about">about</string>
<string name="date_util_prefix_over">over</string>
<string name="date_util_prefix_almost">almost</string>

<string name="date_util_suffix">ago</string>
Tordek
  • 10,628
  • 3
  • 36
  • 67
Zahid Riaz
  • 2,879
  • 3
  • 27
  • 38
7

You can use the built-in DateUtils class with the getRelative...() methods.

Sam
  • 86,580
  • 20
  • 181
  • 179
5

If you need "n weeks ago" with @kaderud's answer, try this.

/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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;
private static final int WEEK_MILLIS = 7 * DAY_MILLIS;

public static String getTimeAgo(long time) {
    if (time < 1000000000000L) {
        // if timestamp given in seconds, convert to millis
        time *= 1000;
    }

    long now = System.currentTimeMillis();
    if (time > now || time <= 0) {
        return null;
    }

    // TODO: localize
    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 if (diff < 7 * DAY_MILLIS) {
        return diff / DAY_MILLIS + " days ago";
    } else if (diff < 2 * WEEK_MILLIS) {
        return "a week ago";
    } else {
        return diff / WEEK_MILLIS + " weeks ago";
    }
}
wonsuc
  • 3,498
  • 1
  • 27
  • 30
4

You can use also the moment.js like libray PrettyTime, that additionnaly give you translated content. http://www.ocpsoft.org/prettytime/

simple exemple :

PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
//prints: “moments from now”

System.out.println(p.format(new Date(System.currentTimeMillis() + 1000*60*10)));
//prints: “10 minutes from now”
Anthony
  • 3,989
  • 2
  • 30
  • 52
3

You can try my library Time4A which is the Android version of Time4J. Demo:

Locale uk = Locale.UK;
TZID paris = EUROPE.PARIS; 
ChronoFormatter<Moment> f = 
    ChronoFormatter.ofMomentPattern("d. MMMM uuuu h:mm B", PatternType.CLDR, uk, paris);
PrettyTime pt = PrettyTime.of(uk);

// => examples for relative formats
Moment moment = f.parse("4. January 2016 5:45 in the afternoon");
System.out.println(pt.printRelative(moment, paris)); 
// output: 1 week ago

Moment now = SystemClock.currentMoment();
System.out.println(pt.printRelative(now, paris)); 
// output: now

Moment shortTimeAgo = now.minus(15, SI.SECONDS);
System.out.println(pt.printRelative(shortTimeAgo, paris)); 
// output: 15 seconds ago

TimeUnit precision = TimeUnit.DAYS;
System.out.println(pt.printRelative(shortTimeAgo, Timezone.of(paris), precision)); 
// output: today

// => examples for list formats     
Duration<?> duration = // this way for getting duration requires extra range-module
    MomentInterval.between(moment, now).getNominalDuration(
      Timezone.of(paris), 
      DAYS, HOURS, MINUTES);

System.out.println(pt.print(duration)); 
// output: 11 days, 1 hour and 26 minutes

System.out.println(pt.print(duration, TextWidth.ABBREVIATED)); 
// output: 11 days, 1 hr, 26 min

Actually there is support for 72 languages (v3.18-2016c) including localized plural rules (important for slavish languages or Arabic) and three different text widths:

af am ar az be bg bn bs ca cs da de el en es et fa fi

fil fr ga gu he hi hr hu hy id is it ja ka kk km kn ko

ky lo lt lv mk mn mr ms my nb ne nl nn pa pl pt ro ru

si sk sl sq sr sv sw ta te th tk tr uk ur uz vi zh zu

All units from years to seconds (or even millis, micros, nanos for list formats) are supported.

In order to convert java.util.Date or long-timestamps (expressing millisecs since Unix epoch) from/to time4j-types you can use:

java.util.Date d = TemporalType.JAVA_UTIL_DATE.from(moment);
long epochMillis = TemporalType.MILLIS_SINCE_UNIX.from(moment);
Moment m1 = TemporalType.JAVA_UTIL_DATE.translate(d);
Moment m2 = TemporalType.MILLIS_SINCE_UNIX.translate(epochMillis);

Update from 2019-06-06:

The newer version Time4A-4.4-2019a with the gradle-dependency

dependencies {
    implementation group: 'net.time4j', name: 'time4j-android', version: '4.4-2019a'
}

supports 94 languages and now also includes relative patterns like "next Sunday".

Community
  • 1
  • 1
Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
2

I checked all answers mentions in this post, didn't able to get a correct and formatted output from any. finally found the answer im looking for in this post,http://memorynotfound.com/calculate-relative-time-time-ago-java/

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class TimeAgo {

    public static final Map<String, Long> times = new LinkedHashMap<>();

    static {
        times.put("year", TimeUnit.DAYS.toMillis(365));
        times.put("month", TimeUnit.DAYS.toMillis(30));
        times.put("week", TimeUnit.DAYS.toMillis(7));
        times.put("day", TimeUnit.DAYS.toMillis(1));
        times.put("hour", TimeUnit.HOURS.toMillis(1));
        times.put("minute", TimeUnit.MINUTES.toMillis(1));
        times.put("second", TimeUnit.SECONDS.toMillis(1));
    }

    public static String toRelative(long duration, int maxLevel) {
        StringBuilder res = new StringBuilder();
        int level = 0;
        for (Map.Entry<String, Long> time : times.entrySet()){
            long timeDelta = duration / time.getValue();
            if (timeDelta > 0){
                res.append(timeDelta)
                        .append(" ")
                        .append(time.getKey())
                        .append(timeDelta > 1 ? "s" : "")
                        .append(", ");
                duration -= time.getValue() * timeDelta;
                level++;
            }
            if (level == maxLevel){
                break;
            }
        }
        if ("".equals(res.toString())) {
            return "0 seconds ago";
        } else {
            res.setLength(res.length() - 2);
            res.append(" ago");
            return res.toString();
        }
    }

    public static String toRelative(long duration) {
        return toRelative(duration, times.size());
    }

    public static String toRelative(Date start, Date end){
        assert start.after(end);
        return toRelative(end.getTime() - start.getTime());
    }

    public static String toRelative(Date start, Date end, int level){
        assert start.after(end);
        return toRelative(end.getTime() - start.getTime(), level);
    }
}
2

You can use android-ago module https://github.com/curioustechizen/android-ago

Add the component in your XML

<com.github.curioustechizen.ago.RelativeTimeTextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:relative_time_prefix="Completed "
android:layout_marginTop="@dimen/margin_primary" />

set the time reference view code

RelativeTimeTextView v = (RelativeTimeTextView)findViewById(R.id.timestamp); //Or just use Butterknife!

v.setReferenceTime(new Date().getTime());

Gaurav Jassal
  • 912
  • 1
  • 6
  • 6
1

Only enter time-stamp number and get your question's answer. output come as per your location.

public static String getTimeAgo(long timestamp) {

    Calendar cal = Calendar.getInstance();
    TimeZone tz = cal.getTimeZone();//get your local time zone.
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm a");
    sdf.setTimeZone(tz);//set time zone.
    String localTime = sdf.format(new Date(timestamp * 1000));
    Date date = new Date();
    try {
        date = sdf.parse(localTime);//get local date
    } catch (ParseException e) {
        e.printStackTrace();
    }

    if(date == null) {
        return null;
    }

    long time = date.getTime();

    Date curDate = currentDate();
    long now = curDate.getTime();
    if (time > now || time <= 0) {
        return null;
    }

    int timeDIM = getTimeDistanceInMinutes(time);

    String timeAgo = null;

    if (timeDIM == 0) {
        timeAgo = "less than a minute";
    } else if (timeDIM == 1) {
        return "1 minute";
    } else if (timeDIM >= 2 && timeDIM <= 44) {
        timeAgo = timeDIM + " minutes";
    } else if (timeDIM >= 45 && timeDIM <= 89) {
        timeAgo = "about an hour";
    } else if (timeDIM >= 90 && timeDIM <= 1439) {
        timeAgo = "about " + (Math.round(timeDIM / 60)) + " hours";
    } else if (timeDIM >= 1440 && timeDIM <= 2519) {
        timeAgo = "1 day";
    } else if (timeDIM >= 2520 && timeDIM <= 43199) {
        timeAgo = (Math.round(timeDIM / 1440)) + " days";
    } else if (timeDIM >= 43200 && timeDIM <= 86399) {
        timeAgo = "about a month";
    } else if (timeDIM >= 86400 && timeDIM <= 525599) {
        timeAgo = (Math.round(timeDIM / 43200)) + " months";
    } else if (timeDIM >= 525600 && timeDIM <= 655199) {
        timeAgo = "about a year";
    } else if (timeDIM >= 655200 && timeDIM <= 914399) {
        timeAgo = "over a year";
    } else if (timeDIM >= 914400 && timeDIM <= 1051199) {
        timeAgo = "almost 2 years";
    } else {
        timeAgo = "about " + (Math.round(timeDIM / 525600)) + " years";
    }

    return timeAgo + " ago";
}

public static Date currentDate() {
    Calendar calendar = Calendar.getInstance();
    return calendar.getTime();
}

private static int getTimeDistanceInMinutes(long time) {
    long timeDistance = currentDate().getTime() - time;
    return Math.round((Math.abs(timeDistance) / 1000) / 60);
}
Ashish
  • 11
  • 2