ISO 8601/RFC 3339 Compatible Dates

There are not standard format specifiers for date that takes a date and converts it to an ISO 8601 or RFC 3339 compatible date. Here are two extension methods that does just that.

public static string ToISO8601(this DateTime date)
{
    return date.ToString("yyyy-MM-dd");
}

public static string ToRFC3339(this DateTime date)
{
    return date.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ");
}

Relative Time Description

Showing an absolute date on a webpage, e.g. January 22, 2009 13:01, is of course a very normal way to do it, apart from various different date formats, it is easily read and consumed. However if the date is shown in a context where the absolute date is of little importance, but it is more important to know if it was a long time ago, just now or maybe just in a couple of minutes, a relative and more descriptive method is better.

For example the date mentioned above might be described as “8 months ago”. Just by giving it a quick glance you get an idea about when this event occurred.

For this here are a couple of extension methods that generate this descriptive text based on a given time:

public static string ToRelativeTime(this DateTime from)
{
    DateTime now = DateTime.Now;
    return from.ToRelativeTime(now);
}

public static string ToRelativeTime(this DateTime from, bool usePreAndSuffix)
{
    DateTime now = DateTime.Now;
    return from.ToRelativeTime(now, usePreAndSuffix);
}

public static string ToRelativeTime(this DateTime from, DateTime to)
{
    return from.ToRelativeTime(to, true);
}

public static string ToRelativeTime(this DateTime from, DateTime to, bool usePreAndSuffix)
{
    string prefix = usePreAndSuffix && (from > to) ? "in " : string.Empty;
    string suffix = !usePreAndSuffix || (from > to) ? string.Empty : " ago";

    // is more than 1 year?
    if (from > to)
    {
        DateTime d = from;
        from = to;
        to = d;
    }

    int years = to.Year - from.Year;
    if ((to.Month < from.Month) || ((to.Month == from.Month) && (to.Day < from.Day)))
    {
        years--;
    }

    if (years > 1)
    {
        return string.Format("{0}{1} years{2}", prefix, years, suffix);
    }
    else if (years == 1)
    {
        return string.Format("{0}1 year{1}", prefix, suffix);
    }

    // less than 1 year, is more than 1 month?
    int months = to.Month - from.Month;
    if (months < 0)
    {
        months += 12;
    }

    if ((to.Day < from.Day) || ((to.Day == from.Day) && (to.TimeOfDay < from.TimeOfDay)))
    {
        months--;
    }

    if (months > 1)
    {
        return string.Format("{0}{1} months{2}", prefix, months, suffix);
    }
    else if (months == 1)
    {
        return string.Format("{0}1 month{1}", prefix, suffix);
    }

    // less than 1 month, is more than 1 day/week?
    TimeSpan diff = to - from;
    if (diff.Days > 7)
    {
        return string.Format("{0}{1} weeks{2}", prefix, diff.Days / 7, suffix);
    }
    else if (diff.Days > 1)
    {
        return string.Format("{0}{1} days{2}", prefix, diff.Days, suffix);
    }
    else if (diff.Days == 1)
    {
        return string.Format("{0}1 day{1}", prefix, suffix);
    }

    // less than 1 day, is more than 1 hour?
    if (diff.Hours > 1)
    {
        return string.Format("{0}{1} hours{2}", prefix, diff.Hours, suffix);
    }
    else if (diff.Hours == 1)
    {
        return string.Format("{0}1 hour{1}", prefix, suffix);
    }

    // less than 1 hour, is more than 1 minute?
    if (diff.Minutes > 1)
    {
        return string.Format("{0}{1} minutes{2}", prefix, diff.Minutes, suffix);
    }
    else if (diff.Minutes == 1)
    {
        return string.Format("{0}1 minute{1}", prefix, suffix);
    }

    // less than 1 minute
    if (diff.Seconds == 1)
    {
        return string.Format("{0}1 second{1}", prefix, suffix);
    }
    else if (diff.Seconds < 1)
    {

        return string.Format("{0}less than 1 second{1}", prefix, suffix);
    }

    return string.Format("{0}{1} seconds{2}", prefix, diff.Seconds, suffix);
}

This can of course be optimized for localization etc.


Follow

Get every new post delivered to your Inbox.