使用自定义的IFormatProvider获取DateTime.ToString()中月份的首字母

5

在DateTime格式字符串中,有没有一种方式可以仅包含月份的第一个字母?

new DateTime(2015, 1, 12).ToString("M-dd-yy") // "1-12-15",  but I want "J-02-15"
new DateTime(2000, 11, 7).ToString("M-dd-yy") // "11-07-00", but I want "N-17-00"

如果不行,是否有一种方法可以将新的自定义格式添加到系统范围内的IFormatProvider中来处理它?
“对于那些问你为什么要那样做的人的注意事项:这是为了供应商的图表控件而设计的,该控件仅接受格式字符串。我们正在尝试使这些日期尽可能短,并且它们面向国际用户,因此月份的数字不起作用。我无法传递新的IFormatProvider,因此我必须以某种方式修改现有的IFormatProvider(希望不会破坏它)。 "

9
一月、七月、六月等呢? - Bruno
1
你可以使用ToString()的覆盖来使用自定义格式:https://msdn.microsoft.com/zh-cn/library/8tfzyc64(v=vs.110).aspx,但是你需要定义自己的格式提供程序。 - Irwene
2
哎呀,从来没想到“JASON”也在月份里。 - DLeh
@Alexander,请注意“你为什么要那样做?”的人留言。 - snumpy
1
如果必须使用格式字符串,那么最好的选择是使用“MMM”来获取月份的前三个字母。这样不够简短吗? - christophano
显示剩余8条评论
3个回答

4
让我们深入探讨一下DateTime.ToString()的实现。当没有传递IFormatProvider对象时,它将使用System.Globalization.DateTimeFormatInfo.CurrentInfo作为DateTimeFormatInfo对象来获取有关如何构建字符串表示的信息。CurrentInfo又从Thread.CurrentThread.CurrentCulture中获取信息。
因此,您希望以某种方式设置当前线程文化,使其具有单个字符月份名称(或如下例子中的缩写),然后使用常规格式字符串。实现此目的的一种方法是以下内容:
using System;
using System.Globalization;
using System.Threading;

namespace DTFTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var dt = DateTime.Now;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("EN-US", true);
            var ci = Thread.CurrentThread.CurrentCulture;
            var monthNames = new[] { "J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D", string.Empty };
            ci.DateTimeFormat.AbbreviatedMonthNames = monthNames;
            Console.WriteLine(dt.ToString("MMM-dd-yyyy"));
        }
    }
}

3

这里有一个使用自定义 FormatProvider 的解决方案:

    public static class SingleLetterMonthNameFormatter
    {
        private static IFormatProvider _formatProvider;
        public static IFormatProvider FormatProvider
        {
            get
            {
                if (_formatProvider == null)
                {
                    var dtfi = new DateTimeFormatInfo();
                    dtfi.AbbreviatedMonthNames = dtfi.MonthNames.Select(x => x.FirstOrDefault().ToString()).ToArray();
                    _formatProvider = dtfi;
                }
                return _formatProvider;
            }
        }
    }

你可以像这样使用它:
var monthName = DateTime.Now.ToString("MMM", SingleLetterMonthNameFormatter.FormatProvider) //First letter of month name

如果你想要传递一个FormatProvider给你想要的方法,但是没有任何方法可以做到这一点,那就有一种不好的方法可以实现,但需要小心使用!
    var currentCulture = Thread.CurrentThread.CurrentCulture.Clone() as CultureInfo;
    currentCulture.DateTimeFormat = SingleLetterMonthNameFormatter.FormatProvider as DateTimeFormatInfo;
    Thread.CurrentThread.CurrentCulture = currentCulture;
    Console.WriteLine(DateTime.Now.ToString("MMM"));

2
不错的解决方案,我没有意识到当前文化实例是公共的。你可能应该添加一些信息,说明这可能会破坏什么(任何其他调用该线程上的DateTime.ToString的调用,实际上想要使用缩写的三个字母的月份名称),并且还要注意,如果供应商在另一个线程上调用ToString,则此方法将无效。 - Matt
你和n0rd的答案非常接近。不幸的是,供应商正在使用另一个线程来调用ToString()。但是你的答案有助于证明,在没有访问供应商的单独线程的情况下,实际上无法修改ToString()的行为。 - snumpy

-1

(new DateTime(2015,1,12).ToString("MM")[0])+(new DateTime(2015,1,12).ToString("-dd-yy"))

但是要注意首字母相同的月份(如一月和七月),这可能会因其他文化差异而有所改善或恶化。


1
M 标识符将月份转换为数字 - ryanyuyu
2
这是针对供应商图表控件的,它只接受格式字符串 - 也就是说,除了指定格式字符串以外,我认为你无法做任何其他事情来正确回答这个问题 - 我不确定它是否可回答。 - James Thorpe
我错过了图表控件部分。如果是这样的话,我认为这不会起作用。微软没有提供此选项,因为J = 一月,七月,A = 四月,八月等。 - dmeglio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接