如何收集访问者的时区信息?

我两者都需要:

时区(例如,欧洲/伦敦) 与UTC或GMT的偏移(例如,UTC+01)


当前回答

这可能不是最优雅的解决方案,但却是最通用的。

这使用了Intl的timeZoneName属性。DateTimeFormat

function getTimeZone(zoneName = "long") { // set up formatter let formatter = new Intl.DateTimeFormat(undefined, { timeZoneName: zoneName }); // run formatter on current date return formatter.formatToParts(Date.now()) // extract the actual value from the formatter, only reliable way i can find to do this .find(formatted => formatted.type === "timeZoneName")['value']; } // console.log every type for (const zoneName of ['short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric']) { console.log(`${zoneName}: ${getTimeZone(zoneName)}`) } /* short: CDT long: Central Daylight Time shortOffset: GMT-5 longOffset: GMT-05:00 shortGeneric: CT longGeneric: Central Time */

这不仅得到格式化的GMT偏移时间(即GMT-5),还得到时区的口语化名称(即中央日光时间)。

这个方法唯一不做的是获取IANA时区。我推荐上面的答案。

据我所知,DateTimeFormat没有自定义格式化的方法,因此使用formattpart,这似乎是获得时区的唯一可靠方法。

值得注意的是,目前的ECMAscript规范中只有short和long被正式定义,其他4个选项只是标准提议的一部分,在撰写本文时,safari中明显没有,尽管它正在进行中

其他回答

正如其他人提到的,要获得时区:

const tz = Intl.DateTimeFormat().resolvedOptions().timeZone

之前没有提到,要从时区获取偏移量,使用区域设置“ia”(参见https://stackoverflow.com/a/64262840/1061871)

const getOffset = (tz) => Intl.DateTimeFormat("ia", {
                timeZoneName: "shortOffset",
                timeZone : tz
              })
                .formatToParts()
                .find((i) => i.type === "timeZoneName").value // => "GMT+/-hh:mm"
                .slice(3); //=> +/-hh:mm

 console.log(tz + ' UTC' + getOffset(tz))


 

它已经回答了如何以分钟为单位获得一个整数的偏移量,但如果有人想要本地格林尼治标准时间偏移量作为字符串,例如。“+ 1130”:

function pad(number, length){
    var str = "" + number
    while (str.length < length) {
        str = '0'+str
    }
    return str
}

var offset = new Date().getTimezoneOffset()
offset = ((offset<0? '+':'-')+ // Note the reversed sign!
          pad(parseInt(Math.abs(offset/60)), 2)+
          pad(Math.abs(offset%60), 2))

这个问题已经有30多个答案了,但我敢肯定,有些人来到这里,却真的不知道他们在寻找什么。

惊喜!你要打开一个棘手的罐子了!

我强烈推荐你阅读我最近写的一篇关于在JS中处理时区的头痛的文章!https://medium.com/@EyeDin/time-and-time-zone-headaches-in-javascript-ae4d873a665d

你到底在找什么?

根据您的实际用例,您实际上需要不同的东西。


1)一种将固定时间点(比如2021年11月13日旧金山晚上11:35:21)转换为用户的挂钟时间的方法,用户可能在世界上任何地方。

使用.toLocaleString()。

通过DateTimeFormat中列出的locale和选项两个参数,它有更多用于格式化的自定义参数。它甚至支持不同的日历、语言、年、月、日等格式(没有传递的参数将使用用户的默认设置,这可能是最理想的)。

作为开发人员,只需将该时间点值(例如,Sales活动或竞赛的开始时间)作为UNIX时间戳数(即UTC从1/1/1970开始的秒数,与时区无关,与dst0无关)传递给客户机,然后执行new Date(timestamp). tolocalestring()。例如,对于上面的时间点,值是1636875321000(毫秒),via +dateWithTimeZone("America/Los_Angeles",2021,10,13,23,35,21)使用这个答案的函数。


2)能够在3个月后的当地时间上午9点向用户发送通知/电子邮件

这是一个完全不同的要求!非常非常不同!

格林尼治时间8点还不够吗?

No!

这只是用户在某个时间点所在的时区,比如“现在,今天”。

您如何知道用户是否使用日光节约时间?(美国的亚利桑那州没有,其他很多国家、州甚至县都没有!) 您如何知道用户是在DST中还是不在DST中?旧金山是冬天(GMT-8)还是阿拉斯加是夏天(同样是GMT-8) ?看看这个巨大的列表,你会抓狂的:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

“PDT”还不够吗?

No!

