views:

4355

answers:

3

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

A: 

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 http://stackoverflow.com/questions/448634/how-to-format-a-datetime-like-oct-10-2008-1043am-cst-in-c.

tvanfosson
Maaaan that's exactly what I was hoping to avoid :(
Daniel Schaffer
It also doesn't work for cultures with a AM/PM designators other than AM/PM.
Jon Skeet
What Jon said. And has the potential to bork in a locale where AM is considered to be part of the day's name (unlikely, but still bad from a correctness POV). Better to use a separate format for the am/pm part, ToLower(), and append it.
Mark
@agreed -- i've fixed this up to leave just the extension method which I think is still the way to go.
tvanfosson
+8  A: 

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;
        }
    }
}
Jon Skeet
I like this one better than anything posted in that other question... thanks!
Daniel Schaffer
The posted code, or the idea of a new DateTimeFormatInfo? If it's the latter, I'll come up with some code for you. If it's the former, I won't bother :) DateTimeFormatInfo.Clone() is probably the way forward though.
Jon Skeet
The former - the latter, while supremely awesome, is waaaay overkill in this case.
Daniel Schaffer
If you only need it the once (or just a few times) I completely agree. If this were all over your code, then *maybe* the format info version would be worthwhile. Fun to write anyway :)
Jon Skeet
Yeah, I've only got it in two places, and this is just for my personal site.
Daniel Schaffer
I'd always go for the former. The latter is *so* specific and unobvious that it would require a method name that is nearly as long as the code itself. GetLowerCaseInfo just doesn't describe what it does, I'm afraid.
Mark
@Mark: I agree that GetLowerCaseInfo isn't descriptive enough for production code. I still think there are places where it could be useful, but the first snippet of code is definitely better for most cases.
Jon Skeet
+2  A: 

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

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 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.

casperOne
I prefer this approach because the "now" variable isn't as available for me - I've got a complicated DataBind expression that would look ugly and this handles it all in one function call.
umbyersw