在我们的C#项目中,我们需要无时间地表示日期。我知道DateTime的存在,但它也包含了一天中的时间。 我想明确指出某些变量和方法参数是基于日期的。因此我无法使用DateTime.Date
属性
此问题的标准方法是什么?当然,我不是第一个遇到这个?为什么C#中没有Date
类?
有没有人有一个很好的实现使用结构,也许在DateTime上有一些扩展方法,可能会实现一些运算符,如==和<,> ?
答案 0 :(得分:37)
请允许我为此经典问题添加更新:
Jon Skeet的Noda Time库现已相当成熟,并且只有一个名为LocalDate
的日期类型。 (在这种情况下,本地意味着某人本地,不一定是运行代码的计算机的本地。)
名为Date
的仅限日期的类型是通过corefxlab项目向.NET Core添加的建议。您可以在System.Time
包中找到它,同时使用TimeOfDay
类型,以及现有类型的几种扩展方法。
我已经研究过这个问题了,所以我也会分享几种原因:
仅限日期和午夜日期值之间存在逻辑差异。
并非每个当地日在每个时区都午夜。示例:巴西的春季夏令时转换将时钟从11:59:59移动到01:00:00。
日期时间始终指的是一天中的特定时间,而仅日期可以指日期的开始,一天的结束或整个当天的范围。
如果没有非常仔细地观看时区,则将时间附加到日期可能会导致更改的日期,因为值会从一个环境传递到另一个环境。这通常发生在JavaScript中(其Date
对象实际上是日期+时间),但也很容易在.NET中发生,或者在数据在JavaScript和.NET之间传递时在序列化中发生。
使用XML或JSON(和其他人)序列化DateTime
将始终包括时间,即使它不重要。这非常令人困惑,特别是考虑到出生日期和周年纪念日等问题,时间无关紧要。
在架构上,DateTime
是DDD value-object,但它在几个方面违反了Single Responsibly Principle:
它被设计为日期+时间类型,但通常仅用作日期(忽略时间)或仅用于时间(忽略日期)。 (TimeSpan
也经常用于时间,但这是另一个主题。)
附加到DateTimeKind
属性的.Kind
值将单个类型拆分为三个,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或其他时区。
答案 1 :(得分:17)
我怀疑没有专门的纯Date
课,因为你已经有DateTime
可以处理它。 Date
会导致重复和混淆。
如果您想要标准方法,请查看DateTime.Date
属性,该属性仅提供DateTime
的日期部分,其时间值设置为午夜12:00:00(00:00:00)
答案 2 :(得分:11)
我已通过电子邮件发送refsrcfeedback@microsoft.com,这是他们的回答
马科斯,这不是一个提出这些问题的好地方。试试http://stackoverflow.com 简短回答是您需要一个模型来表示某个时间点,而DateTime会这样做,这是实践中最有用的方案。人类使用两个概念(日期和时间)来标记时间点的事实是任意的,对于分离是没有用的。
只有在有保证的地方才能脱离,不要仅仅为了盲目做事而做事。可以这样想:你有什么问题可以通过将DateTime分成日期和时间来解决?你现在没有什么问题?提示:如果您查看.NET框架中的DateTime用法:http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references 您将看到大多数是从方法返回的。如果我们没有像DateTime这样的单一概念,则必须使用参数或元组来返回一对日期和时间。
HTH, 基里尔奥森科夫
在我的电子邮件中,我质疑是否因为DateTime使用TimeZoneInfo来获取机器的时间 - 现在是正确的。所以我说这是因为“业务规则”“过于耦合”,他们向我说明了这一点。
答案 3 :(得分:10)
当你需要一个简单的约会而不用担心时间部分,时区,本地和utc等时,我创建了一个简单的Date struct。
答案 4 :(得分:5)
如果您需要运行日期比较,请使用
yourdatetime.Date;
如果您正在显示屏幕,请使用
yourdatetime.ToShortDateString();
答案 5 :(得分:3)
为什么呢?我们只能推测它并没有帮助解决工程问题。一个很好的猜测是DateTime
包含这样一个结构所具有的所有功能。
如果它对您真的很重要,只需将DateTime
包装在您自己的不可变结构中,该结构只显示日期(或查看DateTime.Date
属性)。
答案 6 :(得分:3)
始终有DateTime.Date
属性会切断DateTime
的时间部分。也许你可以在你自己的Date类型中封装或包装DateTime。
对于这个问题,为什么,嗯,我想你必须要问Anders Heljsberg。
答案 7 :(得分:3)
请允许我推测:也许是因为在SQL Server 2008中SQL中没有Date数据类型所以它会很难将它存储在SQL服务器中?它毕竟是微软产品?
答案 8 :(得分:2)
谁知道为什么会那样。 .NET框架中有许多糟糕的设计决策。但是,我认为这是一个非常小的问题。你总是可以忽略时间部分,所以即使某些代码决定让DateTime引用的不仅仅是日期,关心的代码也应该只看日期部分。或者,您可以创建一个仅代表日期的新类型,并使用DateTime中的函数来执行繁重的工作(计算)。
答案 9 :(得分:2)
除了罗伯特的回答,您还有 DateTime.ToShortDateString
方法。此外,如果你真的想要一个Date对象,你总是可以使用Adapter模式并将DateTime对象包装成只暴露你想要的东西(即月,日,年)。
答案 10 :(得分:1)
因为要知道日期,你必须知道系统时间(以刻度表示),其中包括时间 - 那么为什么要丢弃这些信息?
DateTime
如果您根本不关心时间,则会Date
属性。
答案 11 :(得分:1)
是的,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
答案 12 :(得分:1)
int *ptr = func_arg+10;
和 System.DateOnly
类型最近添加到 .NET 6 中,并且在每日构建中可用。
它们包含在 .NET 6 Preview 4 版本中。
见https://github.com/dotnet/runtime/issues/49036
它们位于此处的 .NET 源代码中:
答案 13 :(得分:0)
如果使用Date或Today属性仅从DateTime对象获取日期部分。
DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;
然后,只有时间组件设置为午夜才能获得日期组件。