在使用DateTime.ToString()时如何获取日期后缀

102

使用DateTime.ToString()格式化日期时是否可以包含日期的后缀呢?

例如,我希望按照以下格式打印日期 - 2009年7月27日星期一。但是,使用DateTime.ToString()找到的最接近的例子是:Monday 27 July 2009。

我能否使用DateTime.ToString()实现这一点,还是必须要使用自己的代码来完成?


8
有人提到了NodaTime吗? - grenade
3
了解,"[日期]序数后缀"是这些称呼的标准。 "Day" 通常指周一至周日。 - Richard Szalay
@grenade 我非常希望这能成为答案。我已经花了近一个小时来按照问题中提到的方式格式化NodaTime,但据我所知它不起作用:https://nodatime.org/2.3.x/userguide/localdate-patterns(即使在2020年)。看起来momentjs有这个功能,因为它们建立了自己的本地化模型:https://momentjs.com/docs/#/i18n/ - Drew Delano
此外,我们所有的文本本地化资源(日期和月份名称)都来自.NET框架本身。这有一些显著的限制,并使Noda Time更加依赖CultureInfo,这并不理想。CLDR包含更多信息,应该允许功能,如序数日号(“1st”,“2nd”,“3rd”)和更广泛的支持的日历/文化组合(例如希伯来日历月份的英文名称)。 - Drew Delano
20个回答

274

使用 switch 的另一个选项:

string GetDaySuffix(int 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";
    }
}

10
简单易读,最重要的是适用于所有情况。 - Lynn Crumbling
24
如果你想知道突然的活动是怎么回事:你的答案被《The Daily WTF》链接作为“如何正确做事”的示例。 - tobias_k
@tobias_k 谢谢 - 我想知道我的微不足道的声望为什么会这么快地翻倍了! - Lazlow
1
对于那些想要完整日期格式的人:返回date.ToString("dd MMMM yyyy").Insert(2, GetDaySuffix(date.Day))。例如,2020年1月12日。 - 1fuyouinfinite
我需要这个,很高兴找到了它,但很遗憾这种格式在.NET6中仍未更新。特别是因为我需要与本地化一起使用。 - DVS

71

作为参考,我总是使用/参考 [SteveX字符串格式化]1,似乎没有任何可用变量中包含“th”,但你可以轻松地构建一个带有这个字符串的字符串。

string.Format("{0:dddd dd}{1} {0:MMMM yyyy}", DateTime.Now, (?));

然后你需要为1提供"st",为2提供"nd",为3提供"rd",并且对于所有其他情况都需要提供"th",可以使用"? :" 语句进行内联。

var now = DateTime.Now;
(now.Day % 10 == 1 && now.Day % 100 != 11) ? "st"
: (now.Day % 10 == 2 && now.Day % 100 != 12) ? "nd"
: (now.Day % 10 == 3 && now.Day % 100 != 13) ? "rd"
: "th"

23
否则你会得到类似于“21th”这样的结果,因此需要进一步扩展以涵盖其他情况。 - Kasaku
1
就此而言,微软的字符串格式选项的官方文档可以在此处找到:http://msdn.microsoft.com/en-us/library/fbxft59x%28v=vs.80%29.aspx。 - Bobson
9
如果在同一表达式中多次使用DateTime.Now,当代码在午夜左右执行时,这些值可能会不同。 - AlexD
1
我修复了代码,现在它可以处理超过100的数字,所以您将看到112th而不是112nd - Jez
2
@Jez,我认为日期中没有超过100的天数,请查看Microsoft文档https://learn.microsoft.com/en-us/dotnet/api/system.datetime.day 之前的编辑答案必须是正确的:https://stackoverflow.com/revisions/2050854/4并撤销您的更改。 - ahmed hamdy
@Bobson 那个链接已经失效了。 - ryanwebjackson

46

使用几个扩展方法:

