仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?

Posted

技术标签:

【中文标题】仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?【英文标题】:A type for Date only in C# - why is there no Date type?仅在 C# 中的 Date 类型 - 为什么没有 Date 类型? 【发布时间】:2011-07-15 22:03:04 【问题描述】:

在我们的 C# 项目中,我们需要表示没有时间的日期。我知道 DateTime 的存在,但是,它也包含一天中的时间。 我想明确指出某些变量和方法参数是基于日期的。因此我不能使用DateTime.Date 属性

解决这个问题的标准方法是什么?我肯定不是第一个遇到这种情况的人吗?为什么C#中没有Date类?

有没有人有一个很好的实现,使用结构体,也许在 DateTime 上使用一些扩展方法,也许实现一些运算符,例如 == 和 ?

【问题讨论】:

虽然我理解想要明确、清晰的语义,但 DateTime 会产生哪些具体问题? 1 我需要记住在方法开始时删除小时数。 2 它不能很好地传达它仅在日期上起作用。这很重要,例如当从 Db 存储和加载时,窄类型就足够了。编程是人而不是计算机的交流 我只想说缺少日期类是一件大事,使用 DateTime 根本没有好处。一旦您将“日期”存储为日期时间,您就会成为语言环境/时区日光节约问题的人质。扔掉时间部分可以在时钟改变的一天将所有日期发送回(!)。不同时区的用户在尝试转换日期时间时会看到不同的日期。日期时间可以很好地表示精确的时刻(从某个时间点开始或其他什么),但它们非常不适合表示抽象日期。 后来类似的问题***.com/questions/7167710/…,Jon Skeet 说应该有一个日期。 仅日期数据类型对应于 DateTime,就像整数数据类型对应小数一样。那些认为我们不需要日期的人,因为你可以扔掉时间部分类似于说我们不需要整数,因为我们可以扔掉小数部分。我们的世界有一个不包括时间的日期概念。 3 月 5 日不是 3 月 5 日 00:00:00。 【参考方案1】:

请允许我为这个经典问题添加更新:

DateOnly(和 TimeOnly)类型已添加到 .NET 6,从 Preview 4 开始。请参阅 my other answer here。

Jon Skeet 的 Noda Time 库现在已经相当成熟,并且有一个名为 LocalDate 的仅日期类型。 (在这种情况下,本地仅表示某人的本地,不一定是运行代码的计算机的本地。)

我已经对这个问题进行了深入研究,因此我还将分享这些类型的必要性的几个原因:

    仅日期值和午夜日期值之间存在逻辑差异。

并非每个当地时间在每个时区都有午夜。示例:巴西的春季夏令时转换将时钟从 11:59:59 移动到 01:00:00。

date-time 总是指一天中的特定时间,而 date-only 可能指一天的开始、一天的结束或整个范围天。

    如果不仔细观察时区,将时间附加到日期可能会导致日期更改,因为值会从一个环境传递到另一个环境。这通常发生在 javascript 中(其Date 对象实际上是一个日期+时间),但也很容易发生在 .NET 中,或者在 JavaScript 和 .NET 之间传递数据时的序列化中。

    使用 XML 或 JSON(及其他)序列化 DateTime始终包含时间,即使它并不重要。这非常令人困惑,尤其是考虑到诸如出生日期和纪念日之类的时间无关紧要的事情。

    在架构上,DateTime 是一个DDD value-object,但它在几个方面违反了Single Responsibly Principle:

它被设计为日期+时间类型,但通常用作仅日期(忽略时间)或仅时间(忽略日期)。 (TimeSpan 也经常用于时间,但这是另一个主题。)

附加到.Kind 属性的DateTimeKind 值将单个类型分成三个,Unspecified 类型确实是结构的初衷,应该这样使用。 Utc 种类将值与 UTC 对齐,Local 种类将值与环境的本地时区对齐。

使用单独的 kind 标志的问题在于,每次您使用 DateTime 时,您应该检查 .Kind 以决定采取什么行为。框架方法都这样做,但其他人经常忘记。这确实违反了 SRP,因为该类型现在有两个不同的更改原因(值和种类)。

这两个导致 API 使用可以编译,但通常是荒谬的,或者有由副作用引起的奇怪的边缘情况。考虑:

     // nonsensical, caused by mixing types
     DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??

     // strange edge cases, caused by impact of Kind
     var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
     var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
     var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
     var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
     Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!

总之,虽然DateTime 可以仅用于日期,但只有在每个使用它的地方都非常小心地忽略时间时才应该这样做,并且也是非常小心,不要尝试转换为 UTC 或其他时区。

【讨论】:

如果System.Time.Date 最终会出现在 .NET 框架中就好了:/【参考方案2】:

我怀疑没有专用的纯Date 类,因为您已经拥有可以处理它的DateTime。拥有Date 会导致重复和混乱。

如果您想要标准方法,请查看 DateTime.Date 属性,该属性仅给出 DateTime 的日期部分,时间值设置为午夜 12:00:00 (00:00:00)。

