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?
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?
The Answer by Habsq has the right idea but the wrong methods.
For a span-of-time unattached to the timeline on the scale of years-months-days, use Period. For days meaning 24-hour chunks of time unrelated to calendar and hours-minutes-seconds, use Duration. Mixing the two scales rarely makes any sense.
DurationStart by fetching the current moment as seen in UTC, using the Instant class.
Instant now = Instant.now(); // Capture the current moment as seen in UTC.
Instant then = now.minus( 8L , ChronoUnit.HOURS ).minus( 8L , ChronoUnit.MINUTES ).minus( 8L , ChronoUnit.SECONDS );
Duration d = Duration.between( then , now );
Generate text for hours, minutes, and seconds.
// Generate text by calling `to…Part` methods.
String output = d.toHoursPart() + " hours ago\n" + d.toMinutesPart() + " minutes ago\n" + d.toSecondsPart() + " seconds ago";
Dump to console.
System.out.println( "From: " + then + " to: " + now );
System.out.println( output );
From: 2019-06-04T11:53:55.714965Z to: 2019-06-04T20:02:03.714965Z
8 hours ago
8 minutes ago
8 seconds ago
PeriodStart by getting the current date.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument. If critical, confirm the zone with your user.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true-time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Recreate a date eight days, months, and years ago.
LocalDate then = today.minusYears( 8 ).minusMonths( 8 ).minusDays( 7 ); // Notice the 7 days, not 8, because of granularity of months.
Calculate elapsed time.
Period p = Period.between( then , today );
Build the string of "time ago" pieces.
String output = p.getDays() + " days ago\n" + p.getMonths() + " months ago\n" + p.getYears() + " years ago";
Dump to console.
System.out.println( "From: " + then + " to: " + today );
System.out.println( output );
From: 2010-09-27 to: 2019-06-04
8 days ago
8 months ago
8 years ago
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Just use this:
public static String getTimeAgoFormat(long timestamp) {
return android.text.format.DateUtils.getRelativeTimeSpanString(timestamp).toString();
}
(From a comment)
private const val SECOND_MILLIS = 1
private const val MINUTE_MILLIS = 60 * SECOND_MILLIS
private const val HOUR_MILLIS = 60 * MINUTE_MILLIS
private const val DAY_MILLIS = 24 * HOUR_MILLIS
object TimeAgo {
fun timeAgo(time: Int): String {
val now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
if (time > now || time <= 0) {
return "in the future"
}
val diff = now - time
return when {
diff < MINUTE_MILLIS -> "Just now"
diff < 2 * MINUTE_MILLIS -> "a minute ago"
diff < 60 * MINUTE_MILLIS -> "${diff / MINUTE_MILLIS} minutes ago"
diff < 2 * HOUR_MILLIS -> "an hour ago"
diff < 24 * HOUR_MILLIS -> "${diff / HOUR_MILLIS} hours ago"
diff < 48 * HOUR_MILLIS -> "yesterday"
else -> "${diff / DAY_MILLIS} days ago"
}
}
}
Call
val String = timeAgo(unixTimeStamp)
to get the time ago in Kotlin
This is a better code if we consider performance.It reduces the number of calculations. Reason Minutes are calculated only if the number of seconds is greater than 60 and Hours are calculated only if the number of minutes is greater than 60 and so on...
class timeAgo {
static String getTimeAgo(long time_ago) {
time_ago=time_ago/1000;
long cur_time = (Calendar.getInstance().getTimeInMillis())/1000 ;
long time_elapsed = cur_time - time_ago;
long seconds = time_elapsed;
// Seconds
if (seconds <= 60) {
return "Just now";
}
//Minutes
else{
int minutes = Math.round(time_elapsed / 60);
if (minutes <= 60) {
if (minutes == 1) {
return "a minute ago";
} else {
return minutes + " minutes ago";
}
}
//Hours
else {
int hours = Math.round(time_elapsed / 3600);
if (hours <= 24) {
if (hours == 1) {
return "An hour ago";
} else {
return hours + " hrs ago";
}
}
//Days
else {
int days = Math.round(time_elapsed / 86400);
if (days <= 7) {
if (days == 1) {
return "Yesterday";
} else {
return days + " days ago";
}
}
//Weeks
else {
int weeks = Math.round(time_elapsed / 604800);
if (weeks <= 4.3) {
if (weeks == 1) {
return "A week ago";
} else {
return weeks + " weeks ago";
}
}
//Months
else {
int months = Math.round(time_elapsed / 2600640);
if (months <= 12) {
if (months == 1) {
return "A month ago";
} else {
return months + " months ago";
}
}
//Years
else {
int years = Math.round(time_elapsed / 31207680);
if (years == 1) {
return "One year ago";
} else {
return years + " years ago";
}
}
}
}
}
}
}
}
}
You can use java.time.Duration and java.time.Period which are modelled on ISO-8601 standards and were introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.
Duration to calculate a time-based quantity or amount of time. It can be accessed using duration-based units, such as nanoseconds, seconds, minutes and hours. In addition, the DAYS unit can be used and is treated as exactly equal to 24 hours, thus ignoring daylight savings effects.Period to calculate a date-based amount of time. It can be accessed using period-based units, such as days, months and years.Demo:
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
public class Main {
public static void main(String[] args) {
// An arbitrary local date and time
LocalDateTime startDateTime = LocalDateTime.of(2020, Month.DECEMBER, 10, 15, 20, 25);
// Current local date and time
LocalDateTime endDateTime = LocalDateTime.now();
Duration duration = Duration.between(startDateTime, endDateTime);
// Default format
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format("%d days, %d hours, %d minutes, %d seconds, %d nanoseconds ago",
duration.toDays(), duration.toHours() % 24, duration.toMinutes() % 60, duration.toSeconds() % 60,
duration.toNanos() % 1000000000);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%d days, %d hours, %d minutes, %d seconds, %d nanoseconds ago",
duration.toDaysPart(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(),
duration.toNanosPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT1395H35M7.355288S
58 days, 3 hours, 35 minutes, 7 seconds, 355288000 nanoseconds ago
58 days, 3 hours, 35 minutes, 7 seconds, 355288000 nanoseconds ago
If you have two moments at UTC, you can use Instant instead of LocalDateTime e.g.
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
// Current moment at UTC
Instant now = Instant.now();
// An instant in the past
Instant startDateTime = now.minus(58, ChronoUnit.DAYS)
.minus(2, ChronoUnit.HOURS)
.minus(54, ChronoUnit.MINUTES)
.minus(24, ChronoUnit.SECONDS)
.minus(808624000, ChronoUnit.NANOS);
Duration duration = Duration.between(startDateTime, now);
// Default format
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format("%d days, %d hours, %d minutes, %d seconds, %d nanoseconds ago",
duration.toDays(), duration.toHours() % 24, duration.toMinutes() % 60, duration.toSeconds() % 60,
duration.toNanos() % 1000000000);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%d days, %d hours, %d minutes, %d seconds, %d nanoseconds ago",
duration.toDaysPart(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(),
duration.toNanosPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT1394H54M24.808624S
58 days, 2 hours, 54 minutes, 24 seconds, 808624000 nanoseconds ago
58 days, 2 hours, 54 minutes, 24 seconds, 808624000 nanoseconds ago
Demo on Period:
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
// Replace ZoneId.systemDefault() with the applicable timezone ID e.g.
// ZoneId.of("Europe/London"). For LocalDate in the JVM's timezone, simply use
// LocalDate.now()
LocalDate endDate = LocalDate.now(ZoneId.systemDefault());
// Let's assume the start date is 1 year, 2 months, and 3 days ago
LocalDate startDate = endDate.minusYears(1).minusMonths(2).minusDays(3);
Period period = Period.between(startDate, endDate);
// Default format
System.out.println(period);
// Custom format
String formattedElapsedPeriod = String.format("%d years, %d months, %d days ago", period.getYears(),
period.getMonths(), period.getDays());
System.out.println(formattedElapsedPeriod);
}
}
Output:
P1Y2M3D
1 years, 2 months, 3 days ago
Learn about the modern date-time API from Trail: Date Time.
You can use Java's Library RelativeDateTimeFormatter, it does exactly that:
RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance();
fmt.format(1, Direction.NEXT, RelativeUnit.DAYS); // "in 1 day"
fmt.format(3, Direction.NEXT, RelativeUnit.DAYS); // "in 3 days"
fmt.format(3.2, Direction.LAST, RelativeUnit.YEARS); // "3.2 years ago"
fmt.format(Direction.LAST, AbsoluteUnit.SUNDAY); // "last Sunday"
fmt.format(Direction.THIS, AbsoluteUnit.SUNDAY); // "this Sunday"
fmt.format(Direction.NEXT, AbsoluteUnit.SUNDAY); // "next Sunday"
fmt.format(Direction.PLAIN, AbsoluteUnit.SUNDAY); // "Sunday"
fmt.format(Direction.LAST, AbsoluteUnit.DAY); // "yesterday"
fmt.format(Direction.THIS, AbsoluteUnit.DAY); // "today"
fmt.format(Direction.NEXT, AbsoluteUnit.DAY); // "tomorrow"
fmt.format(Direction.PLAIN, AbsoluteUnit.NOW); // "now"
SQL timestamp to now elapsed time. Set your own timezone.
NOTE1: This will handle singular/plural.
NOTE2: This is using Joda time
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;
}
for this I've done Just Now, seconds ago, min ago, hrs ago, days ago, weeks ago, months ago, years ago in this example you can parse date like 2018-09-05T06:40:46.183Z this or any other like below
add below value in string.xml
<string name="lbl_justnow">Just Now</string>
<string name="lbl_seconds_ago">seconds ago</string>
<string name="lbl_min_ago">min ago</string>
<string name="lbl_mins_ago">mins ago</string>
<string name="lbl_hr_ago">hr ago</string>
<string name="lbl_hrs_ago">hrs ago</string>
<string name="lbl_day_ago">day ago</string>
<string name="lbl_days_ago">days ago</string>
<string name="lbl_lstweek_ago">last week</string>
<string name="lbl_week_ago">weeks ago</string>
<string name="lbl_onemonth_ago">1 month ago</string>
<string name="lbl_month_ago">months ago</string>
<string name="lbl_oneyear_ago" >last year</string>
<string name="lbl_year_ago" >years ago</string>
java code try below
public String getFormatDate(String postTime1) {
Calendar cal=Calendar.getInstance();
Date now=cal.getTime();
String disTime="";
try {
Date postTime;
//2018-09-05T06:40:46.183Z
postTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(postTime1);
long diff=(now.getTime()-postTime.getTime()+18000)/1000;
//for months
Calendar calObj = Calendar.getInstance();
calObj.setTime(postTime);
int m=calObj.get(Calendar.MONTH);
calObj.setTime(now);
SimpleDateFormat monthFormatter = new SimpleDateFormat("MM"); // output month
int mNow = Integer.parseInt(monthFormatter.format(postTime));
diff = diff-19800;
if(diff<15) { //below 15 sec
disTime = getResources().getString(R.string.lbl_justnow);
} else if(diff<60) {
//below 1 min
disTime= diff+" "+getResources().getString(R.string.lbl_seconds_ago);
} else if(diff<3600) {//below 1 hr
// convert min
long temp=diff/60;
if(temp==1) {
disTime= temp + " " +getResources().getString(R.string.lbl_min_ago);
} else {
disTime = temp + " " +getResources().getString(R.string.lbl_mins_ago);
}
} else if(diff<(24*3600)) {// below 1 day
// convert hr
long temp= diff/3600;
System.out.println("hey temp3:"+temp);
if(temp==1) {
disTime = temp + " " +getResources().getString(R.string.lbl_hr_ago);
} else {
disTime = temp + " " +getResources().getString(R.string.lbl_hrs_ago);
}
} else if(diff<(24*3600*7)) {// below week
// convert days
long temp=diff/(3600*24);
if (temp==1) {
// disTime = "\nyesterday";
disTime = temp + " " +getResources().getString(R.string.lbl_day_ago);
} else {
disTime = temp + " " +getResources().getString(R.string.lbl_days_ago);
}
} else if(diff<((24*3600*28))) {// below month
// convert week
long temp=diff/(3600*24*7);
if (temp <= 4) {
if (temp < 1) {
disTime = getResources().getString(R.string.lbl_lstweek_ago);
}else{
disTime = temp + " " + getResources().getString(R.string.lbl_week_ago);
}
} else {
int diffMonth = mNow - m;
Log.e("count : ", String.valueOf(diffMonth));
disTime = diffMonth + " " + getResources().getString(R.string.lbl_month_ago);
}
}else if(diff<((24*3600*365))) {// below year
// convert month
long temp=diff/(3600*24*30);
System.out.println("hey temp2:"+temp);
if (temp <= 12) {
if (temp == 1) {
disTime = getResources().getString(R.string.lbl_onemonth_ago);
}else{
disTime = temp + " " + getResources().getString(R.string.lbl_month_ago);
}
}
}else if(diff>((24*3600*365))) { // above year
// convert year
long temp=diff/(3600*24*30*12);
System.out.println("hey temp8:"+temp);
if (temp == 1) {
disTime = getResources().getString(R.string.lbl_oneyear_ago);
}else{
disTime = temp + " " + getResources().getString(R.string.lbl_year_ago);
}
}
} catch(Exception e) {
e.printStackTrace();
}
return disTime;
}
I'm using the Instant, Date and DateTimeUtils. The data (date) which is stored in the database in type of String and then convert to become Instant.
/*
This method is to display ago.
Example: 3 minutes ago.
I already implement the latest which is including the Instant.
Convert from String to Instant and then parse to Date.
*/
public String convertTimeToAgo(String dataDate) {
//Initialize
String conversionTime = null;
String suffix = "Yang Lalu";
Date pastTime;
//Parse from String (which is stored as Instant.now().toString()
//And then convert to become Date
Instant instant = Instant.parse(dataDate);
pastTime = DateTimeUtils.toDate(instant);
//Today date
Date nowTime = new Date();
long dateDiff = nowTime.getTime() - pastTime.getTime();
long second = TimeUnit.MILLISECONDS.toSeconds(dateDiff);
long minute = TimeUnit.MILLISECONDS.toMinutes(dateDiff);
long hour = TimeUnit.MILLISECONDS.toHours(dateDiff);
long day = TimeUnit.MILLISECONDS.toDays(dateDiff);
if (second < 60) {
conversionTime = second + " Saat " + suffix;
} else if (minute < 60) {
conversionTime = minute + " Minit " + suffix;
} else if (hour < 24) {
conversionTime = hour + " Jam " + suffix;
} else if (day >= 7) {
if (day > 30) {
conversionTime = (day / 30) + " Bulan " + suffix;
} else if (day > 360) {
conversionTime = (day / 360) + " Tahun " + suffix;
} else {
conversionTime = (day / 7) + " Minggu " + suffix;
}
} else if (day < 7) {
conversionTime = day + " Hari " + suffix;
}
return conversionTime;
}
The following solutions are all in pure Java:
The following function will only display the largest time container, for example, if the true elapsed time is "1 month 14 days ago", this function will only display "1 month ago". This function will also always round down, so a time equivalent to "50 days ago" will display as "1 month"
public String formatTimeAgo(long millis) {
String[] ids = new String[]{"second","minute","hour","day","month","year"};
long seconds = millis / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = hours / 24;
long months = days / 30;
long years = months / 12;
ArrayList<Long> times = new ArrayList<>(Arrays.asList(years, months, days, hours, minutes, seconds));
for(int i = 0; i < times.size(); i++) {
if(times.get(i) != 0) {
long value = times.get(i).intValue();
return value + " " + ids[ids.length - 1 - i] + (value == 1 ? "" : "s") + " ago";
}
}
return "0 seconds ago";
}
Simply wrap the time container you'd like to round with a Math.round(...) statement, so if you wanted to round 50 days to 2 months, modify long months = days / 30 to long months = Math.round(days / 30.0)
Here is my test case, hope it helps:
val currentCalendar = Calendar.getInstance()
currentCalendar.set(2019, 6, 2, 5, 31, 0)
val targetCalendar = Calendar.getInstance()
targetCalendar.set(2019, 6, 2, 5, 30, 0)
val diffTs = currentCalendar.timeInMillis - targetCalendar.timeInMillis
val diffMins = TimeUnit.MILLISECONDS.toMinutes(diffTs)
val diffHours = TimeUnit.MILLISECONDS.toHours(diffTs)
val diffDays = TimeUnit.MILLISECONDS.toDays(diffTs)
val diffWeeks = TimeUnit.MILLISECONDS.toDays(diffTs) / 7
val diffMonths = TimeUnit.MILLISECONDS.toDays(diffTs) / 30
val diffYears = TimeUnit.MILLISECONDS.toDays(diffTs) / 365
val newTs = when {
diffYears >= 1 -> "Years $diffYears"
diffMonths >= 1 -> "Months $diffMonths"
diffWeeks >= 1 -> "Weeks $diffWeeks"
diffDays >= 1 -> "Days $diffDays"
diffHours >= 1 -> "Hours $diffHours"
diffMins >= 1 -> "Mins $diffMins"
else -> "now"
}
The getrelativeDateTime function will give you date time as you see in Whatsapp notification.
To get future relative Date Time add conditions for it. This is created specifically for getting Date Time like Whatsapp notification.
private static String getRelativeDateTime(long date) {
SimpleDateFormat DateFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());
SimpleDateFormat TimeFormat = new SimpleDateFormat(" hh:mm a", Locale.getDefault());
long now = Calendar.getInstance().getTimeInMillis();
long startOfDay = StartOfDay(Calendar.getInstance().getTime());
String Day = "";
String Time = "";
long millSecInADay = 86400000;
long oneHour = millSecInADay / 24;
long differenceFromNow = now - date;
if (date > startOfDay) {
if (differenceFromNow < (oneHour)) {
int minute = (int) (differenceFromNow / (60000));
if (minute == 0) {
int sec = (int) differenceFromNow / 1000;
if (sec == 0) {
Time = "Just Now";
} else if (sec == 1) {
Time = sec + " second ago";
} else {
Time = sec + " seconds ago";
}
} else if (minute == 1) {
Time = minute + " minute ago";
} else if (minute < 60) {
Time = minute + " minutes ago";
}
} else {
Day = "Today, ";
}
} else if (date > (startOfDay - millSecInADay)) {
Day = "Yesterday, ";
} else if (date > (startOfDay - millSecInADay * 7)) {
int days = (int) (differenceFromNow / millSecInADay);
Day = days + " Days ago, ";
} else {
Day = DateFormat.format(date);
}
if (Time.isEmpty()) {
Time = TimeFormat.format(date);
}
return Day + Time;
}
public static long StartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
For the lack of simplicity and updated response, follows the more recent Java 8 and beyond version
import java.time.*;
import java.time.temporal.*;
public class Time {
public static void main(String[] args) {
System.out.println(LocalTime.now().minus(8, ChronoUnit.MINUTES));
System.out.println(LocalTime.now().minus(8, ChronoUnit.HOURS));
System.out.println(LocalDateTime.now().minus(8, ChronoUnit.DAYS));
System.out.println(LocalDateTime.now().minus(8, ChronoUnit.MONTHS));
}
}
This is the version that uses the Java Time API that tries to solve issues from the past for dealing with Date and Time.
Javadoc
version 8 https://docs.oracle.com/javase/8/docs/api/index.html?java/time/package-summary.html
version 11 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/package-summary.html
W3Schools Tutorial - https://www.w3schools.com/java/java_date.asp
DZone Article - https://dzone.com/articles/java-8-date-and-time