如何在C#中按示例格式化日期?

11

C#在将DateTime对象格式化为字符串表示时提供了很多灵活性,但是要使用这种灵活性,必须知道所有格式字符串。

如果您想以“Fri,June 24”的形式显示日期,则可以按如下方式操作:

DateTime someDate = DateTime.Now;
Console.Write(someDate.ToString("ddd, MMMM dd"));

虽然这很有效,但对于更复杂的格式来说,尤其是对于第一次使用它的开发者而言,会比较困难。

我想要通过以下代码实现与上面代码相同的结果:

DateTime someDate = DateTime.Now;
Console.WriteLine(someDate.ToString("Wed, June 12"));

作为字符串指定的日期可以是任意的。基本上,必须通过某种方式首先解析日期格式。我知道这种方法有限制(例如本地化),但对于简单的情况来说,它更容易理解。除了自己实现之外,是否有其他方法可以做到这一点?我愿意使用第三方库。


4
我认为后者并不更容易阅读。 - Jouke van der Maas
10
那样如何计算 dd/MM/yy 和 MM/dd/yyyy 之间的差异? - kͩeͣmͮpͥ ͩ
3
抱歉,但对我来说 someDate.ToString("ddd, MMMM dd") 已经足够清楚了。 - Bolu
3
我觉得你的方法相当不直观 - 只需要学习格式化字符串,这并不难掌握。 - BrokenGlass
2
@Slavo,难道不应该把学习这个框架所做的假设和模糊调用的默认值的精力花在学习格式字符串或如何查找它们上吗? - StuperUser
显示剩余6条评论
5个回答

14

通常我只需找出正确的格式字符串(在您的示例中为"ddd,MMMM dd"),然后将其作为应用程序中的某个常量存储...

public static class DateTimeFormats
{
    public string DayOfWeekMonthDay = "ddd, MMMM dd";
}

那么您只需要引用它即可。

DateTime.Now.ToString(DateTimeFormats.DayOfWeekMonthDay)

这样做并不能节省你第一次查找的时间,对吧? - Slavo
3
@Slavo,不是说你必须使用这个附加组件,但它可以节省你编写、配置和维护代码的时间。 - StuperUser
虽然这是一个好主意,对于任何被问题中的建议所诱惑的人来说都是一个好建议,但这实际上是回答问题的答案吗? - Jodrell
1
@Jodrell:这个问题的正确答案是“你无法这样做”。所以我认为@Jon在这里的回答非常好。 - Yuck
1
这是一个很好的方法,可以提高代码的可读性和维护性,非常符合DRY原则,也有助于保持一致性。 - BrokenGlass
显示剩余3条评论

6
我能看出这对于新手开发者来说可能更容易处理,但是在构建“从日期字符串生成日期格式”的功能时,有一些问题需要解决。我可以想象出这样的情况,这个函数可能会失败,并说出以下内容:
  • 你说“五月”; 这是否意味着将六月的日期格式化为“Jun”或“June”?
  • 你说“6月12日”; 这是否意味着将6月5日格式化为“June 5”或“June 05”?
现在,你可以向新开发者解释他们需要小心使用模糊不清的示例日期格式模板。但是这需要他们已经了解它可能存在的歧义。他们需要像格式化函数一样思考。
这就是为什么日期格式字符串被定义为它们所定义的方式 - 尽可能具体地描述开发人员想要/需要生成的期望输出格式。它们尽可能地避免了这种歧义。
如果开发者最终需要“像格式化函数一样思考”才能得到他们想要的结果,那么学习现有的定义可能是值得花时间的。

2
这个问题的严格回答是:除非第三方已经做过,否则没有其他方法可以实现它,你需要自己实现格式字符串解析器。我和大多数回答者一样认为,这样做所需的工作完全不成比例,与仅记住已提供的DateTime格式(或参考其文档)相比,后者更为简单。但是,如果您确实进行了这样的努力,您将希望实现一个 ICustomFormatter 和一个 IFormatProvider,当请求时提供它。请参见上面链接的ICustomFormatter文档中的示例,但是您的任务将涉及提供一个 Format(string format,object arg,IFormatProvider formatProvider)方法,该方法以您感兴趣的格式的字符串为参数,并将其用于将传递给arg的DateTime转换为与该模式匹配的字符串。
一旦完成这个步骤,并且您拥有一个IFormatProvider,它的GetFormat()方法返回您的自定义格式化程序,那么您的示例代码将如下所示:
DateTime someDate = DateTime.Now;
Console.WriteLine(someDate.ToString("Wed, June 12", new CustomDateFormatter()));

猜猜看 - 出于好奇,我尝试实现自己的格式化程序和格式提供程序,但它没有起作用。反射 DateTime.ToString() 的代码揭示了一个问题:你应该总是从 GetFormat 返回一个 DateTimeFormatInfo 对象,否则你的 ICustomFormatter 实现将永远不会被调用。这是 .NET 的一个 bug 还是故意的呢? - Slavo

1

我认为这不可行,你怎么知道12是天还是年。我建议,现有的歧义实际上比学习相对简单的自定义格式字符串更复杂。

你有没有使用过VBA和日期?


1
我认为如果你只指定一个日期来使用,它可能会起作用。在你的库中指定一个特殊的日期,例如2011年9月12日,不能以任何格式重叠。 - Cyril Gandon
@Scorpi - 为了消除所有的歧义,您需要使用第13天和第32年 - 例如2032年9月13日。12/11/11仍然存在歧义...是月份、日期还是年份先? - Gavin Coates
1
@Gavin Coates我不太确定如何礼貌地表达这个意思,所以我直接说吧。九月是第九个月,而不是第十一个月。 :) 但是,您的观点仍然成立:12/09/11仍然存在哪部分是哪部分的歧义! - Dan J
你需要尽可能多种格式的日期,以避免歧义。不幸的是,这会导致冲突:要指示哪个是日、月和两位数年份,你需要一个大于等于13的日和一个大于31的年份。但是,要指示在10以下的日和月是否使用一位数还是两位数,你需要一个小于10的日和月... - Jack V.

0

在搜索了一段时间后,我成功找到了一个Ruby库,它可以做我想要的完全相同的事情。由于我无法找到.NET解决方案,所以我实现了自己的实用程序,受到了Ruby的启发。

支持4种常见格式的Spike可以在github上看到 - Stamp.Net。我将其发布为答案,以便对此类实用程序感兴趣的人可以从那里获取并直接在其项目中使用。

请注意,当前状态仅证明了一个观点,并且是一个实验。没有进行单元测试。欢迎您的评论。

一些实现说明:

.NET框架为用户提供了一种非常好的方式来为类型实现自定义格式化程序和格式化提供程序。@djacobson在回答问题时提到了这一点。不幸的是,DateTime类的实现方式使您无法使用ICustomFormatter。当您使用自定义格式提供程序调用ToString()时,该格式提供程序必须返回一个DateTimeFormatInfo对象,而DateTime类不会调用格式提供程序返回的自定义格式化程序。

这个问题可以通过在DateTime类上实现自定义扩展方法来解决,该方法直接调用自定义格式化程序。库的用户不需要关心这些细节。

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