在C#中,表示时间对象的最佳实践是什么?

24

我的数据库表中有一个“时间”列。日期不重要,我们只需要一天中的时间。在C#中,最好用哪种类型来表示它?我原本想用DateTime,但我不喜欢有日期这个概念。

我有一个在数据库表中的时间列,日期无关紧要,只需要记录一天中的时间。在C#中,代表这个时间的最佳数据类型是什么?我本打算使用DateTime,但我不想涉及日期的概念。

这个回答解决了你的问题吗?如何在.NET中表示仅时间值? - Grisha
8个回答

36

更新的答案:

从.NET 6开始,您可以使用内置的TimeOnly数据类型。其背景与以下相同:

原始答案:

虽然其他答案大多是正确的,一个TimeSpan是唯一可用的内置类型,但重要的是要意识到“经过的时间测量”和“一天中的时间”之间存在明显的差异。

最明显的区别在于一天中的时间必须小于24小时。而TimeSpan对象可以覆盖更长的时间。另一个区别是TimeSpan类型可以是负数,这表示时间向后移动。作为一天中的时间,负值没有意义。最后,一天中的时间包括任何可能适用于所在时区的夏令时概念。因此,您不能将其视为“自午夜以来经过的时间”。如果这是美国的夏令时转换日,则自午夜以来已经过去了3个小时。如果这是美国的冬令时转换日,则自午夜以来实际上已经过去了5个小时。由于夏令时在全世界各地都不同,午夜甚至可能不存在或存在两次。这种情况发生在巴西和其他地方。
如果您将TimeSpan用作一天中的时间,则需要注意这些问题。 .NET没有内置类型用于表示一天中的时间,因此这是一种可以接受的妥协,即使它违反了自身的设计。
即使是.NET Framework本身也做出了这个妥协。例如:
- DateTime类有一个TimeOfDay属性,返回一个TimeSpan。 - 如果在SQL Server中使用time类型,则通过.NET SQL Client返回时将会是一个TimeSpanMSDN参考文档TimeSpan类型有以下说明:
"TimeSpan结构还可以用于表示一天中的时间,但前提是该时间与特定日期无关。否则,应改用DateTime或DateTimeOffset结构。"
这基本上是另一种方式来表达我在第三点中涉及的夏令时问题。

然而,如果您不想在设计中妥协,并且希望实时的时间类型,请看看Noda Time库。

  • LocalTime类型表示一天中的时间。 这是问题的直接答案。
  • Duration类型表示经过的时间。
  • Period类型表示日历上的位置移动 - 这是TimeSpan无法做到的。例如,“3年5个月”将是一个Period值。
  • Offset类型类似于Duration,但用作时区相对于UTC的偏移量。它的范围仅限于此目的。

虽然有人可能会说TimeSpan更加通用,因为它可以处理所有这些内容,但事实是它会让您陷入麻烦。通过分离类型,您可以获得安全性和一致性。

或者,您可以考虑使用微软的 CoreFX Lab 中可用的 System.Time 包。该包含有一个名为Time的仅时间类型和一个名为Date的仅日期类型的实现。您将需要使用dotnet-corefxlab MyGet feed导入此包。 更新:在 .NET 6+ 中内置了 TimeOnly 数据类型


你只是在试图让它变得更加复杂。大部分内容与问题根本无关。 - Guffa
6
问题是“在C#中最好用什么类型来表示一天中的时间?”,答案是使用Noda Time库中的“LocalTime”类型。其余部分是对此做出解释的内容。 - Matt Johnson-Pint
你在那个句子中给出了自己对“它”的解释,但这不是问题所在。OP从数据库列中获取了一个特定类型的值,并希望在C#中得到相应的类型。没有比这更复杂的了。 - Guffa
4
@Guffa - 显然我们意见不一。我坚持我的立场,同时也尊重你的观点。祝您愉快。 - Matt Johnson-Pint

20

您可以使用 TimeSpan 结构在 .NET 中表示时间。


6
但是TimeSpan不是时间范围吗?显然,结束时间未知,事件可能在下午2点开始并持续无限期。仍然使用TimeSpan是否仍然是一个好主意? - Carlo
1
但是一天中的时间实际上只是自午夜以来经过的时间。因此,实际上是相同的概念。 - dommer
我来试试看。谢谢。 - Carlo
3
@dommer - 不完全正确。请看我的回答。 - Matt Johnson-Pint
TimeSpan 表示时间间隔。最佳方法应该是使用 DateTime 的时间组件。 - Sercan

5

2
你可以尝试这样做:
TimeSpan ts = DateTime.Now.TimeOfDay

在您应用 DateTime 对象的 time 属性时,只需使用它即可。


1
使用TimeSpan表示从午夜到某个时间的时间跨度。

这是一个常见的误解,或者说是一种妥协。请查看我的答案以获取更多细节。 - Matt Johnson-Pint
@MattJohnson:不,这既不是一个误解也不是一个妥协。即使您在夏令时方面是正确的,那也与问题无关,因为数据库中的数据类型存在完全相同的问题。使用Timespan来保存时间值并不会引入您提到的问题。 - Guffa
嗨。实际上,不是这样的。假设他谈论的是SQL Server time类型,它代表一天中的时间,而不是经过的时间。我也在这个答案中提到了这一点。在现实生活中,这可能会在用户输入验证中出现问题。一个字符串可能可以解析为有效的TimeSpan,但无法适应数据库中的time列。如果用户输入24:00作为一天中的时间,这将失败,除非您采取额外的步骤进行范围验证。 - Matt Johnson-Pint
@MattJohnson:不好意思,你错了。这是真的。如果你愿意把它弄复杂,那也没关系,但事实并非如此。一个值如何被解析为TimeSpan值与问题无关。 - Guffa

1

我会使用TimeSpan来表示这个,其中TimeSpan是自午夜以来的时间跨度。这与框架中DateTime的TimeOfDay属性相对应。


并不完全正确。请看我的答案。 - Matt Johnson-Pint
@MattJohnson 我不明白你的回答中有什么否定了我所写的内容。是的,TimeSpan 可以 表示其他时间,但它也完全可以用来表示从0到24小时的时间。 - Reed Copsey
嗨,里德。实际上,一天的时间是 [0,24),但无论如何,我的答案中的重点在第三个要点。你不能仅仅说它是“自午夜以来经过的时间”,因为有些值并不是这样。MSDN参考文献稍微有所不同,我会更新我的答案以反映这一点。 - Matt Johnson-Pint
@MattJohnson 我并不是在与你争论这个问题,但是 TimeSpan 允许你表示那些值为真的情况,这就是我想说的。 - Reed Copsey
当然。这就是为什么我称之为“妥协”的原因。并不是想引发争论,只是这是一个常见的错误源,所以我尽可能准确。 - Matt Johnson-Pint

1

考虑到这个问题是我刚刚进行的谷歌搜索中的顶部结果,可能值得说一下,在.NET 6即将发布(目前计划于2021年11月9日发布)之后,正确答案可能是新的TimeOnly结构体


0
我们实际上自己编写了一个时间类。我们遇到的问题是TimeSpan没有时区偏移的知识,而我们仍然需要它。因此,我们创建了一个TimeSpanOffset类。有点类似于DateTimeOffset类。如果时区不重要,那么我肯定会坚持使用TimeSpan。

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