为什么将持续时间(以秒为单位)添加到时刻日期会给过去 1 个月的值带来奇怪的结果?

Why does adding a duration in seconds to a moment date give weird results for values past 1 month?

我 运行 遇到了为 JS 添加 moment date in my Rails app. I use Stimulus 秒数的问题,但这与问题的原因无关。

我有一个 select 表单输入,是我使用 Rails 表单生成器生成的,使用这些 options_for_select:

[["1 day", 1.day], ["3 days", 3.days], ["7 days", 1.week],
["14 days", 2.weeks], ["1 month", 1.month],
["3 months", 3.months], ["6 months", 6.months], ["1 year", 1.year]]

这导致 select 看起来像这样:

<select class="form-control" data-admin--reports-form-target="sendFrequencySelect"
<option value="86400">1 day</option>
<option value="259200">3 days</option>
<option value="604800">7 days</option>
<option value="1209600">14 days</option>
<option value="2629746">1 month</option>
<option value="7889238">3 months</option>
<option selected="selected" value="15778476">6 months</option>
<option value="31556952">1 year</option>
</select>

我有一些 JS,它采用 selected 选项的值并将该秒数添加到时刻日期,然后将不同输入的值设置为从该计算:

let finalRunAt = moment(this.lastGeneratedTimestampInputTarget.value, "YYYY-MM-DD HH:mm");
let secondsToAdd = parseInt(this.sendFrequencySelectTarget.value);
finalRunAt.add(secondsToAdd, "seconds");
this.finalRunTimestampInputTarget.value = finalRunAt.format("YYYY-MM-DD HH:mm");

这可以正常工作...直到您达到 1 个月或更大。以下是 2021-02-25 12:00:

开始日期的计算结果
Select option Result of adding seconds to start date
1 day 2021-02-26 12:00
3 days 2021-02-28 12:00
7 days 2021-03-04 12:00
14 days 2021-03-11 12:00
1 month 2021-03-27 23:29
3 months 2021-05-27 20:27
6 months 2021-08-27 03:54
1 year 2022-02-25 17:49

我一直在尝试对奇怪的(粗体)结果进行逆向工程。是什么导致了这些结果? Rails 认为 1 个月和什么时候认为 1 个月之间是否存在差异?

虽然我不确定为什么 Rails 使用的细微差别,例如 2629746 作为 1.month 的表示,但我已经针对这种情况实施了一个解决方法。

我想保留 Rails 生成的 select 输入,因为它生成的值(在问题中列出)在 Rails 端工作得很好。也就是说,我可以将 select 无缝地用于 select 用作 ActiveSupport::Duration 的间隔。因此,我不想更改 select 输入。

相反,我编写了一个简单的(虽然有点老套)函数来根据 select:

的值进行正确的时刻操作
  addRailsSecondsStringToMoment(seconds_string, moment) {
    switch (seconds_string) {
      case "86400":
        moment.add(1, "days");
        break;
      case "259200":
        moment.add(3, "days");
        break;
      case "604800":
        moment.add(7, "days");
        break;
      case "1209600":
        moment.add(14, "days");
        break;
      case "2629746":
        moment.add(1, "months");
        break;
      case "7889238":
        moment.add(3, "months");
        break;
      case "15778476":
        moment.add(6, "months");
        break;
      case "31556952":
        moment.add(1, "years");
        break;
    }
  }

那我就可以这样调用了:

this.addRailsSecondsStringToMoment(this.sendFrequencySelectTarget.value, finalRunAt);

... 其中 finalRunAt 是一个 moment 对象。可能会更漂亮一些,但它确实有效。如果有人发布一个更性感的解决方案,我很乐意接受,特别是如果它提供了一些关于为什么 Rails 使用它所使用的秒值的信息。