Your Computer Science Resource

Java Relative Date and Time

In an application I’m working on, I needed the ability to show the date and time in a relative manner. In other words, rather than displaying the full date and time like “August 13, 2009 at 3:07 pm”, I wanted to be able to determine when this date is relative to now and show this instead (e.g. “one hour ago” or “one hour from now”). The result? A simple yet useful class which converts a Calendar or Date object into a String representing the relative date.

The Code

In my case, I wanted to be able to convert a Calendar or Date object into a relative date only if the date isn’t beyond one day in the past or future. For example, I want to look at a date and be able to convert it to “23 seconds ago” or “2 hours ago”, etc. in comparison to the date and time that it is right now. BUT, as mentioned, if the relative date is beyond one day, I want to display the full date instead, and in some cases, I would like to be able to display the full date and time in a format of my choosing (see the setDateFormat method). Below is a class file that is the outcome of these requirements.

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class RelativeDate {

    private static SimpleDateFormat sdf = new SimpleDateFormat( "h:mm a MMM dd, yyyy" );

    /**
     * This method computes the relative date according to
     * the Calendar being passed in and the number of years,
     * months, days, etc. that differ. This will compute both
     * past and future relative dates. E.g., "one day ago" and
     * "one day from now".
     * <p>
     * <strong>NOTE:</strong> If the calendar date relative
     * to "now" is older than one day, we display the actual date in
     * its default format as specified by this class. The date format
     * may be changed by calling {@link RelativeDate#setDateFormat(SimpleDateFormat)}
     * If you don't want to show the actual date, but you want to show
     * the relative date for days, months, and years, you can add the
     * other cases in by copying the logic for hours, minutes, seconds.
     *
     * @param Calendar calendar
     * @param int years
     * @param int months
     * @param int days
     * @param int hours
     * @param int minutes
     * @param int seconds
     * @return String representing the relative date
     */

    private static String computeRelativeDate( Calendar calendar, int years, int months, int days, int hours, int minutes, int seconds ) {

        String date = sdf.format( calendar.getTime() );

        // Year

        if ( years != 0 ) return date;

        // Month

        else if ( months != 0 ) return date;

        // Day

        else if ( days != 0 ) return date;

        // Hour

        else if ( hours == 1 ) return 1 + " hour from now";
        else if ( hours == -1 ) return 1 + " hour ago";
        else if ( hours > 0 ) return hours + " hours from now";
        else if ( hours < 0 ) return Math.abs( hours ) + " hours ago";

        // Minute

        else if ( minutes == 1 ) return 1 + " minute from now";
        else if ( minutes == -1 ) return 1 + " minute ago";
        else if ( minutes > 0 ) return minutes + " minutes from now";
        else if ( minutes < 0 ) return Math.abs( minutes ) + " minutes ago";

        // Second

        else if ( seconds == 1 ) return 1 + " second from now";
        else if ( seconds == -1 ) return 1 + " second ago";
        else if ( seconds > 0 ) return seconds + " seconds from now";
        else if ( seconds < 0 ) return Math.abs( seconds ) + " seconds ago";

        // Must be now (date and times are identical)

        else return "now";

    } // end method computeRelativeDate

    /**
     * This method returns a String representing the relative
     * date by comparing the Calendar being passed in to the
     * date / time that it is right now.
     *
     * @param Calendar calendar
     * @return String representing the relative date
     */

    public static String getRelativeDate( Calendar calendar ) {

        Calendar now = GregorianCalendar.getInstance();

        int years = calendar.get( Calendar.YEAR ) - now.get( Calendar.YEAR );
        int months = calendar.get( Calendar.MONTH ) - now.get( Calendar.MONTH );
        int days = calendar.get( Calendar.DAY_OF_MONTH ) - now.get( Calendar.DAY_OF_MONTH );
        int hours = calendar.get( Calendar.HOUR_OF_DAY ) - now.get( Calendar.HOUR_OF_DAY );
        int minutes = calendar.get( Calendar.MINUTE ) - now.get( Calendar.MINUTE );
        int seconds = calendar.get( Calendar.SECOND ) - now.get( Calendar.SECOND );

        return computeRelativeDate( calendar, years, months, days, hours, minutes, seconds );

    } // end method getRelativeDate

    /**
     * This method returns a String representing the relative
     * date by comparing the Date being passed in to the date /
     * time that it is right now.
     *
     * @param Date date
     * @return String representing the relative date
     */

    public static String getRelativeDate( Date date ) {

        Calendar converted = GregorianCalendar.getInstance();
        converted.setTime( date );
        return getRelativeDate( converted );

    } // end method getRelativeDate

    /**
     * This method sets the date format. This is used when
     * the relative date is beyond one day. E.g., if the
     * relative date is > 1 day, we will display the date
     * in the format: h:mm a MMM dd, yyyy
     * <p>
     * This can be changed by passing in a new simple date
     * format and then calling {@link RelativeDate#getRelativeDate(Calendar)}
     * or {@link RelativeDate#getRelativeDate(Date)}.
     *
     * @param SimpleDateFormat dateFormat
     */

    public static void setDateFormat( SimpleDateFormat dateFormat ) {

        sdf = dateFormat;

    } // end method setDateFormat

} // end class RelativeDate

Usage

Include the file in your project, and call the getRelativeDate method in a static manner, while passing in a Calendar or Date object as a parameter. Below is an example. The returned String is your relative date / time. Again, note that if the relative date is beyond one day, it will display the full date and time — this was my desired result.

import java.util.Calendar;
import java.util.GregorianCalendar;

public class RelativeDateTest {

    public static void main(String[] args) {

        // Get a new calendar instance

        Calendar c = GregorianCalendar.getInstance();

        // Set the date and time

        c.set( Calendar.YEAR, 2009 );
        c.set( Calendar.MONTH, Calendar.AUGUST );
        c.set( Calendar.DAY_OF_MONTH, 27 );
        c.set( Calendar.HOUR_OF_DAY, 14 );
        c.set( Calendar.MINUTE, 21 );
        c.set( Calendar.SECOND, 1 );

        // Print out the relative date

        String relative = RelativeDate.getRelativeDate( c );
        System.out.println( "Relative Date / Time:\t" + relative );    

    }

}

Notes

As documented, this will only display the relative date if the date is not beyond one day. This was a requirement for my specific case, but if you want to display the relative date unconditionally, you can change the computeRelativeDate method and under the “Year”, “Month”, and “Day” blocks, change the code to reflect the same logic as shown in the “Hour”, “Minute”, or “Seconds”. Since this change would be really simple, I’ll leave it to the reader if you are inclined to implement it and need this type of behavior.

Enjoy!

Tags: ,

Leave a Reply