【问题标题】:Displaying consistent time regardless of time zone using JavaScript/MomentJS使用 JavaScript/MomentJS 显示一致的时间,无论时区如何
【发布时间】:2019-07-09 00:12:28
【问题描述】:

在处理存储为时间戳(自纪元以来的毫秒数)的日期时遇到问题,其中我有一个事件的时间戳和时区,但 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 开始。

但 LA 的用户会看到此事件从下午 12:00 开始

无论用户身在何处,目标都是将此事件显示为从下午 3:00 开始,因为事件的日期应为:

3:00 PM - 5:00 PM PDT,但读取为:12:00 PM - 2:00 PM PDT

我的理解是存储带有时区的 UTC 时间戳意味着可以避免这个确切的问题,但是某处出了点问题。任何指导将不胜感激。

【问题讨论】:

    标签: javascript timezone momentjs


    【解决方案1】:

    moment(event.startTime.getTime()).utc().valueOf() 将等于event.startTime.getTime(),因为Date 对象的getTime 函数返回一个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();
    

    这里的关键部分是传递给tz 实例函数的keepTime 参数的true 标志。它提供与utcOffset 实例函数中的类似的行为,但使用命名时区而不是固定偏移量。 docs for that描述如下:

    utcOffset 函数有一个可选的第二个参数,它接受 一个布尔值,指示是否保留当前时间。

    • 传递 false(默认值)将在 Universal 中保持相同的瞬间 时间,但当地时间会改变。

    • 传递true 将保持相同的本地时间,但代价是 选择世界时的不同时间点。

    missing documentationtz 函数中有关于此的内容,但它的工作方式相同,有助于为您的场景创建正确的时间戳。

    以这种方式“作弊”的缺点是,您的用户将无法选择在其当地时区中属于 DST 间隔的时间,即使该时间在事件所在时区有效。例如,2020-03-08 的凌晨 2 点在 America/New_York 中无效,但在 America/Phoenix 中完全没问题。不使用 Date 对象的选择器将允许这样做,但我上面描述的方式“作弊”不会。

    【讨论】:

    • 将此标记为答案。日期选择器小部件确实提供了一个 Date 对象,并且不知道 tz() 函数上的附加布尔标志。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    相关资源
    最近更新 更多