Get AM/PM for a date time in lowercase using only a datetime format

I'm to get a custom DateTime format including the AM/PM designator, but I want the "AM" or "PM" to be lowercase without making the rest of of the characters lowercase.

Is this possible using a single format and without using a regex?

Here's what I've got right now:

item.PostedOn.ToString("dddd, MMMM d, yyyy a\\t h:mmtt")

An example of the output right now would be Saturday, January 31, 2009 at 1:34PM


I would personally format it in two parts: the non-am/pm part, and the am/pm part with ToLower:

string formatted = item.PostedOn.ToString("dddd, MMMM d, yyyy a\\t h:mm") +
                   item.PostedOn.ToString("tt").ToLower();

Another option (which I'll investigate in a sec) is to grab the current DateTimeFormatInfo, create a copy, and set the am/pm designators to the lower case version. Then use that format info for the normal formatting. You'd want to cache the DateTimeFormatInfo, obviously...

EDIT: Despite my comment, I've written the caching bit anyway. It probably won't be faster than the code above (as it involves a lock and a dictionary lookup) but it does make the calling code simpler:

string formatted = item.PostedOn.ToString("dddd, MMMM d, yyyy a\\t h:mmtt",
                                          GetLowerCaseInfo());

Here's a complete program to demonstrate:

using System;
using System.Collections.Generic;
using System.Globalization;

public class Test
{
    static void Main()
    {
        Console.WriteLine(DateTime.Now.ToString("dddd, MMMM d, yyyy a\\t h:mmtt",
                                                GetLowerCaseInfo());
    }

    private static readonly Dictionary<DateTimeFormatInfo,DateTimeFormatInfo> cache =
        new Dictionary<DateTimeFormatInfo,DateTimeFormatInfo>();

    private static object cacheLock = new object();

    public static DateTimeFormatInfo GetLowerCaseInfo()
    {
        DateTimeFormatInfo current = CultureInfo.CurrentCulture.DateTimeFormat;
        lock (cacheLock)
        {
            DateTimeFormatInfo ret;
            if (!cache.TryGetValue(current, out ret))
            {
                ret = (DateTimeFormatInfo) current.Clone();
                ret.AMDesignator = ret.AMDesignator.ToLower();
                ret.PMDesignator = ret.PMDesignator.ToLower();
                cache[current] = ret;
            }
            return ret;
        }
    }
}

You could split the format string into two parts, and then lowercase the AM/PM part, like so:

DateTime now = DateTime.Now;
string nowString = now.ToString("dddd, MMMM d, yyyy a\\t h:mm");
nowString = nowString + now.ToString("tt").ToLower();

However, I think the more elegant solution is to use a DateTimeFormatInfo instance that you construct and replace the AMDesignator and PMDesignator properties with "am" and "pm" respectively:

DateTimeFormatInfo fi = new DateTimeFormatInfo();

fi.AMDesignator = "am";
fi.PMDesignator = "pm";

string nowString = now.ToString("dddd, MMMM d, yyyy a\\t h:mmtt", fi);

You can use the DateTimeFormatInfo instance to customize many other aspects of transforming a DateTime to a string.


EDIT: Jon's example is much better, though I think the extension method is still the way to go so you don't have to repeat the code everywhere. I've removed the replace and substituted Jon's first example in place in the extension method. My apps are typically intranet apps and I don't have to worry about non-US cultures.

Add an extension method to do this for you.

public static class DateTimeExtensions
{
    public static string MyDateFormat( this DateTime dateTime )
    {
       return dateTime.ToString("dddd, MMMM d, yyyy a\\t h:mm") +
              dateTime.ToString("tt").ToLower();
    }
}

...

item.PostedOn.MyDateFormat();

EDIT: Other ideas on how to do this at How to format a DateTime like "Oct. 10, 2008 10:43am CST" in C#.