3

我正在阅读 Phobos 文档。有时我无法理解某些方法的逻辑。

日期卷

将给定的年数或月数添加到此日期。负数将减去。滚动和添加之间的区别在于滚动不会影响较大的单位。

也许是火卫一不好考虑,也许我不明白它在哪里有帮助。

如果我添加到 2013-07-01 例如 200 天,我希望得到 2014 年,而不是 2013 年。

有人能解释一下逻辑吗?

4

2 回答 2

5

roll也存在于 Java 中......我记得唯一一次在这两种语言中使用它虽然是实现一个小的日期选择器小部件。每件事都有单独的字段:月、日、年,并且您希望用户能够在不增加月份的情况下旋转天数,因为它们是单独的字段。(假设它被设置为第 30 个并且他们想选择第一个。最快的方法可能是点击向上箭头并让它翻滚。)

可能还有其他用途,我现在不记得了。即使我前段时间实现 ical 支持(可悲的是,专有的),我也从未使用过 roll 方法,但那里可能有它的潜力。ical 是日历上重复事件的标准,其中有一些棘手的事情,并且您可以通过几种不同的方式来实现它。

在评论中,您还问为什么不能add几天。原因是添加天数非常简单,并且包含opBinary

datetime += 5.days; // works

但这几个月都行不通,因为几个月的持续时间是可变的。这就是该add方法发挥作用的地方:假设现在是 1 月 30 日,并且您添加了一个月。您可以只添加 31 天,因为 1 月份有 31 天。这将使您在 3 月 2 日登陆(我认为,在我的脑海中这样做)......但是 1 月 30 日 + 1 个月可能也需要是 2 月 28 日。或者可能是 2 月 29 日。假设您要支付每月到期的账单月,这些到期日之间的天数将发生变化。

add方法处理此问题,并为您提供在最后一天跌倒或溢出到下个月的选项。

不过,我有点认为天也没有静态长度……考虑 DST 过渡。(或闰秒,但处理它们会很疯狂)。也许我们可以问JMD。

于 2015-08-02T18:00:25.440 回答
5

因此,简短的回答是Adam 是对的,并且roll专门用于处理日期选择器和类似用例,并且add专门用于处理月份和年份,因为Duration不能(因为月份和年份不会转换为 hecto-纳秒不知道起点)。但是,如果可以澄清事情,我会采取更深入的答案。

DateTime, Date, 并且TimeOfDay是为基于日历的操作而设计的。它们与系统时钟无关,没有时区概念,并且它们在内部将日期/时间的部分作为单独的单位(年、月、日、小时等)保存。因此,当您hour在 a 上设置字段时DateTime,您实际上只是在调整其中一个DateTime成员变量。当您Duration向它们添加 a 时,必须将其拆分以单独添加到每个单元中,因此执行诸如添加Duration7 小时之类的操作可以增加的不仅仅是hour字段。例如

auto dt = DateTime(2015, 8, 2, 20, 0, 0);
dt += hours(7);
assert(dt == DateTime(2015, 8, 3, 3, 0, 0));

而且由于Duration在内部以百纳秒为单位保持其价值,因此您不一定要添加到特定单位。即它不像+= hours(7)是专门添加到该hour领域。它在整体上增加了一些百纳秒,DateTime并且必须弄清楚它如何影响每个单位,而不是影响特定单位。例如

auto dt = DateTime(2015, 8, 2, 20, 0, 0);
dt += hours(7) + minutes(5) + seconds(22);
assert(dt == DateTime(2015, 8, 3, 3, 5, 22));

roll另一方面,专门用于添加到特定单元而不影响任何其他单元。正如 Adam 正确确定的那样,它的主要用例是日期选择器对话框中的旋转控件等,您最终会在其中调整特定时间单位一定量,并且您不想影响其他单位,只是那个你在调整。所以,如果你增加 7 个小时,你最终不会增加一天,只是增加几个小时。

