使用JavaScript / MomentJS不论时区如何显示一致的时间

时间:2019-07-09 00:12:28

标签: javascript timezone momentjs

使用存储为时间戳记的日期(自纪元以来的毫秒数)时遇到问题,其中我具有事件的时间戳记和时区,但是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时间戳意味着避免出现此确切问题,但是某些地方出了问题。任何指导将不胜感激。

1 个答案:

答案 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对象的选择器将允许这样做,但是以我上面描述的方式“作弊”是不允许的。