首先,PDT并不是全年都有的。现在是11月,timeanddate.com网站上写着“PDT目前没有位置。你想看PST吗?”(阅读我上面的Medium文章了解更多细节。)

除非你想要从客户端到客户端的UI字符串,这个PDT/CEST/等等。一点价值都没有!

为什么?因为墨西哥的蒂华纳和美国的旧金山在6月都在PDT,但他们有不同的DST(日光节约)日,他们切换到PST。你需要知道这一点!

美国2022年夏令时为2022年3月13日至11月6日。墨西哥2022年的夏令时时间为2022年4月3日至2022年10月30日。因此,虽然蒂华纳和旧金山在近93%的时间里共享“PDT/PST”,但在7%的时间里(例如,2022年的3月14日至4月3日,以及10月31日至11月6日),它们并不匹配。

所以,即使你知道现在是11月,也知道用户处于PDT状态,你也无法判断用户设备中的时间戳数字是2022年4月1日上午9点。

PS.如果你试图从new Date(). tostring()中获取PDT,情况会更糟。仅供参考,如果你将你的机器的语言环境更改为es-MX(西班牙语,这是13%的美国人的语言),你会得到这样的结果:

> new Date().toString()
'Sun Nov 14 2021 00:47:25 GMT-0800 (hora estándar del Pacífico)'

祝hedP好运!

那么,我该怎么办呢?

唯一正确的方法是使用IANA(阅读“标准”)时区值。这是维基百科页面的第二栏。它来自:

const timeZoneIANA = Intl.DateTimeFormat().resolvedOptions().timeZone;

它返回IANA值,即旧金山的America/Los_Angeles。如果有的话,这是您应该为用户存储在数据库中的值。您可以从时区获得关于它的各种信息。Json包,并将时间从/转换为该时区,加上日期(这很重要),就像这个答案。

那么IE11呢?

IE11不支持Intl....事情大多数填充物只是猜测,是不准确的。所以,试着放弃对IE11的支持,或者使用一个库,为一些不准确做好准备。


3)我只是想知道他们目前的时区偏移(例如-8:00)

你可能不应该这么做。我想不出你需要这个的真正原因。再说一次,知道今天的值,并不能告诉你明天的值是多少。

不管怎样,你总能得到这个值

new Date().getTimezoneOffset() / 60

在客户端。

注意:. gettimezoneoffset()不是date对象的时区偏移量,你是在调用它。请阅读文档。

准确地说,它接受传递的Date对象(该对象具有时间戳,引用历史中的单个时间点),然后告诉该时间点的UTC时间和本地时间之间的差值。

同样,这取决于日期。看到的:

console.log(new Date("6/13/2021").getTimezoneOffset() / 60); // It's 7 on a machine that runs in San Francisco
console.log(new Date("11/13/2021").getTimezoneOffset() / 60); // It's 8 on a machine that runs in in San Francisco

有图书馆吗?

下面是支持时区交换的库列表。

但是,请记住,您必须首先解决设计问题,然后再寻找代码。关于时区的事情很容易变得复杂,你必须知道“PDT”和“GMT-8”不是全年为用户存储在DB中的有信息/有价值的时区值。:)

这可能不是最优雅的解决方案,但却是最通用的。

这使用了Intl的timeZoneName属性。DateTimeFormat

function getTimeZone(zoneName = "long") { // set up formatter let formatter = new Intl.DateTimeFormat(undefined, { timeZoneName: zoneName }); // run formatter on current date return formatter.formatToParts(Date.now()) // extract the actual value from the formatter, only reliable way i can find to do this .find(formatted => formatted.type === "timeZoneName")['value']; } // console.log every type for (const zoneName of ['short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric']) { console.log(`${zoneName}: ${getTimeZone(zoneName)}`) } /* short: CDT long: Central Daylight Time shortOffset: GMT-5 longOffset: GMT-05:00 shortGeneric: CT longGeneric: Central Time */

这不仅得到格式化的GMT偏移时间(即GMT-5),还得到时区的口语化名称(即中央日光时间)。

这个方法唯一不做的是获取IANA时区。我推荐上面的答案。

据我所知,DateTimeFormat没有自定义格式化的方法,因此使用formattpart,这似乎是获得时区的唯一可靠方法。

值得注意的是,目前的ECMAscript规范中只有short和long被正式定义,其他4个选项只是标准提议的一部分,在撰写本文时,safari中明显没有,尽管它正在进行中

尝试Date对象的getTimezoneOffset():

var curdate = new Date()
var offset = curdate.getTimezoneOffset()

此方法返回时区偏移量(以分钟为单位),即GMT和本地时间之间的差值(以分钟为单位)。