17

编辑 2009 年 11 月 4 日

好的,所以自从我第一次发布这个问题以来已经有一段时间了。在我看来,许多最初的响应者并没有真正理解我在说什么——一个常见的反应是“你所说的没有任何意义”的一些变化——所以我做了一些方便图表来真正说明我的观点。

当我们谈到数字时,我们通常指的是小学生学习的点,称为数字线:

数线

现在,当我们学习算术时,我们的大脑学会了对这个概念进行非常有趣的转换。评估表达式1 + 0.5,例如,如果我们简单地应用我们的“数轴思维”,将需要我们以某种方式理解这一点:

在数轴上添加两个点

很难真正说明这一点,因为很难想到:“添加”两点。这就是许多响应者为添加日期的想法而苦苦挣扎的地方(或者干脆认为这是荒谬的),因为他们将日期视为点。

然而,这个表达对我们来说1 + 0.5 确实有意义,因为当我们想到它时,我们真的在想象这个:

添加一个数字(点)和一个幅度(向量)

也就是说,数字(或)1,加上向量0.5,得到1.5。

或者,我们可能会这样想象:

添加两个向量

向量1,加上向量0.5,得到向量1.5。

换句话说,在处理数字时,我们可以互换地处理点和向量。但是日期呢?毕竟,日期基本上是数字。如果您不相信我,请将此行与上面的数字行进行比较:

时间线

注意到时间线和数轴之间的对应关系了吗?这是我的观点:如果我们用numbers执行上述转换,我们也应该能够用 date 来做。0001-Jan-02 00:00:00 + 0001-Jan-01 12:00:00因此,正如许多响应者指出的那样,应用“时间线思维”,这种表达方式并没有多大意义:

在时间线上添加两个点

但是,如果我们在头脑中进行与每次加减数字时相同的概念转换,我们可以轻松地“重新考虑”上述内容:

添加时间点和时间向量

很明显,aDateTime和 a之间TimeSpan的差异与点和向量之间存在的差异相同。我认为导致很多人对我的建议做出负面反应的原因是,以这种方式将日期视为数量级是非常不自然的。但我不认同没有明显参考点可用作零的论点。有一个明显的参考点,我会给你一个提示:大约 2010 年前。

不要误会我的意思:我并不是在质疑在a和 a的概念之间划出概念鸿沟的有用性。真的,我的问题一直应该是(正如 ChrisW 间接建议的那样),为什么我们在处理常规数字类型时要互换处理数字和向量?(或者:为什么我们只有一种类型,而不是和?)有很大的不同,但直到初中或高中的某个时候,当我们开始几何时,我们才真正考虑过它。然后它被视为这个新的数学概念,而实际上,自从我们学会用手指数数来添加数字以来,我们就一直在使用它。DateTimeTimeSpanintintintspan

最后,最好的答案来自 Strilanc,他指出使用DateTimeandTimeSpan仿射空间的一种实现,它具有不需要参考点作为原点的方便属性。所以谢谢,斯特兰克。然而,我将接受的答案交给 ChrisW,因为他是第一个提出向量和点概念的人,这确实触及了问题的症结所在。


原始问题(为后代)

我当然不是所有行业的编程专家,但我知道 PHP 和 .NETTimeSpan除了一个DateTime类(或 .NET 中的结构)之外还有一个类,我猜在各种其他语言和框架中都是这种情况以及(尽管我主要参考 .NET 结构来编写此内容)。这似乎是一个奇怪的问题,但不是TimeSpan多余的吗?

如果您认为答案很明显(“ADateTime是一个绝对时间点,而 aTimeSpan是一个时间范围——就这么简单!”),请考虑一下:整数可以概念化为绝对值(点在数轴上)或值之间的距离——对于这些不同的概念化,我们不需要两种单独的数据类型。我仍然可以毫不含糊地写出 5 + 6 的意思。

只要有一个一致的零点引用,在我看来,就没有理由需要一个TimeSpan对象来对对象执行算术运算DateTime,或者获取它们之间的距离。

我错过了什么?为什么不能TimeSpan简单地将结构的独特方法和属性折叠成DateTime

(免责声明:这不像我对这个或任何东西充满热情;我一直很好地使用DateTimeTimeSpan对象,因为它们一直都是预期的。我只是在问一个问题。)

编辑:好的,过度简化的例子来说明我的观点:

考虑等式 10 - 5 = 5。可以将其解读为“从 10(值)开始,将 5 向左移动(跨度),最终到 5(值)”。

假设,为了简单起见,我们将 1900 年 1 月 1 日设为零点,并且TimeSpan仅以天为单位定义对象。

那么 10 - 5 = 5 可以理解DateTime为 1900 年 1 月 11 日 - 1900 年 1 月 6 日 = 1900 年 1 月 6 日。这很好,因为根据我们的定义,1 月 11 日只是“10”,而 1 月 6 日是“5”。我们将 10 视为value,前 5 视为span,最后 5 再次视为value的事实仅仅是为了我们自己的概念利益。我的观点是这样的:唯一的区别在于你如何看待这个数字,而不是它实际上是什么。这就是为什么我们没有单独的结构,比如整数值和整数跨度——一个普通的旧整数覆盖了我们所有的基础。

我说得有道理吗?

4

9 回答 9

12

(以数学家的身份说话)这是因为“日期”上的算术运算不是封闭的或定义明确的,因此需要额外的结构。

例如,2000 年 1 月 1 日 - 1999 年 12 月 1 日 = ... ? 我们知道它们之间有 31 天,但如果将其解释为日期,那么答案是 Epoch(即零)+ 31 天。这不再是一个有效的“日期”。