namespace System
{
    public static class IntegerExtensions
    {
        public static string ToOccurrenceSuffix(this int integer)
        {
            switch (integer % 100)
            {
                case 11:
                case 12:
                case 13:
                    return "th";
            }
            switch (integer % 10)
            {
                case 1:
                    return "st";
                case 2:
                    return "nd";
                case 3:
                    return "rd";
                default:
                    return "th";
            }
        }
    }   

    public static class DateTimeExtensions
    {
        public static string ToString(this DateTime dateTime, string format, bool useExtendedSpecifiers)
        {
            return useExtendedSpecifiers 
                ? dateTime.ToString(format)
                    .Replace("nn", dateTime.Day.ToOccurrenceSuffix().ToLower())
                    .Replace("NN", dateTime.Day.ToOccurrenceSuffix().ToUpper())
                : dateTime.ToString(format);
        } 
    }
}

使用方法:

return DateTime.Now.ToString("dddd, dnn MMMM yyyy", useExtendedSpecifiers: true);
// Friday, 7th March 2014

注意:整数扩展方法可用于任何数字,而不仅限于1到31。例如:

return 332211.ToOccurrenceSuffix();
// th

1
谢谢伙计。非常有用。我已经在我的项目中实现了。 :) - Chandan Kumar
6
你的代码有点...忘记检查 useExtendedSpecifiers 布尔值 :p - Nyerguds
2
最优雅的解决方案。这正是扩展方法的设计初衷。我已将其添加到我的不断增长的扩展方法库中,谢谢! - Radderz
1
ToOrdinal(),也许可以吗? :) - Mladen B.

13

另一个选择是使用模数运算符

public string CreateDateSuffix(DateTime date)
{
    // Get day...
    var day = date.Day;

    // Get day modulo...
    var dayModulo = day%10;

    // Convert day to string...
    var suffix = day.ToString(CultureInfo.InvariantCulture);

    // Combine day with correct suffix...
    suffix += (day == 11 || day == 12 || day == 13) ? "th" :
        (dayModulo == 1) ? "st" :
        (dayModulo == 2) ? "nd" :
        (dayModulo == 3) ? "rd" :
        "th";

    // Return result...
    return suffix;
}
你可以通过将一个DateTime对象作为参数传递来调用上述方法,例如:
// Get date suffix for 'October 8th, 2019':
var suffix = CreateDateSuffix(new DateTime(2019, 10, 8));

有关 DateTime 构造函数的更多信息,请参阅 Microsoft 文档页面


2
@Greg,这很奇怪,因为在我的情况下 var suffix = CreateDateSuffix(new DateTime(2013, 10, 8)); 返回的是“第8天”? - Anthony Walsh
1
如果在字符串'eight'后面添加'th'就是错误的,但在这种情况下,由于使用了数字8,所以是正确的。 - gcochard
以上方法需要一个 DateTime 对象,我无法看出它如何使用除数字值以外的任何东西进行实例化 - 在这种情况下,'8' 代表月份中的某一天。 - Anthony Walsh
在这种情况下,它是正确的。如果你用数字的文本表示来替换数字值,那就是错误的。我想这取决于谁来进行替换,知道这一点并将“8t”替换为“eight”,或更正确地说,将“8”替换为“eigh”。 - gcochard

8

参考@Lazlow的回答,以下是完整可重用的扩展方法及其使用示例:

internal static string HumanisedDate(this DateTime date)
{
    string ordinal;

    switch (date.Day)
    {
        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 string.Format("{0:dddd dd}{1} {0:MMMM yyyy}", date, ordinal);
} 

要使用此功能,您只需在DateTime对象上调用它即可;
var myDate = DateTime.Now();
var myDateString = myDate.HumanisedFormat()

这将为您提供:

2016年6月17日,星期五


8

这里是包括第11、12和13版的扩展版本:

DateTime dt = DateTime.Now;
string d2d = dt.ToString("dd").Substring(1);
string daySuffix =
    (dt.Day == 11 || dt.Day == 12 || dt.Day == 13) ? "th"
    : (d2d == "1") ? "st"
    : (d2d == "2") ? "nd"
    : (d2d == "3") ? "rd"
    : "th";

“11th”,“12th”和“13th”呢? - sjngm
我可能错过了这个。我已经在上面修复了它。 - Piotr Lewandowski
如果需要处理比 ("dd") 更大的数字,请使用 string.PadLeft() - maxp
@PiotrLewandowski - 你不会是来自曼彻斯特吧?因为我认识一个人来自那里。太巧了,顺便点个赞 +1。 - Piotr Kula

6

更新

NuGet包:
https://www.nuget.org/packages/DateTimeToStringWithSuffix

示例:
https://dotnetfiddle.net/zXQX7y

支持:
.NET Core 1.0及以上版本
.NET Framework 4.5及以上版本


这是一个扩展方法(因为每个人都喜欢扩展方法),以Lazlow的答案为基础(选择Lazlow的方法因其易于阅读)。

DateTime的常规ToString()方法相同,唯一的区别在于如果格式包含ddd,则会自动添加后缀。

/// <summary>
/// Return a DateTime string with suffix e.g. "st", "nd", "rd", "th"
/// So a format "dd-MMM-yyyy" could return "16th-Jan-2014"
/// </summary>
public static string ToStringWithSuffix(this DateTime dateTime, string format, string suffixPlaceHolder = "$") {
    if(format.LastIndexOf("d", StringComparison.Ordinal) == -1 || format.Count(x => x == 'd') > 2) {
        return dateTime.ToString(format);
    }

    string suffix;
    switch(dateTime.Day) {
        case 1:
        case 21:
        case 31:
            suffix = "st";
            break;
        case 2:
        case 22:
            suffix = "nd";
            break;
        case 3:
        case 23:
            suffix = "rd";
            break;
        default:
            suffix = "th";
            break;
    }

    var formatWithSuffix = format.Insert(format.LastIndexOf("d", StringComparison.InvariantCultureIgnoreCase) + 1, suffixPlaceHolder);
    var date = dateTime.ToString(formatWithSuffix);

    return date.Replace(suffixPlaceHolder, suffix);
}

1
很惊讶这个问题没有得到更多的赞,我更喜欢它是一个扩展的事实。这使得它更容易使用,也更易读。 - 0Neji
那个 NuGet 包不支持包含“ddd”或“dddd”的格式,即星期几的名称。如果您想将日期显示为“2021年11月4日星期四”,则需要解决这个限制 - 例如 Console.WriteLine(DateTime.UtcNow.ToString("dddd")+" "+DateTime.UtcNow.ToStringWithSuffix("d MMMM yyyy")); - John

4

C# 8引入了更简单的解决方案,使用switch表达式:

var daySuffix = dateTime.Day switch {
                    1 or 21 or 31 => "st",
                    2 or 22 => "nd",
                    3 or 23 => "rd",
                    _ => "th",
                };

2

我认为这是一个不错的解决方案,可以涵盖像111th等数字:

private string daySuffix(int day)
{
    if (day > 0)
    {
        if (day % 10 == 1 && day % 100 != 11)
            return "st";
        else if (day % 10 == 2 && day % 100 != 12)
            return "nd";
        else if (day % 10 == 3 && day % 100 != 13)
            return "rd";
        else
            return "th";
    }
    else
        return string.Empty;
}

尽管这是一种更通用的方法,适用于任何数字,而不仅仅是月份天数(我认为)。 - Duncan

2

对于那些乐于使用外部依赖(在这种情况下是极好的Humanizr.net),这很简单。

dateVar.Day.Ordinalize(); \\ 根据dateVar的值,可以得到1st、4th等结果。


很棒的建议。我希望这能够内置到.NET中。 - ryanwebjackson

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