37

I am creating a feature in an Android app to get an arbitrary date (past, present or future) and find the difference relative to now.

Both my now and due variables are longs, and this is my code:

long now = System.currentTimeMillis();
long due = now + 864000;

Log.d("Time in 1 day", DateUtils.getRelativeTimeSpanString(due,now, DateUtils.DAY_IN_MILLIS));

I want the output to be something like yesterday, today, in 4 days or 19/12/2012. However, the current output returns in 0 days...

I don't want the time to appear on these date strings.

What am I doing wrong and is the best method for formatting dates on Android?

Todd Davies
  • 5,484
  • 8
  • 47
  • 71

10 Answers10

32

What I have in mind is changing:

DateUtils.getRelativeTimeSpanString(due, now, 0L, DateUtils.FORMAT_ABBREV_ALL);

Since the documentation says it returns the time relative to now.

If that fails use some of the brilliant libraries:

Joda Time

PrettyTime

TimeAgo

Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148
9

Finally I have implemented what you wanted..!

First you need to download Joda Time from here

Extract it to any folder and put joda-time-2.2.jar into androidProject/libs folder.

MainActivity

import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Months;
import org.joda.time.MutableDateTime;
import org.joda.time.Weeks;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;


public class MainActivity extends Activity
{
  private int day ;
  private int month ;
  private int year ;
  private int hour ;
  private int minute ;
  private long selectedTimeInMillis;
  private long currentTimeInMillis;
  private String strDay ="";

 @Override
 protected void onCreate(Bundle savedInstanceState) 
 {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    year = 2013;
    month = 8;
    day = 10;
    hour = 15;
    minute = 28;

    DateTime selectedTime = new DateTime(year,month,day,hour,minute);
    selectedTimeInMillis = selectedTime.getMillis();

    MutableDateTime epoch = new MutableDateTime();
    epoch.setDate(selectedTimeInMillis); //Set to Epoch time

    DateTime now = new DateTime();

    currentTimeInMillis = now.getMillis();

    int days = Days.daysBetween(epoch, now).getDays();
    int weeks = Weeks.weeksBetween(epoch, now).getWeeks();
    int months = Months.monthsBetween(epoch, now).getMonths();

    Log.v("days since epoch: ",""+days);
    Log.v("weeks since epoch: ",""+weeks);
    Log.v("months since epoch: ",""+months);


    if(selectedTimeInMillis < currentTimeInMillis) //Past 
    {       
        long yesterdayTimeInMillis = currentTimeInMillis - 86400000;

        DateTime today = new DateTime(currentTimeInMillis);
        int year = today.getDayOfYear();
        int intToday = today.getDayOfMonth();
        DateTime yesterday = new DateTime(yesterdayTimeInMillis);
        int intYesterday = yesterday.getDayOfMonth();
        DateTime selectedDay = new DateTime(selectedTimeInMillis);
        int intselectedDay = selectedDay.getDayOfMonth();
        int intselectedYear = selectedDay.getDayOfYear();

        if(intToday == intselectedDay & year == intselectedYear)
        {
            strDay = "today";
        }
        else if(intYesterday == intselectedDay)
        {
            strDay = "yesterday";
        }
        else
        {
            strDay = "before "+ days +" days from today";
        }


    }
    else if(selectedTimeInMillis > currentTimeInMillis) //Future
    {
        long tomorrowTimeInMillis = currentTimeInMillis + 86400000;

        DateTime tomorrow = new DateTime(tomorrowTimeInMillis);
        int intTomorrow = tomorrow.getDayOfMonth();
        DateTime today = new DateTime(selectedTimeInMillis);
        int intToday = today.getDayOfMonth();

        if(intToday == intTomorrow)
        {
            strDay = "tomorrow";
        }
        else
        {
            days = -days;
            strDay = "after "+ days +" days from today";
        }


    }

    Log.v("strDay: ",""+strDay);
  }
}

You just need to change the value of day and you will get the desire output. Currently I have given date 10 as input so output will be today.

I have set date/day = 10 , month = 8 , year = 2013 , hour = 15 , min = 28

For past dates:

input day 9 output yesterday

input day 3 output before 7 days from today

input year 2012 and day 10 output before 365 days from today

For future dates:

input day 11 output tomorrow

input day 27 output after 17 days from today

input day 23 and year 2016 output after 1109 days from today
Mel
  • 5,837
  • 10
  • 37
  • 42
TheFlash
  • 5,997
  • 4
  • 41
  • 46
  • 4
    Prettytime lib is also very human readble time format http://www.londatiga.net/it/programming/android/how-to-convert-time-in-millisecond-into-relative-time-format-in-android/ – Lorensius W. L. T Feb 13 '14 at 22:13
  • oh I was searching for this,now I tested this approach and working like a charm Guys! Thanks man – Naveed Ahmad Jun 16 '14 at 12:42
  • how to get hours and mins difference from this method? Can we get the past/future hours,minutes etc? – Naveed Ahmad Jun 16 '14 at 12:50
  • @naveed ahmad yes dude u can extract hours and minutes from Milli seconds..and than u can get difference..:) – TheFlash Jun 17 '14 at 04:13
