Convert to Relative Time

 

Displaying date and time on a user interface is useful, but it is not always the most user-friendly way to present information. Imagine a social media website, which displays recent comments, but displays the date and time when a photo or comment was published. It requires more than just a quick glance to work out when it happened.

However, if we were to display the relative time, such as “4 minutes ago”, “1 hour ago”, it becomes much easier to read, and provides the user with context to the content on the page.

In a similar scenario, we may wish to display the relative time for an event that is happening in the future, such as when a video conference call is starting. We may want the user to see the relative time to when the event is commencing. For example “The video conference call is starting in 10 minutes from now“.

The code below is an extension method that can convert a DateTime value to a relative time string. It takes into account whether the date & time is in the past (“ago”) or future (“from now”).


public static class DateTimeExtensions
{
	/// <summary>
	/// Returns the difference between the start date/time and current date/time as a relative term.
	/// Such as 30 seconds ago, 1 hour ago, etc. The minimum measure is seconds. The maximum is years.
	/// The value is rounded to the nearest whole number.
	/// </summary>
	/// <param name="startDateTime">The date that the event occurred.</param>
	/// <param name="currentDateTime">This should be today's date/time, but can be overridden.</param>
	/// <returns>Values such as 30 seconds ago, 1 hour ago, etc.</returns>
	public static string ToRelativeTime(this DateTime startDateTime, DateTime? currentDateTime = null)
	{
		var timeSpan = (currentDateTime ?? DateTime.Now) - startDateTime;
		var verbTense = timeSpan.Ticks > 0 ? "ago" : "from now";
		var duration = timeSpan.Duration();

		string unit;
		double value;

		if (duration <= TimeSpan.FromSeconds(60))
		{
			unit = "second";
			value = duration.TotalSeconds;
		}
		else if (duration <= TimeSpan.FromMinutes(60))
		{
			unit = "minute";
			value = duration.TotalMinutes;
		}
		else if (duration <= TimeSpan.FromHours(24))
		{
			unit = "hour";
			value = duration.TotalHours;
		}
		else if (duration <= TimeSpan.FromDays(30))
		{
			unit = "day";
			value = duration.TotalDays;
		}
		else if (duration <= TimeSpan.FromDays(365))
		{
			unit = "month";
			value = duration.TotalDays / 30;
		}
		else
		{
			unit = "year";
			value = duration.TotalDays / 365;
		}

		var rndVal = Math.Abs(Math.Round(value, 0));
		var plural = rndVal > 1 || rndVal < 0.001 ? "s" : string.Empty;
		return $"{rndVal} {unit}{plural} {verbTense}";
	}
}

 

The method will return the relative time in the following units:

UnitRangeExample String
SecondUp to 60 seconds29 seconds from now
MinuteEqual to or less than 60 minutes2 minutes ago
HourEqual to or less than 24 hours4 hours ago
DayEqual to or less than 30 days3 days from now
MonthEqual to or less than 365 days1 month ago
YearAnything greater than 365 days2 years ago

In addition, the code will also handle singular and plural units. For example “1 minute ago” and “3 minutes from now”.

 

Example Usage

From the example below, it can be seen that startDate uses the current date & time, but subtracts 3 days. Therefore, the result should be “3 days ago”.


DateTime startDate = DateTime.Now.AddDays(-3);
string relativeTime = startDate.ToRelativeTime();

// Output should be: "3 days ago"	
Console.WriteLine(relativeTime);

In another example, the date & time will be set in the future. As can be seen, this will switch from “ago” to “from now”.


DateTime startDate = DateTime.Now.AddMinutes(10);
string relativeTime = startDate.ToRelativeTime();

// Output should be: "10 minutes from now"
Console.WriteLine(relativeTime);

Please Note: That DateTime.MinValue and DateTimeMax value will also be displayed. Such as “2022 years ago” and “7984 years from now”. It may sound obvious, but depending on the implementation, DateTime.MinValue maybe a scenario which the variable has not been set and therefore should not be shown to the user.

 

Post Categories