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
.
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.