使用 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>
我使用 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>