使用 jQuery 日期选择器的英国夏令时(计算丢失 1 天)

British Summer Time with jQuery Datepicker (calculation lost 1 day)

我使用 Vue.js with jQuery Datepicker 并看到计算所选日期还剩多少天的问题:

计算天数差异:

getDifferenceInDays: function(date1, date2) {
  const diffTime = date2.getTime() - date1.getTime(); //Math.abs(...);
  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  return diffDays;
},

结果我看不到 13 天了。也许这个问题与 10 月 27 日结束的英国夏令时有关:

你能建议一下如何解决这个问题吗? 非常感谢!

你说得对,这是由夏令时 (DST) 过渡引起的,特别是英国的夏令时将于 10 月 27 日结束。

日期、时间和时区在 JavaScript 中 确实 棘手。当我们访问不同的时区时,本机 Date 对象很难处理。

我建议检查一个在这种事情上做得更好的库:moment and moment-timezone 非常擅长正确处理 DST 更改。

您得到错误结果的原因是您正在查看 UTC 时区中日期之间的差异(因为 getTime() 总是 returns 自 1970-01-01 以来的毫秒数00:00 UTC(Unix 纪元))。

结果(在您的情况下)将是 13.04(或 13 + 1/24)天,因为 UTC 偏移量在两个日期(从 UTC+01 到 UTC+00)之间发生变化。

例如,伦敦时间 10 月 14 日 12:00 是 11:00 UTC, 而伦敦时间 10 月 27 日 12:00 是 12:00 UTC。所以差异将是 313 小时,或 13.04 天。

由于您调用的是 Math.ceil,这将四舍五入为 14,这是不正确的。

这在两个日期都在相同的 UTC 偏移量内时有效(例如,都在夏季,都在冬季)。但是如果它们是 DST 更改的任一侧,您将得到不正确的结果。

下面的代码将计算指定时区中两个日期之间的天数。您可以将 true 的第三个参数传递给 moment.diff 以获取小数天数。 (在这种情况下将为 0)。

如果您不想使用其他库,例如 moment,您可以简单地使用 Math.round 来获得大致的天数差异。 不会那么稳健,因为如果 date2 比 date1 早不到 12 小时,您将得到零天差。 只要您了解这一点,它就可以满足您的需求。我添加了一个 getDifferenceInDaysRounding 来显示这一点。

const CURRENT_TIMEZONE = "Europe/London";

// Date 1 is 12:00, 14th October, London time
const date1 = moment.tz("2019-10-14T12:00:00", CURRENT_TIMEZONE).toDate();

// Date 2 is 12:00, 27th October, London time
const date2 = moment.tz("2019-10-27T12:00:00", CURRENT_TIMEZONE).toDate();

function getDifferenceInDaysOriginal (date1, date2) {
  const diffTime = date2.getTime() - date1.getTime();
  let diff1 = diffTime / (1000 * 60 * 60 * 24);
  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  return diffDays;
}

// Use with caution.. this will not work in all cases!
function getDifferenceInDaysRounding (date1, date2) {
  const diffTime = date2.getTime() - date1.getTime();
  const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24));
  return diffDays;
}

function getDifferenceInDaysTimezoneAware(date1, date2, timezone) {
  // Ensure the dates are in the correct timezone.
  const dateLocal1 = moment.tz(date1.getTime(), timezone);
  const dateLocal2 = moment.tz(date2.getTime(), timezone);

  // Pass false to return the integer number of days. NB, this will be 0 for less than 24 hours difference. Pass true to get the fractional days.
  return dateLocal2.diff(dateLocal1, "days", false);
}

console.log("Original result:", getDifferenceInDaysOriginal(date1, date2));
console.log("Get difference (rounding):", getDifferenceInDaysRounding(date1, date2));
console.log("Timezone-aware result:", getDifferenceInDaysTimezoneAware(date1, date2, CURRENT_TIMEZONE));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data-1970-2030.js"></script>