同样,整数上的所有算术运算都没有明确定义(1 / 2 在整数中没有答案.. 整数数学在这里返回零,但 0 * 2 = 0,而不是您期望的 1)。这就需要我们称之为分数的额外结构。

于 2009-09-19T17:13:01.857 回答
12

考虑一下:整数可以概念化为绝对值(数轴上的点)或值之间的距离

根据您的逻辑,不是 TimeSpan 是不必要的:而是 DateTime 是不必要的,并且可以替换为 TimeSpan (从零开始的持续时间)。

另外,整数有一个明显的零,而日期却没有明显的零。但是,如果您想用“距零/原点的距离/跨度”替换“在数轴上的位置”,则必须有一个明显的零。


编辑:

点(平面上的位置)与矢量不同。

它们看起来很相似...

  • 一个向量(到原点的距离)可以表示一个点
  • 一个点(相对于原点)可以表示一个向量

...但是,如果原点发生变化,表示给定点所需的向量值将发生变化。

添加两个(相对)向量总是有意义的;但是,添加两个点是没有意义的,除非将这些点转换为向量然后添加向量。

两个向量的和不受原点变化的影响,但如果通过将它们转换为向量并相加将它们相加,则两点的和会受到原点变化的影响(因为改变原点会影响原点的变化)这些向量的值)。

[在上面的论点中,用 DateTime 代替 'point',用 TimeSpan 代替 'vector'。]

我认为绝对值和相对值之间存在真正的区别。我不知道为什么这种差异在算术中不明显,即为什么“数字”似乎可以互换使用来表示绝对值和相对值。

于 2009-09-19T17:27:30.613 回答
5

仅仅因为你可以定义一个操作并不意味着你应该。例如,除以零未定义的原因之一是因为定义它需要牺牲一些非常有用的算术属性(例如,关联性等)。

时间跨度和日期之间的区别归结为加法。添加两个时间跨度是有意义的,但添加两个日期是没有意义的,除非您有一个任意的参考日期。通过不允许添加日期,您可以抽象出该任意参考日期。我不知道 .Net 中的日期“0”是什么,而且我从来不需要知道。这不是很好吗?

添加两个日期几乎总是一个错误(说真的,试着想想这在命理之外有意义的地方)。通过引入时间跨度(创建仿射空间),您可以消除一整类错误。

于 2009-09-19T21:11:33.047 回答
4

一个原因是,拆分类型可以防止出现一类你认为你有一个相对时间但实际上有一个绝对时间的错误,反之亦然。例如,如果两种类型是分开的,则添加两个绝对时间可以标记为编译器错误。

此外,当成员数量较少时,IntelliSense(和新手发现)效果更好——通过在两种类型之间拆分方法,使用每种方法变得更容易。

于 2009-09-19T17:12:07.263 回答
2

反过来问:在这方面削弱类型系统有什么好处?

这完全是成本与收益的问题,并且DateTime通过禁止此类操作来减少由于不合逻辑的日期/时间计算而导致的错误的巨大好处。DateTime存在的原因与最初存在严格的类型检查系统的原因非常相似:使代码中的语义错误产生编译时消息。通知程序员其代码中的错误。

相反,拥有DateTime: zilch 是有代价的。

现在考虑放弃DateTime。我们会得到什么?

直接回答你的问题:“不是TimeSpan多余的吗?” 绝对不是,它减少了错误。对我来说,它肯定有。

于 2009-09-19T18:26:24.277 回答
2

从概念上考虑它。如果我告诉你我要在 7 天后举行派对,那句话中的“7 天”就是一个日期。我可以说我的派对是 7 天吗?当然不是,因为 7 天不是一个日期。面向对象编程的关键思想之一是将系统中的此类概念表示为类型。的确,我们可以将一切都表示为一个整数(事实上,很多人都有并且已经这样做了),但是在面向对象编程中,我们有项目类型及其行为和属性的概念,从这个意义上说,它使感觉有一个表达这一点的对象。

于 2009-09-19T20:29:57.077 回答
0

我认为你可以提出相反的论点DateTime是多余的,我们应该只有TimeSpan:)

说真的,所有日期都只是时间跨度。它们都相对于某个起点。从技术上讲,基督教历法中没有“零年”(因为你不能真正拥有“我们主的零年”),但如果我们将 12:00 AM BC 0001 年 1 月 1 日指定为“零点” ,那么之后(或之前)的每个日期都可以被认为是相对于该日期的。因此,2009 年 9 月 19 日上午 12:00 的时间跨度为 734033 天。

因此,在数学DateTimeTimeSpan是多余的。但是当我们编写代码时,我们试图传达的不仅仅是抽象的数学结构。任何给定DateTime的实例实际上可能只是相对于某个任意零点的时间跨度,但对于大多数阅读您的代码的人来说,它意味着日历上的特定点。同样,aTimeSpan表示日历上两点之间的差距。

在这种情况下,微软选择了清晰而不是吝啬。我不能说我不同意这个决定。

于 2009-09-19T18:15:12.287 回答
0

日期有很多复杂性,例如:

  • 闰年
  • 闰秒
  • 1582年改为公历
  • 没有0年这样的事实
  • 月份长度的差异

将日期和时间跨度视为不同的事物意味着这些类型的问题在实践中不太可能让您感到困惑。

于 2011-07-14T23:21:08.960 回答
-1

它的糖不多或少....

于 2009-09-19T17:14:16.637 回答