BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Algorithms and Data Structures
.NET 2.0+

Displaying Relative Time

When developing software that works with time stamped information, it is common to show the date and time at which an item was created or last updated. A user-friendly alternative is to display a relative time, such as "Two Hours Ago".

Relative Time

It is common to see web sites that accept user content display the relative time of creation of a comment or post. A message such as "Twelve Hours Ago", instead of the actual date and time of posting, allows the user to quickly determine the age of an item without having to perform a mental calculation or consider the time zone of the creator. Although less common, this approach can also be used in other software for past and future dates.

In this article we will create a reusable class to generate the text for such time offsets with times expressed in units of seconds, minutes, hours, days, weeks or years. We will utilise the code from the "Convert a Number into Words" article to convert the offset values to words. The class will generate relative times that are rounded to an appropriate unit such as "One Hour Ago", "Three Weeks in the Future" and "Just Now".

Creating the Project

To begin, create a new console application. Add the NumberToWordsConverter class described in the earlier article and a new class named "RelativeTimeGenerator". NB: The code for both classes can be downloaded along with a Windows Forms-base demonstration using the link at the top of this page.

The GetRelativeTime Method

The RelativeTimeGenerator class includes one public method. This method accepts a single parameter containing the date and time to convert into a relative time string. To declare the method signature, add the following code:

public string GetRelativeTime(DateTime time)
{
}

The date and time supplied to the GetRelativeTime method could be for any time zone. To ensure that the calculation of the difference between the two times is correct, the time from the provided argument is converted to Co-ordinated Universal Time (UTC). The current time is retrieved from the static UtcNow property. The difference between the two values can then be correctly calculated by subtraction.

For much of the time during the conversion we are not interested if the difference between the two dates and times is positive or negative. We also do not need the millisecond accuracy provided by the TimeSpan object. We can therefore round the value and use the Math.Abs method with the TimeSpan's TotalSeconds value to obtain a positive version. This will make the conversion simpler.

TimeSpan difference = DateTime.UtcNow - time.ToUniversalTime();
TimeSpan positive = TimeSpan.FromSeconds((int)Math.Abs(difference.TotalSeconds));

There is a single special case for the method. If the rounded TimeSpan is zero, the time is neither in the past or the future. In such cases the method will return "Just Now". To add the check, add the following to the end of the method:

if (positive.TotalSeconds == 0) return "Just Now";

To complete the GetRelativeTime method requires three steps. Firstly, the relative duration or time offset is calculated using a private method. This may be in the past or the future. The second call obtains a suffix based upon the original difference, which may be positive or negative. The suffix will either be "Ago" or "in the Future". Finally, the two strings are combined using string.Format and the result is returned.

string relativeTime = GetOffset(positive);
string suffix = GetSuffix(difference);

return string.Format("{0} {1}", relativeTime, suffix);

Getting the Time Offset

Obtaining the time offset is achieved using an if-else-if ladder. Each if statement checks if the TimeSpan is greater than or equal to a cut-off value. If it is, the GetOffSetWords method is called to obtain the result using a specified unit of time. The ladder starts with the largest duration and finishes with the smallest. This allows the use of one predicate per condition only as the upper bound of a range is implicitly set by the previous condition.

private string GetOffset(TimeSpan positive)
{
    if (positive.Days >= 365)
        return GetOffsetWords(positive.Days / 365, "Year");
    else if (positive.Days >= 7)
        return GetOffsetWords(positive.Days / 7, "Week");
    else if (positive.Days >= 1)
        return GetOffsetWords(positive.Days, "Day");
    else if (positive.Hours >= 1)
        return GetOffsetWords(positive.Hours, "Hour");
    else if (positive.Minutes >= 1)
        return GetOffsetWords(positive.Minutes, "Minute");
    else
        return GetOffsetWords(positive.Seconds, "Second");
}

The GetOffSetWords method builds the duration string from the offset value and unit. The offset value is converted into a string using the NumberToWordsConverter class and the unit is appended. If the number is not one, an "s" is added to the unit to avoid results such as "One Seconds" or "Two Minute".

private string GetOffsetWords(int offset, string unit)
{
    string offsetWords = new NumberToWordsConverter().NumberToWords(offset);
    if (offset != 1) unit += "s";
        return string.Format("{0} {1}", offsetWords, unit);
}

Getting the Suffix

The final private method obtains a suffix for the time offset. This method simply determines whether the difference between the current and provided times is positive or negative. If positive, the suffix is "Ago". If negative, the suffix is "in the Future".

private string GetSuffix(TimeSpan difference)
{
    return difference.TotalSeconds > 0 ? "Ago" : "in the Future";
}

You can now test the class by calling it from the Main method of the console application. Try using different past and future dates to see the results. You can also download the demo application from the link at the top of the page. This is a Windows Forms program that allows you to specify a date and time and see the relative time produced.

14 February 2010