使用存储为时间戳记的日期(自纪元以来的毫秒数)时遇到问题,其中我具有事件的时间戳记和时区,但是JavaScript / MomentJS显示的时间似乎是不正确的偏移量。
为了简洁起见,这是我所拥有的对象的类型:
{
"name" : "Something happening",
"timezone" : "America/Los_Angeles",
"start" : 1586631607338
}
以上活动在加利福尼亚州洛杉矶举行,日期为('start
')。该事件已在东部夏令时通过浏览器保存到数据库中。因此,在创建此事件的浏览器中,时间是正确的。
现在将浏览器的时区更改(从EDT更改为PDT)时,上述事件的时间现在要早3个小时。
这不准确,该事件需要显示事件开始的正确时间,在本示例中,该时间应该为3:00 PM
,但显示为12:00 PM
基本上,如果用户在洛杉矶,他们会看到此事件的开始时间为12:00 PM
,但作者将开始时间定义为3:00 PM
使用MomentJS,将每个事件的日期转换为UTC时间戳并写入数据库
在创作方面,我们有基本上可以完成以下工作的代码:
// event.startTime is an instance of a JS Date object
var startTime = moment(event.startTime.getTime()).utc().valueOf();
// startTime is now a UTC time stamp
startTime
与事件的时区一起被写入数据库。
在前端/显示器上,我们这样做:
var startTime = moment.tz(event.startTime, event.timezone);
// startTime is now a MomentJS object, which we then use format()
在作者方面,他们创建了一个下午3:00的事件,该事件在事件详细信息屏幕中显示为从3:00 PM开始。
但是,洛杉矶的用户会在下午12:00开始看到此事件
目标是将该事件显示为从3:00 PM开始,无论用户身在何处,该事件的日期都应显示为:
太平洋夏令时间3:00 PM-5:00 PM,但读取时间为:太平洋夏令时间12:00 PM-2:00 PM
据我了解,存储带有时区的UTC时间戳意味着避免出现此确切问题,但是某些地方出了问题。任何指导将不胜感激。
答案 0 :(得分:0)
moment(event.startTime.getTime()).utc().valueOf()
将等于event.startTime.getTime()
,因为getTime
对象的Date
函数返回Unix时间戳(以毫秒为单位),而Unix时间戳为始终基于UTC。从Moment的本地模式切换到UTC模式不会更改底层的时间戳,因此您在这里并没有做太多事情。
在适用于此的各种情况下,让我们看看您提供的时间戳记:
moment.utc(1586631607338).format() //=> "2020-04-11T19:00:07Z"
moment.tz(1586631607338, 'America/New_York').format() //=> "2020-04-11T15:00:07-04:00"
moment.tz(1586631607338, 'America/Los_Angeles').format() //=> "2020-04-11T12:00:07-07:00"
如您所见,该UTC时间戳在东部时间代表下午3点,在太平洋时间代表中午。 (您的时间戳中还有一个额外的7338
毫秒,您可能需要四舍五入。)
如果您希望时间戳记代表太平洋时间当天的下午3点,则应为1586642407338
:
moment.utc(1586642407338).format() //=> "2020-04-11T22:00:07Z"
moment.tz(1586642407338, 'America/New_York').format() //=> "2020-04-11T18:00:07-04:00"
moment.tz(1586642407338, 'America/Los_Angeles').format() //=> "2020-04-11T15:00:07-07:00"
在我看来,您遇到的问题是,您是从浏览器本地时间生成的Date
对象开始的。可能来自某种日期/时间选择器。由于这个问题,许多这样的选择器已被编写为返回ISO 8601字符串而不是Date
对象。您还将发现,HTML5的<input type="date" />
和<input type="datetime-local" />
内置在浏览器中的对象也使用字符串而不是Date
对象。还有一些基于Moment并具有时区设置的选择器-因此您在这方面确实有选择。
但是,如果您不想更改选择器,则可以通过以下方式调整时区来作弊:
var startTime = moment(event.startTime).tz(event.timezone, true).valueOf();
这里的关键部分是传递到true
实例函数的tz
参数的keepTime
标志。它提供的行为与utcOffset
实例函数中的行为类似,但是具有指定的时区而不是固定的偏移量。 docs for that的描述如下:
utcOffset
函数具有一个可选的第二个参数,该参数接受 一个布尔值,指示是否保留一天中的现有时间。
传递
false
(默认)将在Universal中保持不变 时间,但当地时间会改变。传递
true
将保持相同的本地时间,但以 在世界标准时间中选择其他时间点。
tz
函数上有missing documentation,但它的工作方式相同,将有助于为您的方案创建正确的时间戳。
以这种方式“作弊”的缺点是,即使在事件所在的时区有效,您的用户也无法选择落入当地时区DST间隙的时间。例如,2020-03-08的凌晨2点在America/New_York
中无效,但在America/Phoenix
中则完全可以。不使用Date
对象的选择器将允许这样做,但是以我上面描述的方式“作弊”是不允许的。