问题说明:
我正在尝试在(时刻日期数组)中创建一个日期范围,其中每个日期都比前一个日期晚1天。 这对于所有用户都可以正常工作,但是我们已经获得了针对美国/圣地亚哥时区的用户的错误报告,该用户在UI中看到重复的日期。 我注意到的是,将特定日期增加1天实际上只增加了23小时。
在美国/圣地亚哥时区中,日期为8/11/2018 00:00:00
,增加1天将使时间增加到8/11/2018 23:00:00
而不是8/12/2018 00:00:00
。
该文档解释说,在使用days_时跨DST转移时应保留小时数,但此处似乎不正确。
添加跨越夏时制时间的时间时,还需要记住一些特殊的注意事项。 如果要添加年,月,周或天,则原始小时将始终与添加的小时匹配。
// This code is from moment.js docs https://momentjs.com/docs/#/manipulating/add/
var m = moment(new Date(2011, 2, 12, 5, 0, 0)); // the day before DST in the US
m.hours(); // 5
m.add(1, 'days').hours(); // 5
重现步骤
在以下代码中,使用的时区在午夜是DST规则。 我的日期是该规则生效前一天的午夜。我为此日期添加了1天,并希望它应该是第二天的午夜/ 0小时(应保留小时),但实际上它只是在添加23小时。
fmt = d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x); // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(1, 'day')); // "2018-08-11T23:00:00-04:00 America/Santiago" - offset unchanged, added 23 hours not 1 day
fmt(x.clone().add(2, 'day')); // "2018-08-13T00:00:00-03:00 America/Santiago" - original hour preserved now
在这里,您可以看到添加24小时将其向上移动了25小时,并且更改了时区偏移量。
fmt = d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x); // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(24, 'hours')); // "2018-08-12T01:00:00-03:00 America/Santiago" - offset changed, added 25 hours
fmt(x.clone().add(48, 'hours')); // "2018-08-13T01:00:00-03:00 America/Santiago"
环境:
其他可能有用的信息:
JS日期输出
(新的Date())。toString()
(新的Date())。toLocaleString()
(新的Date())。getTimezoneOffset()
navigator.userAgent
瞬间版本
我试图在欧洲/罗马时区重现此内容,但无法执行以下操作:
欧洲/罗马-https: //www.timeanddate.com/time/change/italy/rome
2018年10月28日-夏令时结束
当当地夏令时快到了
2018年10月28日星期日凌晨3:00:00将时钟向后调1小时
改为当地标准时间2018年10月28日星期日凌晨2:00:00。
此代码中没有错误。
d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('10/27/2018 00:00:00'), 'Europe/Rome');
fmt(x) // "2018-10-27T06:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'day')) // "2018-10-28T06:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'day')) // "2018-10-29T06:00:00+01:00 Europe/Rome"
您可以看到TZ发生了变化,但是小时数保持不变。
根据复制步骤,当添加的日期恰好落在DST截止日期上时,代码中似乎存在差异。 在圣地亚哥时区,DST规则是在午夜将时钟倒退一个小时。
似乎这种边缘情况可能专门针对DST截止时间为午夜的时区,因为我无法使用截止时间为凌晨3点的时区进行重现,而x
日期为凌晨3点。
欧洲/罗马-https: //www.timeanddate.com/time/change/italy/rome
2018年10月28日-夏令时结束
当当地夏令时快到了
2018年10月28日星期日凌晨3:00:00将时钟向后调1小时
改为当地标准时间2018年10月28日星期日凌晨2:00:00。
此代码中没有错误。
fmt = d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('October 27, 2018 03:00:00'), 'Europe/Rome');
fmt(x); // "2018-10-27T09:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'days')) // "2018-10-28T09:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'days')) // "2018-10-29T09:00:00+01:00 Europe/Rome"
我能够确认该错误仅在DST截止时间是午夜时才存在于时区,并且您要向日期添加时间,从而导致该日期恰好在截止时间。
美国/ Punta_Arenas - https://www.timeanddate.com/time/zone/chile/punta-arenas
在2016年,该时区在午夜也有一个截止时间。
此代码似乎有一个错误。 每行后查看评论
fmt = d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('08/13/2016 00:00:00'), 'America/Punta_Arenas')
fmt(x); // "2016-08-13T00:00:00-04:00 America/Punta_Arenas"
fmt(x.clone().add(1, 'days')); // "2016-08-13T23:00:00-04:00 America/Punta_Arenas" - 23 hours added, not 1 day, no tz offset change
fmt(x.clone().add(2, 'days')); // "2016-08-15T00:00:00-03:00 America/Punta_Arenas"
我发现在减去相同类型的时区(午夜DST)时似乎出现了类似的问题。
将日期精确地减去DST时,不只在当天保留小时,而是更改日期。
fmt = d => d.format() + ' ' + d.tz()
x = moment.tz(new Date('08/13/2018 23:00:00'), 'America/Santiago');
fmt(x); // "2018-08-14T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(1, 'days')); // "2018-08-13T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(2, 'days')); // "2018-08-12T01:00:00-03:00 America/Santiago" - hour not preserved, but date changed
fmt(x.clone().subtract(3, 'days')); // "2018-08-11T00:00:00-04:00 America/Santiago" - original hour preserved now
我们收到了以America/Asuncion
作为其时区的用户的另一报告。
在我们的应用程序中产生错误的函数如下所示:
function generateDayRange(start, end) {
const days = [];
let current = start.clone();
while (current <= end) {
days.push(current.clone());
current = current.add(1, 'days');
}
return days;
}
使用此功能时,我们传入的开始日期是一天的开始,而结束日期的时间不太重要:
generateDayRange(startDate.clone().startOf('week'), startDate.clone().endOf('week'));
generateDayRange(getAppDate(startDate), getAppDate(endDate));
generateDayRange(startRange, startRange.clone().add(27, 'days'));
问题在于,生成的时刻日期数组包含重复的日期,这反映在我们的应用程序的UI中(日历具有重复的日期)。
我更大的担心是,由于库的行为不符合预期,因此可能会注意到一些更细微的问题,例如感知到的数据丢失,错误的请求等。
我将努力在Codepen或其他工具中进行一系列测试,以使好奇的读者可以刺痛地解决此问题。
相关问题#4785包含以下代码的链接:
时刻示例https://runkit.com/embed/1r62d83amq7x
luxon可以正确处理
https://runkit.com/embed/t49achvensqf
我知道最近有DST更改,因此我们应该对照develop
(但未发布)上当前的代码进行检查。 或者,我们可以等到下一个版本,看看情况是否仍然相同。
这里的行为看起来像是在时区v0.5.4和v0.5.26之间进行了更改。
在旧版本中,在“ 2018-08-11”中增加1天可让您当天的晚上11点
在新版本中,它在午夜为您提供``2018-08-12''。 但是这个时间实际上不存在,添加1分钟可以返回一个小时-但是,这种DST更改应该增加1小时。
旧版本,其行为如本期所述:
https://jsfiddle.net/eqyvuxht/1/
新版本,改变了行为,但我认为还是错误的?
https://jsfiddle.net/0h6atn4b/4/
这是一种解决方法:
function switchZone(m, zone) {
let arr = [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second()];
if(zone) {
return moment.tz(arr, zone);
}
return moment(arr);
}
function safeAddDays(m, days) {
let oldZone = m.tz();
let utc = switchZone(m, 'UTC');
utc.add(days, 'days');
return switchZone(utc, oldZone);
}
我们在本月(2019年9月)遇到了同样的问题,使用我们的日期选择器的圣地亚哥用户看到的日历的日期分别为1、2、3、4、5、6、7、7、7、7 9月……在后台,我们还使用了.add(1, 'days')
。 如果到那时我们还没有解决方案,它将在明年2020年9月再次发生。
@mblandfo解决方法似乎也对我们
最有用的评论
这里的行为看起来像是在时区v0.5.4和v0.5.26之间进行了更改。
在旧版本中,在“ 2018-08-11”中增加1天可让您当天的晚上11点
在新版本中,它在午夜为您提供``2018-08-12''。 但是这个时间实际上不存在,添加1分钟可以返回一个小时-但是,这种DST更改应该增加1小时。
旧版本,其行为如本期所述:
https://jsfiddle.net/eqyvuxht/1/
新版本,改变了行为,但我认为还是错误的?
https://jsfiddle.net/0h6atn4b/4/
这是一种解决方法: