How To Append 'st', 'nd', 'rd' or 'th' To Day Numbers in Dates

For a new project, my client required to output date strings in a format that included the two letter suffix added to the day number, like in British date ("January 1st 2018" as opposed to "January 1 2018").

The solution makes use of the custom formatting capabilities in the framework whereby it is possible to implement the IFormatProvider and ICustomFormatter interfaces to provide custom string formatting for use with methods that support formatting such as String.Format() or Console.WriteLine().

I decided to extend the existing DateTime format provision in the framework by adding new format options for the DateTime type.

StandardExampleWith suffixExample
dd31ddx31st
D31 January 2008Dx31st January 2008
F31 January 2008 21:30:15Fx31st January 2008 21:30:15
f31 January 2008 21:30fx31st January 2008 21:30
m31 Januarymx31st January
RThu, 31 Jan 2008 21:30:15 GMTRxThu, 31st Jan 2008 21:30:15 GMT
U31 January 2008 21:30:15Ux31st January 2008 21:30:15
--xst

The result code is:

namespace PSC.Extensions
{
    /// <summary>
    /// Custom Formatter for providing suffix text for day numbers 
    /// (e.g. "1st", "22nd", "14th", etc.)
    /// 
    /// Example:
    /// string str = string.Format(new DayNumberFormatInfo(), 
    ///                            "{0:Dx}", DateTime.Now);
    /// </summary>
    public sealed class DayNumberFormatInfo : IFormatProvider, ICustomFormatter
    {
        #region IFormatProvider Members
        /// <summary>
        /// IFormat provider
        /// </summary>
        /// <returns>IFormatProvider</returns>
        /// <param name="formatType">Format type.</param>
        object IFormatProvider.GetFormat(Type formatType)
        {
            if (typeof(ICustomFormatter).Equals(formatType))
                return this;
            else
                return null;
        }
        #endregion
        #region ICustomFormatter Members
        /// <summary>
        /// ICustomFormatter
        /// </summary>
        /// <returns>ICustomFormatter</returns>
        /// <param name="format">Format.</param>
        /// <param name="arg">Argument.</param>
        /// <param name="formatProvider">Format provider.</param>
        string ICustomFormatter.Format(string format, object arg, 
                                       IFormatProvider formatProvider)
        {
            // If this is a DateTime with a supported format string...
            if (!string.IsNullOrEmpty(format) && arg is DateTime)
            {
                DateTime dateTime = (DateTime)arg;
                string formatString = format.Trim();

                if (formatString.Equals("Dx", StringComparison.Ordinal))
                    return string.Format(formatProvider, 
                                        "{1} {0:MMMM} {0:yyyy}", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));

                else if (formatString.Equals("Mx", StringComparison.OrdinalIgnoreCase))
                    return string.Format(formatProvider, 
                                         "{1} {0:MMMM}", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));

                else if (formatString.Equals("fx", StringComparison.Ordinal))
                    return string.Format(formatProvider, 
                                         "{1} {0:MMMM} {0:yyyy} {0:t}", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));

                else if (formatString.Equals("Fx", StringComparison.Ordinal))
                    return string.Format(formatProvider, 
                                         "{1} {0:MMMM} {0:yyyy} {0:T}", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));

                else if (formatString.Equals("ddx", StringComparison.Ordinal))
                    return FormatDayNumberSuffix(dateTime);

                else if (formatString.Equals("x", StringComparison.Ordinal))
                    return GetDayNumberSuffix(dateTime);

                else if (formatString.Equals("Rx", StringComparison.OrdinalIgnoreCase))
                    return string.Format(formatProvider, 
                                         "{0:ddd}, {1} {0:MMM} {0:yyyy} {0:T} GMT", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));

                else if (formatString.Equals("Ux", StringComparison.Ordinal))
                    return string.Format(formatProvider, 
                                         "{1} {0:MMMM} {0:yyyy} {0:T}", 
                                         dateTime, 
                                         FormatDayNumberSuffix(dateTime));
            }

            // If it's not a DateTime or has an unsupported format...
            if (arg is IFormattable)
                return ((IFormattable)arg).ToString(format, formatProvider);
            else
                return arg.ToString();
        }
        #endregion
        #region Helpers
        /// <summary>
        /// Returns day number with two character suffix text for the 
        /// day number component in the given date value
        /// </summary>
        /// <param name="date">Date</param>
        /// <returns>String of day number including suffix (1st, 2nd, 3rd, 4th etc.)</returns>
        private static string FormatDayNumberSuffix(DateTime date)
        {
            if (date == null)
                throw new ArgumentNullException(nameof(date));

            return string.Format(CultureInfo.InvariantCulture, "{0:dd}{1}", 
                                 date, GetDayNumberSuffix(date));
        }

        /// <summary>
        /// Returns just the two character suffix text for the 
        /// day number component in the given date value
        /// </summary>
        /// <param name="date">Date</param>
        /// <returns>String of day number suffix (st, nd, rd or th)</returns>
        private static string GetDayNumberSuffix(DateTime date)
        {
            if (date == null)
                throw new ArgumentNullException(nameof(date));

            int day = date.Day;

            switch (day)
            {
                case 1:
                case 21:
                case 31:
                    return "st";

                case 2:
                case 22:
                    return "nd";

                case 3:
                case 23:
                    return "rd";

                default:
                    return "th";
            }
        }
        #endregion
    }
}

Advertsing

125X125_06

Planet Xamarin

Planet Xamarin

TagCloud

MonthList