auto dt = DateTime(2015, 8, 2, 20, 0, 0);
dt.roll!"hours"(7);
assert(dt == DateTime(2015, 8, 2, 3, 0, 0));

现在,事情确实变得更有趣了几个月和几年,因为它们没有固定的长度。这就是为什么Duration不支持他们。你不能说x百纳秒等于y几个月或z几年。但是,虽然Duration对于月份或年份没有意义,但我们仍然希望能够将月份和年份添加到 a Dateor DateTimeadd!"years"所以,我们必须add!"months"处理这个问题,它允许你控制一些奇怪的事情,比如在 1 月 31 日加上 1 个月应该在 2 月底还是 3 月初结束(因为没有 2 月 31 日)。

这些都不考虑任何类型的时区。它纯粹是处理日历时间。addroll是纯粹基于日历的操作,并且在、和+=上实现是基于日历的操作。它在进行计算时会考虑年、月、日等(并且在他们的情况下不能这样做,因为单位在其成员变量中被拆分)。DateTimeDateTimeOfDay

然后我们有SysTime。它旨在表示系统的时间(这确实意味着将时区考虑在内)。但是,避免 DST 相关问题等的唯一方法是始终保持 UTC 时间。因此,SysTime始终将其时间保持在 UTC 内部(以百纳秒为单位),并仅在需要时使用TimeZone对象(LocalTime默认情况下)将其转换为特定时区。这避免了各种烦人的与时间相关的错误。并且任何在系统时间上操作的人都应该使用SysTime而不是DateTime, Date, 或TimeOfDay.

因此,当您添加到SysTimeusing时+=,您实际上只是将来自 的百纳秒添加到内部使用Duration的百纳秒SysTime。不会发生任何日历操作(如果发生了,您最终会遇到许多与 DST 相关的错误)。SysTime根本不是基于日历的。为了执行任何日历操作,SysTime必须将 a 内部保存的百纳秒转换为公历日期表示的任何内容(使用TimeZone对象考虑时区)。

但是,为了用户友好,SysTime提供了许多基于日历的属性和功能DateTime(例如yearmonthdayhour等属性和addandroll功能)。为了支持这一点,它基本上最终转换为DateDateTime在内部执行它们,这意味着将时区考虑在内,如果使用不正确,这可能意味着引入 DST 错误。在某些情况下,它的效率低得令人讨厌。例如,如果您有一个SysTime名为st

auto year = st.year;
auto month = st.month;
auto day = st.day;

转换stDate三倍,而直接转换为 aDate并从那里获取单位会更有效。因此,用户友好性有点像一把双刃剑。

此外,它提出了 Adam 提出的关于如何添加和滚动处理 DST 的问题。roll并且addSysTime两者上都处理 aDateDateTime内部,并且将 DST 考虑在内,因为从 UTC 转换SysTimeDateDateTime将其从 UTC 转换为它的TimeZone对象所代表的任何内容。因此,roll并按照他们的操作方式进行操作add-因为他们正在转换并再次转换。SysTimeDateTimeDateTime

但正如我已经说过的,+=onSysTime只是将其添加Duration到其 UTC 时间。因此,没有办法SysTime向其中添加小于月的单位并考虑 DST。您必须明确转换为 a DateTime+=在其上使用,然后将其转换回 a SysTime,这有点难看,但这不是您通常想要的。我想我们可以SysTime.add使用小于几个月的单位进行工作,但是你最终只会让人们使用它,并在他们+=本应使用但不理解差异时最终遇到与 DST 相关的错误。

真的,在这一点上,我质疑将这些日历功能添加到其中SysTime是否明智,并且它实际上最终会损害可用性而不是按预期提供帮助。但我怀疑我们现在是否可以更改它——或者更准确地说,虽然我们可以更改它,但我质疑弃用这些功能SysTime并强迫人们更改他们的代码是否值得它所带来的痛苦。如果我可以重做,我会认真考虑不要将任何基于日历的操作放在SysTime.

无论如何,希望那堵文字墙足够有启发性,值得你花时间。

于 2015-08-02T23:04:10.540 回答