Login


A Friendly DateTime Formatter

By Jonathan Wood on 3/25/2011
Language: C#
Technology: .NET
Platform: Windows
License: CPOL
Views: 19,312
General Programming » Time & Date » General » A Friendly DateTime Formatter

Screenshot of Demo Project

Download Source Code Download Source Code

Introduction

Many websites that have frequent posts, such as discussion forums, have the ability to format the time a post was made relative to the current time. For example, a post that was recently made might show the posted time as "2 seconds ago" or "3 minutes ago." This format makes it a little easier for users to recognize this as a recent post.

Different websites vary, but some will show the date and time formatted normally when it is not recent.

In this article, I will present code to format a DateTime value. If the value is close to the current date and time, a relative format is used. In addition, the code I will present supports relative times in the future. For example, "In 2 seconds" or "In 3 minutes".

The RelativeString() Method Extension

Listing 1 shows my code to format a DateTime value. I decided to implement this method as an extension method for the DateTime class. If you're not familiar with extension methods, they provide a way to add a method to an existing class without creating a completely new class that inherits from that existing class. The method can then be called as though it was a part of the original class. I'll demonstrate its use later.

The ToRelativeString() method starts by calculating the TimeSpan between the current date and time and the date and time passed to the method. Next, it determines if the given time is in the future. If so, it normalizes the TimeSpan by making sure it is positive. This allows the rest of the code to work equally well with dates in the past or future. It also sets the future flag, which can be tested for special cases. It then performs a test to see if the given time is within a second of the current date and time. If so, it just returns "Now".

After getting a format string for the String.Format() method that is based on whether the date is in the future or the past, the code attempts to create a relative description in seconds, minutes or hours. There's plenty of room for rounding errors here. The code uses Math.Max() to ensure the printed value is at least 1.

If the difference between the date and time values is measured in days, execution falls through to the next lines of code. These lines start by formatting the date. If the difference is one day, either "Yesterday" or "Tomorrow" is used. If the difference is two days, then "2 days" is used. Finally, if the difference is greater than 2 day, the date is formatted to show the month and date. The year is also added if the given date is not in the current year.

For all formats measured in days, the time is appended in a regular time format.

Listing 1: The ToRelativeString() Extension Method

public static class DateTimeExensions
{
    /// <summary>
    /// Returns a string representation of this date/time. If the
    /// value is close to now, a relative description is returned.
    /// </summary>
    public static string ToRelativeString(this DateTime dt)
    {
        TimeSpan span = (DateTime.Now - dt);

        // Normalize time span
        bool future = false;
        if (span.TotalSeconds < 0)
        {
            // In the future
            span = -span;
            future = true;
        }

        // Test for Now
        double totalSeconds = span.TotalSeconds;
        if (totalSeconds < 0.9)
        {
            return "Now";
        }

        // Date/time near current date/time
        string format = (future) ? "In {0} {1}" : "{0} {1} ago";
        if (totalSeconds < 55)
        {
            // Seconds
            int seconds = Math.Max(1, span.Seconds);
            return String.Format(format, seconds,
                (seconds == 1) ? "second" : "seconds");
        }

        if (totalSeconds < (55 * 60))
        {
            // Minutes
            int minutes = Math.Max(1, span.Minutes);
            return String.Format(format, minutes,
                (minutes == 1) ? "minute" : "minutes");
        }
        if (totalSeconds < (24 * 60 * 60))
        {
            // Hours
            int hours = Math.Max(1, span.Hours);
            return String.Format(format, hours,
                (hours == 1) ? "hour" : "hours");
        }

        // Format both date and time
        if (totalSeconds < (48 * 60 * 60))
        {
            // 1 Day
            format = (future) ? "Tomorrow" : "Yesterday";
        }
        else if (totalSeconds < (3 * 24 * 60 * 60))
        {
            // 2 Days
            format = String.Format(format, 2, "days");
        }
        else
        {
            // Absolute date
            if (dt.Year == DateTime.Now.Year)
                format = dt.ToString(@"MMM d");
            else
                format = dt.ToString(@"MMM d, yyyy");
        }

        // Add time
        return String.Format("{0} at {1:h:mmt}", format, dt);
    }
}

Using the Code

Using this extension method is straight forward. Simply declare a DateTime value, and call the new method. This is demonstrated in Listing 2. As you can see, ToRelativeString() behaves like any other DateTime method. It even appears in Intellisense within Visual Studio.

Listing 2: Using the ToRelativeString() Extension Method

DateTime dt = DateTime.Now;
Console.WriteLine(dt.ToRelativeString());
Console.WriteLine(dt.AddSeconds(1).ToRelativeString());
Console.WriteLine(dt.AddMinutes(-1).ToRelativeString());

This code produces the following output.

Now
In 1 second
1 minute ago

Conclusion

That's about it. The attached download includes a copy of the source code and a console application that prints various date and time values to test the code. I hope you find the code to be useful.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.