假设您有一个想要在网站上显示的时间。您已在您的时区中输入了它。您始终可以明确说明,例如:下午 3:00 东部标准时间。然后让读者将其转换为他们当地时间。每个时区 是一个非常好的网站。
但为读者本地化时间会更好。时区转换非常混乱。而且这是计算机擅长的领域。毕竟,JavaScript 知道读者所在的时区。
我相信有很多方法可以做到这一点。我最近在一个新项目中需要这样做,我借鉴了 Dave Rupert 的做法,他在ShopTalk 的网站上实现了它,以显示我们下次直播的本地化时间。
库
Moment.js 和 Moment Timezone 可以完成这项工作。如果您需要进行任何其他更复杂日期和时间操作或相对计算(例如“32 分钟前”、“2015 年 8 月 12 日”),那么使用这些库会非常方便。
我们还需要检测时区本身,值得庆幸的是,有一个库可以做到这一点。
所以我们将使用
- jstz.js
- moment.js
- moment-timezone.js
您可以通过任何方式打包或链接这些库。
步骤 1:获取时区
在获取时区后,不妨将其存储在 localStorage 中。这样可以减少浏览器下次检索时所需的工作量。
if (!sessionStorage.getItem('timezone')) {
var tz = jstz.determine() || 'UTC';
sessionStorage.setItem('timezone', tz.name());
}
var currTz = sessionStorage.getItem('timezone');
步骤 2:获取时间值
Moment 期望一个日期/时间值(以UTC 格式表示),例如
2015-08-12T14:30Z
如果我们只对时间感兴趣,我们可以从 Moment 本身创建该字符串的日期部分,表示今天的日期(这使得格式化更容易)。日期实际上很重要,因为 UGHGKGJHGH 夏令时。
var date = moment().format("YYYY-MM-DD");
然后我们将创建最终字符串,假设我们有一个包含我们想要的时间的变量
var stamp = date + "T" + theTime + "Z";
并将其转换为 Moment 对象
var momentTime = moment(stamp);
步骤 3:本地化时间
现在我们可以使用 Moment Timezone 调整/本地化该时间
var tzTime = momentTime.tz(currTz);
并正确格式化以供显示
var formattedTime = tzTime.format('h:mm A');
步骤 4:使用它
现在我们可以在页面上的任何位置放置它。如果您也输出时区,您甚至可以替换页面上当前输出的时间,因为即使时区检测失败,它也会回退到 UTC 并将其输出为时区。
output.textContent = "Time in " + currTz + ": " + formattedTime;
调整时间
有时这样做是有意义的(可能从 CMS 的角度来看),要求人们以当地时区输入时间。但是为了正确进行转换,最好将该时间存储为 UTC。
Moment 本身可以帮助调整时间,例如
moment().subtract(4, 'hours');
moment().add(3, 'hours');
服务器端语言也可以提供帮助。例如,PHP 可以对时间进行数学运算
<?php var $sixHoursAgo = strtotime("-6 hours", time()); ?>
使用Moment Timezone 可能是最合理的方法,因为它并不总是精确的公式(再次 UGHADKGHK 夏令时)。
演示
在此演示中,我们使用 HTML5 时间输入并在运行时将其调整为本地化时间。
查看 Chris Coyier 在 CodePen 上的笔 将时间调整为本地时区(@chriscoyier)。
其他方法
您是否有这方面的经验?您是如何处理的?
我们在此处使用的依赖项并非微不足道!似乎存在原生 JavaScript 方法(例如 getTimezoneOffset())具有相当好的支持,可以用于类似的效果。您是否有依赖这些方法的经验?
这非常好,我从未想过在客户端这样做。至少现在我可以通过 AJAX 获取一些帖子并为其本地用户转换其时间。
Moment++
它在我的当前项目中提供了巨大的帮助,该项目的大部分内容是需要大量日期和时间逻辑的预订系统。
jstz 尝试根据偏移量确定时区,但它无法可靠地做到这一点——可能有多个时区与一个偏移量匹配。
因此需要用户交互——用户必须验证自动选择的时区是否正确。
您应该将保存函数包装在 try catch 块中,以避免 Safari 私人浏览模式出现问题。
您在文章中提到了“localStorage”,但在示例中使用了“sessionStorage”;后者是正确的,因为它会过期。原因是时区当然会发生变化,例如通过更改物理位置或自动更改,例如英国的 BST/GMT。
您不需要库来做到这一点!
JavaScript 日期最初设计为在本地时间工作,后来扩展为支持 UTC。这意味着只要您在代码中以 UTC 存储时间(并且您确实应该在任何地方都将所有日期内容存储为 UTC,并且仅在显示时将其本地化),您就可以轻松地转换时间,而无需任何库。
http://codepen.io/Merri/pen/EjJbyw
如果您对 Moment 有更多实际用途,以便使用其更多功能,那么我认为它还可以。但对于这种情况来说,它确实有些过分了。
我之前也考虑过自己提及 Date 对象的本地时间方法。
您的示例唯一“缺少”的是时区的实际名称;而 Chris 的示例告诉我时间是“欧洲/柏林”——但我真的需要该信息吗?通常我都会知道自己所在的时区,对吧?
因此,三个(额外)库产生的效果如此之小?在我看来,这似乎非常臃肿且浪费资源。
显示我的时间是 16:30——实际是 07:30
奇怪的是,我似乎再也找不到我在 StackOverflow 上发布的帖子了。我确实记得处理过将近 20 年前的日期显示,当时这里的夏令时规则不同。我当时不明白为什么显示时间似乎错误。事实证明,由于开发时生效的夏令时规则,该小时不存在……JS 认为无论年份如何,接受系统上的当前夏令时规则都是一个好主意。如果我没记错的话,这是规范的一部分,因此浏览器不会修复此问题。有趣的是,Node.js(我认为当时是 0.10-0.11 版本)对该不存在的小时进行了不同的舍入,为一致性欢呼:P
我相信这是我在丢失的 StackOverflow 问题中链接到浏览器的示例代码。
原生 Date():http://jsfiddle.net/ybnhfmer/1/
使用 MomentJS:http://jsfiddle.net/ybnhfmer/
时区为新西兰。您可能需要调整您的时区才能看到错误。您可以在此处查看夏令时日期:http://www.timeanddate.com/time/change/new-zealand/auckland?year=1998
它输出到控制台
DATE :Sun Sep 27 1998 03:55:19 GMT+1300 (New Zealand Daylight Time), OFFSET: -780, TIMESTAMP: 906821719000
但很明显,时间比它应该有的时间晚了 1 个小时。碰巧的是,那一天恰好是 2015 年夏令时开始的那一天:http://www.timeanddate.com/time/change/new-zealand/auckland?year=2015
我不得不在一个与奥运会相关的项目中做类似的事情,用户时区、奥运会事件的时区以及此网站运行的服务器的时区都以令人讨厌的方式相互影响。我们发现最简单的解决方案是在服务器生成的 HTML 中以服务器本地时区传递日期/时间,然后在客户端以用户设备的时区重新格式化它们。
但使这更容易管理的是使用数据属性来存储与 JS 兼容的日期字符串。因此,我们位于纽约的服务器将提供
<span data-datetime="2015-08-18T18:30:00.000Z">8 月 18 日,下午 2:30 EDT</span>
,但加利福尼亚州的用户将看到8 月 18 日,上午 11:30
。该属性允许我们使用简单的 CSS 查询获取所有需要格式化的日期,并且该值在无需进行任何字符串操作的情况下进行解析。我们在进行此项目时还没有使用 Moment.js,但它会让生活变得轻松得多。拥有一个浏览器原生的
Intl.DateTimeFormat
会更好,令人高兴的是,这现在几乎成为现实了。我为一个通过 TamperMonkey 用户脚本嵌入到网站中的聊天框做了这件事,该脚本从 Node.js websocket 服务器获取时间戳(类似于
new Date()
),我使用此函数将时间转换为客户端上的用户时区我目前正在使用 moment.js、moment-timezone.js 和 jstz.js 将分析数据本地化为特定时区。您可以像这样简化 updateTime 函数
默认情况下,moment 解析日期将日期设置为现在并将其覆盖为它接收到的日期。您可以在此处查看其运行情况:http://codepen.io/gbergeron/pen/RWbJaX