【讨论】:

专用 Date 类的一大优点是它不受时区和夏令时的复杂性影响。 @DimitriC。我不同意-您可以将 DateTime 与 UTC 一起使用,并且您不会遇到所解释的问题,加上 DateTime,即使您只想要日期,您仍然可以进行涉及时间的数学运算(即,如果我减去 20 x 2 小时,请给我日期从今天开始)。 必须考虑 UTC 以及与时区有关的任何事情只是浪费精力,因为可以通过单独的 Date 类轻松避免。而且我没有看到 Date 和 DateTime 之间有任何混淆。 同意 C# 确实应该有一个 Date 类。时区转换不仅是潜在错误的源头,而且在处理基于工作日而不是基于时间的事情时,这简直是痛苦的。 @RobertMacLean:考虑“如果我从今天减去 20 x 2 小时的日期”是没有意义的。在这个例子中,你认为今天是“今天午夜”吗? “今天中午”?等等。这个问题没有很好地表述。如果您的意思是“今天午夜”,那么您已经在使用 DateTime,而不是 Date。为了减去小时数,您需要一个小时,而不是一天。【参考方案3】:

我已发送电子邮件至 refsrcfeedback@microsoft.com,这就是他们的答复

Marcos,这里不是问这些问题的好地方。试试http://***.com 简短的回答是您需要一个模型来表示一个时间点,而 DateTime 可以做到这一点,这是实践中最有用的场景。人类使用两个概念(日期和时间)来标记时间点的事实是任意的,并且没有用分开。

只在需要的地方解耦,不要为了一味的做事而做事。这样想:你有什么问题可以通过将 DateTime 拆分为 Date 和 Time 来解决?你会遇到什么现在没有的问题?提示:如果您查看 .NET 框架中的 DateTime 用法:http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references 您会看到大多数都是从方法返回的。如果我们没有像 DateTime 这样的单一概念,则必须使用 out 参数或元组来返回一对日期和时间。

HTH, 基里尔·奥森科夫

在我的电子邮件中,我曾质疑这是否是因为 DateTime 使用 TimeZoneInfo 来获取机器的时间 - 在 Now 中是恰当的。所以我会说这是因为“业务规则”“过于耦合”,他们向我证实了这一点。

【讨论】:

这篇文章确实提供了关于没有内置日期类的设计决策背后的想法的见解。你发给他们的问题是什么?我并不是因为上面列出的@TheMathemagician 的原因而暗示我同意这个决定。 也许 MS 可以全力以赴并实现 SpaceTime 类!嘿,根据爱因斯坦的说法,空间和时间是紧密耦合的,所以我们也不应该区分它们,对吧? (!!!!!!!!!!!!)我对 C# 有点陌生,但我不得不说,这是一个来自 VB.NET 的雷区,简单来说,dateToday()、@987654326 @ 等等。没有DateTime 前缀垃圾,不要胡闹。 (而且这些分号和这种区分大小写的做法确实令人讨厌!现在就开枪打死我!) 并且他们自己的 SQL Server 具有 Date 类型,并且结果必须是 Date 类型 - 如果它是 Date 类型的结果,则预期为没有时间的字符串。例如,Delphi 也有 Date 作为 DateTime,但对于 Date 和 DateTime,typeinfo 不同。 Kirill Osenkov 正在回答“为什么不使用单独的日期和时间类 DateTime 类?”的问题。 实际问是“为什么不有单独的日期和时间类?”。我知道对于 date-time 概念的许多用例,日期和时间应该耦合到一个类中。然而,如果不是更多,也可能至少与 date 概念的有效用例一样多。当然,time 概念也有很多有效的用例。 鉴于 SQL 服务器具有 DateTime、Date 和 Time 类型,这似乎是一个奇怪的答案。显然,我们需要将它们作为许多现实世界应用程序的独立实体。【参考方案4】:

我创建了一个简单的Date struct,用于当您需要一个简单的日期而不用担心时间部分、时区、本地与 UTC 等时。

https://github.com/claycephus/csharp-date

【讨论】:

【参考方案5】:

System.DateOnlySystem.TimeOnly 类型最近被添加到 .NET 6 中,并且在每日构建中可用。

它们包含在 .NET 6 Preview 4 版本中。

见https://github.com/dotnet/runtime/issues/49036

它们在此处的 .NET 源代码中:

https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs

https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs

I've blogged about them here.

【讨论】:

是时候了,如果可以的话【参考方案6】:

如果您需要运行日期比较,请使用

yourdatetime.Date;

如果您要在屏幕上显示,请使用

yourdatetime.ToShortDateString();

【讨论】:

.Date 部分是我要找的。​​span> 【参考方案7】:

请允许我推测:可能是因为在 SQL Server 2008 之前 SQL 中没有 Date 数据类型,所以很难将其存储在 SQL Server 中?它毕竟是微软的产品吗?

【讨论】:

