Date roll在哪里有用?

时间:2015-08-02 14:16:51

标签: datetime d phobos

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

Date roll

  

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

也许Phobos没有被考虑好,也许我不明白哪里有用。

如果我在2013-07-01中添加例如200天,我预计将获得2014年,但不会是2013年。

有人可以解释这个逻辑吗?

2 个答案:

答案 0 :(得分:5)

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

可能还有其他用途,我现在就不记得了。即使我刚刚实施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。

答案 1 :(得分:5)

所以,简短的回答是Adam is rightroll专门用于处理日期选择器和类似的用例,add专门处理数月和数年,因为Duration无法解决问题(因为数月和数年不会在不知道起点的情况下转换为百纳秒)。但是,如果澄清事情的话,我会更深入地回答它。

DateTimeDateTimeOfDay专为基于日历的操作而设计。它们与系统时钟无关,它们没有时区概念,它们将日期/时间的各个部分作为单独的单位(年,月,日,小时等)保存在内部。因此,当您在hour上设置DateTime字段时,您实际上只是调整DateTime个成员变量之一。当你向他们添加Duration时,必须将其分开以单独添加到每个单元,因此执行类似于添加Duration 7小时的内容可能会增加hour以上的内容。 }字段。 e.g。

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增加了几百纳秒的时间,并且必须弄清楚它是如何影响每个单位而不是影响特定单位的。 e.g。

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专门用于添加到特定单元而不影响任何其他单元。正如亚当正确确定的那样,它的主要用例是日期选择器对话框中的旋转控件等,你最终会调整一定数量的特定时间单位,而你不想影响其他单位,只是一个你正在调整的。所以,如果你增加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在几个月或几年内没有意义,但我们仍希望能够将月份和年份添加到DateDateTime。所以,我们有add!"years"add!"months"来处理这个问题,它允许你控制一些奇怪的事情,比如在1月31日加1个月到2月底或3月初是否结束(因为没有2月31日)。

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

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

因此,当您使用SysTime添加到+=时,您实际上只是将Duration的百纳秒加到{{1}的百纳秒内部使用。没有日历操作发生(如果他们这样做,你最终会得到许多与DST相关的错误)。 SysTime根本不是基于日历的。为了执行任何日历操作,其内部持有的SysTime百纳秒必须转换为格里高利历上日期表示的任何内容(使用SysTime对象将时区考虑在内。)

但是,为了方便用户使用,TimeZone提供了SysTime所做的许多基于日历的属性和功能(例如DateTimeyearmonthday等属性以及houradd函数)。为了支持这一点,它基本上最终会在内部转换为rollDate来执行它们,这意味着要考虑时区,如果它们未正确使用,可能意味着引入DST错误。在某些情况下,它的效率非常低。例如,如果您有一个名为DateTime的{​​{1}}变量

SysTime

st转换为auto year = st.year; auto month = st.month; auto day = st.day; 三次,而仅显式转换为st并从那里获取单位会更高效。所以,这种用户友好性是一把双刃剑。

此外,它提出了亚当提出的关于如何添加和滚动处理夏令时的问题。 Date上的Dateroll都会在内部处理addSysTime,并将DST考虑在内,因为从Date转换为DateTimeSysTime将其从UTC转换为其Date对象所代表的任何内容。因此,DateTimeTimeZone完全按照rolladd的操作方式进行操作 - 因为他们正在转换为SysTime并返回试。

但正如我已经说过的那样,DateTime上的DateTime只会将+=添加到其UTC时间。因此,SysTime无法向其添加小于月的单位并将DST考虑在内。您已明确转换为Duration,在其上使用SysTime,然后将其转换回DateTime,这有点难看,但它已经过了不是你通常想要的东西。我想我们可以让+=使用小于几个月的单位,但是当你最后使用那些并最终得到与DST相关的错误时,他们应该使用{{1}但是没有理解这种差异。

真的,在这一点上,我质疑将这些日历功能添加到SysTime是明智的,并且它实际上最终会损害可用性而不是按预期帮助。但我怀疑我们是否可以在此时更改它 - 或者更确切地说,当我们可以更改它时,我质疑在SysTime.add上弃用这些函数并强迫人们更改其代码将是值得它会造成的痛苦。如果我可以重做它,我会认真考虑不在+=上进行任何基于日历的操作。

在任何情况下,希望文本墙充满启发性,值得您花时间。