4

Best way to format a date relative to now on Android

I suggest you to use JodaTime

It's lightweight handy library and i think actually the best tool for working with Date instances.

And you can start here.

Community
  • 1
  • 1
Simon Dorociak
  • 33,374
  • 10
  • 68
  • 106
  • 9
    A 500kb library for something simple as this? Unless my app is really dependant on dates (e.g. a calendar app), I would stick to using `DateUtils`, which is embedded in the OS. – Dzhuneyt Aug 09 '13 at 13:14
  • @WordPressDeveloper JodaTime is actually best but i respect your opinion. – Simon Dorociak Aug 09 '13 at 13:25
  • 1
    I agree with @hasMobi-AndroidApps There is no such thing as "best" library. It's all about tradeoffs. – JakeWilson801 Jul 21 '16 at 03:03
4

Why not just check for yesterday and tomorrow to avoid the in 0 days/0 days ago bug and leave DateUtils.getRelativeTimeSpanString handle the remaining cases?

String relative = null;

if(now < due && (due-now)<864000){
    relative = "tomorrow";
}else if(now > due && (now-due)<864000){
    relative = "yesterday";
}else{
    relative = DateUtils.getRelativeTimeSpanString(due, now, DateUtils.DAY_IN_MILLIS); // e.g. "in 4 days"
}

Log.d("result", relative);

Edit: You may also add today with a simple check as well.

Dzhuneyt
  • 8,437
  • 14
  • 64
  • 118
1

build.gradle

compile 'joda-time:joda-time:2.9.9'

Utils.java

private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd, yyyy");
    private static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat(" 'at' h:mm aa");
    public static String getRelativeDateTimeString(Calendar startDateCalendar) {
        if (startDateCalendar == null) return null;

        DateTime startDate = new DateTime(startDateCalendar.getTimeInMillis());
        DateTime today = new DateTime();
        int days = Days.daysBetween(today.withTimeAtStartOfDay(), startDate.withTimeAtStartOfDay()).getDays();

        String date;
        switch (days) {
            case -1: date = "Yesterday"; break;
            case 0: date = "Today"; break;
            case 1: date = "Tomorrow"; break;
            default: date = DATE_FORMAT.format(startDateCalendar.getTime()); break;
        }
        String time = TIME_FORMAT.format(startDateCalendar.getTime());
        return date + time;
    }

Output

Yesterday at 9:52 AM
Today at 9:52 AM
Tomorrow at 9:52 AM
Sep 05, 2017 at 9:52 AM
thanhbinh84
  • 17,876
  • 6
  • 62
  • 69
0

The actual reason is the number 864000 is in miliseconds, which corresponds to 14 minutes. 14 minutes is so small compared to DAY_IN_MILLIS (a day). There for you get "in 0 days". If you want it to produce "in 14 mins", just change DAY_IN_MILLIS to MIN_IN_MILLIS.

Tin Tran
  • 2,265
  • 1
  • 14
  • 17
0

I came here for an alternative but I can't find perfect rather than my code. So I shared here any improvements are welcome.

public String getCreatedAtRelative() {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
    df.setTimeZone(TimeZone.getTimeZone("IST"));
    CharSequence relative = null;
    try {
        relative = DateUtils.getRelativeTimeSpanString(df.parse(createdAt).getTime(), new Date().getTime(),
                0L, DateUtils.FORMAT_ABBREV_ALL);
    } catch (ParseException e) {
        Log.e("Parse Exception adapter", "created at", e);
    } catch (NullPointerException e) {
        e.printStackTrace();
    }
    if (null == relative) {
        return createdAt;
    } else {
        return relative.toString().replace(".", " ");
    }
}
Arul
  • 1,031
  • 13
  • 23
-1

So your computation is based on milliseconds unit, then you format the result with SimpleDateFormat.

For this, you can easily use SimpleDateFormat formatter like this :

Date date = new Date(milliseconds);
SimpleDateFormat formatter = new SimpleDateFormat("EEEE dd MMMM yyyy");
String strDate = formatter.format(date);

So your computation should be based on milliseconds unit, then you format the result with SimpleDateFormat.

The pattern ("EEEE dd MMMM yyyy") allows you to get a date format like Monday, 04 February 2013.

You can change the pattern as you like : "EEEE dd/MM/yy", ...

-1

for Android you can use most simple way with Joda-Time-Android library:

Date yourTime = new Date();
DateTime dateTime = new DateTime(yourTime); //or simple DateTime.now()
final String result = DateUtils.getRelativeTimeSpanString(getContext(), dateTime);
ultraon
  • 2,220
  • 2
  • 28
  • 27
-2

long now = System.currentTimeMillis();

DateUtils.getRelativeDateTimeString(mContext, now), DateUtils.SECOND_IN_MILLIS, DateUtils.DAY_IN_MILLIS, 0)

a link!

Ar Rik
  • 31
  • 2