"Time Since/Ago" Library for Android/Java

Android

Android Problem Overview


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

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

Android Solutions


Solution 1 - Android

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";
    }
}

Solution 2 - Android

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

Solution 3 - Android

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>

Solution 4 - Android

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

Solution 5 - Android

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”

Solution 6 - Android

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";
    }
}

Solution 7 - Android

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".

Solution 8 - Android

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);
    }
}

Solution 9 - Android

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());

Solution 10 - Android

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);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondannyroaView Question on Stackoverflow
Solution 1 - AndroidkaderudView Answer on Stackoverflow
Solution 2 - AndroidwsanvilleView Answer on Stackoverflow
Solution 3 - AndroidZahid RiazView Answer on Stackoverflow
Solution 4 - AndroidSamView Answer on Stackoverflow
Solution 5 - AndroidAnthonyView Answer on Stackoverflow
Solution 6 - AndroidwonsucView Answer on Stackoverflow
Solution 7 - AndroidMeno HochschildView Answer on Stackoverflow
Solution 8 - AndroidKasun WanniarachchiView Answer on Stackoverflow
Solution 9 - AndroidGaurav JassalView Answer on Stackoverflow
Solution 10 - AndroidAshish View Answer on Stackoverflow