数据库日期时间不同于 C# 日期时间。 db datetime 没有时区,因此它们实际上并不指特定的时刻。但 C# 确实知道时刻是并存储自 UTC 纪元以来的滴答声。 讨论是关于专用日期,而不是关于日期时间部分,所以我不明白你要表达的意思吗? 这没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。 @Barranka - 问题包含“为什么 C# 中没有 Date 类?”【参考方案8】:

谁知道为什么会这样。 .NET 框架中有很多糟糕的设计决策。但是,我认为这是一个很小的问题。您总是可以忽略时间部分,因此即使某些代码确实决定让 DateTime 引用的不仅仅是日期,关心的代码也应该只查看日期部分。或者,您可以创建一个仅表示日期的新类型,并使用 DateTime 中的函数来完成繁重的工作(计算)。

【讨论】:

我真的不认为这是一个错误的决定,无论您是否只想使用日期。我不会对你投反对票,但这是我的看法。 我觉得我措辞不好。就其本身而言,我实际上并没有太大的问题,尽管从抽象/优雅的角度来看,我可以看到拥有两种或三种类型会更合适。我的真正意思是,.NET 框架中有很多东西可能会让你摸不着头脑,不值得为此太沮丧,特别是考虑到与一些令人震惊的设计决策(通用约束)。 +1 因为这是真的...这是 .NET 的唯一问题(或最大的问题):-) :-) 他们需要多少个 SQL Server 版本才能添加 DATE 和时间类型?而且它们更有用(至少出于完整性原因) 我还应该补充一点,我认为“一切都从 -100 点开始”是制作一个糟糕的框架的好方法,这可能是陷入垃圾的事情之一. 我刚刚被这个问题所困扰,因为代码的一部分忽略了使用 .Date 属性,因此没有正确比较。我绝对认为需要一个不存储任何时间的 Date 类型,以避免这种类型的错误【参考方案9】:

为什么?我们只能推测,它对解决工程问题没有多大帮助。一个很好的猜测是DateTime 包含了这样一个结构所具有的所有功能。

如果它对您来说真的很重要,只需将 DateTime 包装在您自己的不可变结构中,该结构只公开日期(或查看 DateTime.Date 属性)。

【讨论】:

【参考方案10】:

除了罗伯特的回答之外,您还有 DateTime.ToShortDateString 方法。此外,如果您真的想要一个 Date 对象,您可以始终使用 Adapter 模式并包装 DateTime 对象,只公开您想要的内容(即月、日、年)。

【讨论】:

【参考方案11】:

总是有DateTime.Date 属性截断DateTime 的时间部分。也许您可以将 DateTime 封装或包装在您自己的 Date 类型中。

对于这个问题,好吧,我想你得问问 Anders Heljsberg。

【讨论】:

【参考方案12】:

是的,System.DateTime 也是密封的。我见过一些人通过创建一个自定义类来玩这个游戏,只是为了获取前面帖子中提到的时间的字符串值,比如:

class CustomDate

    public DateTime Date  get; set; 
    public bool IsTimeOnly  get; private set; 

    public CustomDate(bool isTimeOnly)
    
        this.IsTimeOnly = isTimeOnly;
    

    public string GetValue()
    
        if (IsTimeOnly)
        
            return Date.ToShortTimeString();
        

        else
        
            return Date.ToString();
        
    

这可能是不必要的,因为您可以轻松地从普通的旧 DateTime 类型中提取 GetShortTimeString 而无需新类

【讨论】:

【参考方案13】:

因为要知道日期,您必须知道系统时间(以刻度为单位),其中包括时间 - 那么为什么要丢弃这些信息?

DateTime 有一个 Date 属性,如果你根本不关心时间的话。

【讨论】:

今天是2020-12-07;是否愿意提供该日期的准确系统时间?【参考方案14】:

如果您使用 Date 或 Today 属性仅从 DateTime 对象中获取日期部分。

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

那么你将只得到日期组件,时间组件设置为午夜。

【讨论】:

这绝对不是我想要的 @Carlo V. Dango:我不同意。我认为这正是您想要的。 @Carlo V. Dango:您有什么特别想做而这些属性不允许您完成的事情? 这很简单:日期内存占用可能只有日期时间内存占用的一半(32 位而不是 64 位)。您可以确定您的愚蠢同事没有 .AddHours(1) 更改您的日期,而是从“仅日期”的 POV 中“保持不变”。如果(对于错误)将 DateTime 设置为 DateTimeKind.Local 并且将时间标准化为 UTC,则 Date 可能会更改(通过使用 XmlSerialization 和糟糕的 JSON 往返发生在我身上)... 够了吗?

以上是关于仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效

C#程序,将数据从dataset导出到excel表格里的时间类型为啥显示成“#”号呢

json中的String类型的日期为啥自动转换成date类型

从 C# 保存到 SQL 中的 DATE 类型列 - Linq to SQL

hibernate oracle 中查询Date类型,忽略了时分秒 为啥?如何解决?

为啥 C# 无法从非泛型静态方法的签名推断泛型类型参数类型?