Smart Relative date formatting in C# using extension methods.

The C# DateTime object has some nice .ToString() overloads for formatting dates but sometimes we would like a friendly, relative format like what you see in many Android or iPhone apps.

The idea is to only include year, month, and day when they are not the current one. Examples are as follows...

Just now        
55 seconds ago
1 min ago
15 mins ago
1 hour ago
13 hours ago
Sunday, 25th at 12:53 PM              -  different day, same month
Tuesday, May 2nd at 12:53 PM     -  difference month, same year
May 2nd, 2016 at 12:53 PM          - different year


I like this format because it's flexible enough to give you a nice relative format that scales well with dates far in the past. To implement, I used an extension method. Since I am using ordinals for the days, I used another extension that I found in a Stack Overflow post. The Humanizr library accomplishes ordinals very well, but in this case I found it to be overkill.

All code below. Now put it into action in your User Interfaces!



  public static class DateTimeExtensions
  {
        public static String ToRelativeFormat(this DateTime dateTime)
        {
            var now = DateTime.Now;
            var timeDifference = now - dateTime;
            var daySuffix = GetDaySuffix(dateTime);
            
            if (now.Year == dateTime.Year)
            {
                // exclude year

                if (now.Month == dateTime.Month)
                {
                    // exclude month

                    if (now.Day == dateTime.Day)
                    {
                        // exclude day

                        if (timeDifference.Hours < 24)
                        {
                            // display as hours

                            if (timeDifference.Hours < 1)
                            {
                                // display as minutes

                                if (timeDifference.Minutes < 1)
                                {
                                    // display as seconds
                                    if (timeDifference.Seconds <= 1)
                                        return "Just now";
                                   
                                    return timeDifference.Seconds + " seconds ago";
                                }
                                if (timeDifference.Minutes == 1)
                                {
                                    return timeDifference.Minutes + " minute ago";
                                }

                                // display as minutes
                                return timeDifference.Minutes + " minutes ago";
                            }

                            // display as hours
                            if (timeDifference.Hours == 1)
                                return "1 hour ago";
                            
                            return timeDifference.Hours + " hours ago";
                        }
                    }

                    // display with year and month excluded
                    return dateTime.ToString($"dddd, d'{daySuffix}' 'at' h:mm tt");
                }

                // display with year excluded
                return dateTime.ToString($"dddd, MMMM d'{daySuffix}' 'at' h:mm tt");
            }

            // display with name of day excluded
            return dateTime.ToString($"MMMM d'{daySuffix}', yyyy 'at' h:mm tt");
        }


        #region HELPERS

       
        private static String GetDaySuffix(DateTime date)
        {
            return date.Day.ToOrdinal();
        }

        #endregion

    }

    public static class NumericExtensions
    {
        // Taken from: http://stackoverflow.com/questions/2050805/getting-day-suffix-when-using-datetime-tostring 
        public static String ToOrdinal(this Int32 value)
        {
            var ordinal = "";

            switch (value)
            {
                case 1:
                case 21:
                case 31:
                    ordinal = "st";
                    break;
                case 2:
                case 22:
                    ordinal = "nd";
                    break;
                case 3:
                case 23:
                    ordinal = "rd";
                    break;
                default:
                    ordinal = "th";
                    break;
            }

            return ordinal;
        }
    }



Comments

Popular posts from this blog

ASP.NET Identity Remember Me

IIS Express Client Certificates

ASP.NET MVC - How to enable/disable CaC/Client Certificate authentication per area or route.