如何收集访问者的时区信息?
我需要两个:
时区(例如,欧洲/伦敦)和与 UTC 或 GMT 的偏移量(例如,UTC+01)
使用偏移量来计算时区是一种错误的方法,你总会遇到问题。时区和夏令时规则在一年中可能会发生多次变化,并且很难跟上变化。
要在 JavaScript 中获取系统的 IANA timezone,您应该使用
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
截至 2022 年 4 月,这适用于全球 93.5% 的浏览器。
旧的兼容性信息
ecma-402/1.0 表示如果没有提供给构造函数,timeZone
可能是未定义的。但是,未来的草案 (3.0) 通过更改为系统默认时区解决了该问题。
在此版本的 ECMAScript 国际化 API 中,如果提供给 Intl.DateTimeFormat 构造函数的选项对象中没有提供 timeZone 属性,则 timeZone 属性将保持未定义。但是,应用程序不应依赖于此,因为将来的版本可能会返回一个字符串值来标识主机环境的当前时区。
在 ecma-402/3.0 中,它仍处于草案中,它更改为
在此版本的 ECMAScript 2015 国际化 API 中,如果在提供给 Intl.DateTimeFormat 构造函数的选项对象中未提供 timeZone 属性,则 timeZone 属性将是默认时区的名称。在这种情况下,以前的版本未定义 timeZone 属性。
使用 getTimezoneOffset()
您可以像这样以分钟为单位获取时区偏移量:
var offset = new Date().getTimezoneOffset();控制台.log(偏移量); // 如果偏移量等于 -60,则时区偏移量为 UTC+01
时区偏移量是 UTC 和本地时间之间的差异,以分钟为单位。请注意,这意味着如果本地时区晚于 UTC,则偏移量为正,如果早于 UTC,则偏移量为负。例如,如果您的时区是 UTC+10(澳大利亚东部标准时间),则将返回 -600。即使对于给定的语言环境,夏令时也可以防止此值保持不变
Mozilla 日期对象参考
请注意,并非所有时区都被整小时偏移:例如,纽芬兰是 UTC 减去 3h 30m(不考虑夏令时)。
另请注意,这只会为您提供时区偏移量(例如:UTC+01),它不会为您提供时区(例如:欧洲/伦敦)。
getTimezoneOffset
不准确的声明是否有效?您所指的文章日期为 2007 年 6 月,没有详细说明该功能如何不准确。实际上,您指出的库 jsTimezoneDetect
使用 getTimezoneOffset
本身。
var hrs = -(new Date().getTimezoneOffset() / 60)
以获得通常使用的小时数的偏移量
timezone
!= utc offset
编辑 3-19-2022 - 警告:我不再推荐这种方法,因为它在多个浏览器和语言环境中存在问题。
我意识到这个答案有点离题,但我想我们中的许多人在寻找答案时也想格式化时区以进行显示,也许还想获得时区缩写。所以这里...
如果您希望客户端时区格式正确,您可以依赖 JavaScript Date.toString 方法并执行以下操作:
var split = new Date().toString().split(" ");
var timeZoneFormatted = split[split.length - 2] + " " + split[split.length - 1];
例如,这将为您提供“GMT-0400 (EST)”,包括适用的时区分钟。
或者,使用正则表达式,您可以提取任何所需的部分:
对于“GMT-0400 (EDT)”:
new Date().toString().match(/([A-Z]+[\+-][0-9]+.*)/)[1]
对于“GMT-0400”:
new Date().toString().match(/([A-Z]+[\+-][0-9]+)/)[1]
对于“EDT”:
new Date().toString().match(/\(([A-Za-z\s].*)\)/)[1]
对于“-0400”:
new Date().toString().match(/([-\+][0-9]+)\s/)[1]
Date.toString 参考:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toString
编辑 10/6/2020 - 上述解决方案可能不适用于所有浏览器和语言环境。如果可能的话,我建议使用提供时区支持的 javascript 库,如 date-fns、luxon 或 dayjs。
split[4] + " " + split[5]
?!
var fullTz = new Date().toString().match(/\(([A-Za-z\s].*)\)/)[1]; var parts = fullTz.split(' '); var tz = ""; parts.forEach(function (element, index, array) { tz += element.substring(0, 1); });
产生:“EST”,例如
new Date().toString()
后 JS 时间的实际表示完全取决于您的语言环境设置。期望其他客户的输出与您的输出相似是一个非常糟糕的主意。
已经回答了如何以整数形式在分钟内获得偏移量,但如果有人想要将本地 GMT 偏移量作为字符串,例如 "+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))
pad(parseInt(Math.abs(offset/60)), 2)
替换第一个填充以使其正确...否则您最终可能会像我一样得到+5.530...我不确定数学地板等在这里是否会更好... .但这至少给了我+0530的预期
parseInt(...)
替换为 (...)|0
或 Math.floor(...)
。为什么要将其转换为字符串然后再转换回数字?
您可以使用:
时刻时区
<script src="moment.js"></script>
<script src="moment-timezone-with-data.js"></script>
// retrieve timezone by name (i.e. "America/Chicago")
moment.tz.guess();
浏览器时区检测是相当棘手的,因为浏览器提供的信息很少。
Moment Timezone 在当年的少数几个时刻使用 Date.getTimezoneOffset()
和 Date.toString()
来收集尽可能多的有关浏览器环境的信息。然后,它将该信息与加载的所有时区数据进行比较,并返回最接近的匹配项。如果出现平局,则返回人口最多的城市所在的时区。
console.log(moment.tz.guess()); // America/Chicago
我在我的项目中编写了一个函数,它以 hh:mm
格式返回时区。我希望这可以帮助某人:
function getTimeZone() {
var offset = new Date().getTimezoneOffset(), o = Math.abs(offset);
return (offset < 0 ? "+" : "-") + ("00" + Math.floor(o / 60)).slice(-2) + ":" + ("00" + (o % 60)).slice(-2);
}
// Outputs: +5:00
function getTimeZone() { var offset = new Date().getTimezoneOffset(), o = Math.abs(offset); return (offset < 0 ? "+" : "-") + ("00" + Math.floor(o / 60)).slice(-2) + ":" + ("00" + (o % 60) ).slice(-2); } // 查看输出 document.write(getTimeZone());
提供偏移量和时区的单行代码是在新的 Date 对象上简单地调用 toTimeString()。来自 MDN:
toTimeString() 方法以人类可读的美式英语形式返回 Date 对象的时间部分。
问题是时区不是标准的 IANA 格式;它比“大陆/城市”IANA 格式更易于用户使用。试试看:
console.log(new Date().toTimeString().slice(9)); console.log(Intl.DateTimeFormat().resolvedOptions().timeZone); console.log(new Date().getTimezoneOffset() / -60);
目前在加利福尼亚州,toTimeString()
返回 Pacific Daylight Time
,而 Intl API 返回 America/Los_Angeles
。在哥伦比亚,您会得到 Colombia Standard Time
,而 America/Bogota
。
请注意,此问题的许多其他答案都试图通过调用 Date.toString() 来获取相同的信息。这种方法并不那么可靠,如 MDN explains:
日期实例是指特定的时间点。调用 toString() 将返回格式为美式英语的人类可读格式的日期。 [...] 有时需要获取时间部分的字符串;这样的事情可以通过 toTimeString() 方法来完成。 toTimeString() 方法特别有用,因为实现 ECMA-262 的兼容引擎可能在从 toString() 获得的 Date 对象的字符串中有所不同,因为格式取决于实现;简单的字符串切片方法可能无法在多个引擎中产生一致的结果。
PDT
可能不是您正在寻找的值的更详细的答案:stackoverflow.com/a/69961228
尝试 Date
对象的 getTimezoneOffset()
:
var curdate = new Date()
var offset = curdate.getTimezoneOffset()
此方法以分钟为单位返回时区偏移量,即 GMT 和本地时间之间的差异(以分钟为单位)。
这个问题已经有 30 多个答案了,但我相信有些人会来到这里,但真的不知道他们在寻找什么。
惊喜!你即将打开一罐蠕虫!
我强烈推荐阅读我最近写的这篇关于在 JS 中处理 TimeZones 令人头疼的文章! https://medium.com/@EyeDin/time-and-time-zone-headaches-in-javascript-ae4d873a665d
您到底在寻找什么?
根据您的实际用例,您实际上需要不同的东西。
1) 一种将固定时间点(如旧金山 2021 年 11 月 13 日晚上 11:35:21)转换为用户的挂钟时间的方法,用户可能在世界任何地方。
通过在 DateTimeFormat 中列出的 locale
和 options
这两个参数,它具有比您想象的更多的格式化自定义参数。它甚至支持不同的日历、语言、格式化年、月、日等的方式(没有传递的参数将使用用户的默认设置,这可能是最理想的。)
作为开发人员,只需将该时间点值(例如销售活动或比赛的开始时间)作为 UNIX 时间戳编号(即从 1970 年 1 月 1 日到UTC, timezone-agnostic and DST0agnostic) 到客户端,然后执行 new Date(timestamp).toLocaleString()
。例如,对于上面的那个时间点,值是 1636875321000
(以毫秒为单位),通过 +dateWithTimeZone("America/Los_Angeles",2021,10,13,23,35,21)
使用来自 this answer 的函数。
2) 能够在本地时间上午 9 点向用户发送通知/电子邮件,从现在起 3 个月
这是完全不同的要求!就像,非常非常不同!
GMT-8:00 还不够吗?
不!
这只是用户在某个时间点所处的时区,例如“现在,今天”。
你怎么知道用户是否有夏令时? (美国的亚利桑那州没有,还有很多其他国家和州,甚至县!)
你怎么知道用户是在夏令时还是不在夏令时?是旧金山的冬天(GMT-8)还是阿拉斯加的夏天(再次,GMT-8)。看到这个巨大的列表吓坏了:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
“PDT”还不够吗?
不!
首先,PDT 甚至不是全年的事情。现在是 11 月,timeanddate.com says“PDT 上目前没有地点。您想看 PST 吗?”(阅读我上面的 Medium 文章了解更多详细信息。)
除非您要使用从客户端到客户端的 UI 字符串,否则此 PDT/CEST/等。一文不值!
为什么?因为墨西哥的蒂华纳和美国的旧金山在 6 月都在 PDT,但是他们有不同的 DST(夏令时)天,他们切换到 PST。你需要知道这一点!
美国 2022 年的 DST 天数为 Mar 13, 2022 to Nov 6, 2022。墨西哥 2022 年的 DST 天数为 April 3, 2022 to Oct 30, 2022。因此,虽然蒂华纳和旧金山在将近 93% 的日子里共享“PDT/PST”,但在 7% 的日子里(例如 2022 年的 3 月 14 日至 4 月 3 日,以及 10 月 31 日至 11 月 6 日),他们没有不匹配。
因此,即使您知道现在是 11 月并且您知道用户在 PDT 中,您也无法确定用户设备中何时(哪个时间戳编号)是 2022 年 4 月 1 日上午 9 点。
PS。如果您试图从 new Date().toString()
获取 PDT
,情况会更糟。仅供参考,如果您从 change the locale of your machine 到 es-MX
(西班牙语,这是美国 13% 人口的语言),您会得到:
> new Date().toString()
'Sun Nov 14 2021 00:47:25 GMT-0800 (hora estándar del Pacífico)'
祝hedP
好运!
那么,我该怎么办?
唯一正确的方法是使用 IANA(阅读“标准”)时区值。这是 this Wikipedia page 的第二列。它来自:
const timeZoneIANA = Intl.DateTimeFormat().resolvedOptions().timeZone;
它返回 IANA 值,即旧金山的 America/Los_Angeles
。这是您应该为用户存储在数据库中的值(如果有的话)。您可以从 timezones.json 包中获取有关它的各种信息,并将时间从/转换到该时区,加上日期(很重要),如 this answer。
IE11呢?
IE11 不支持那个 Intl....
东西。大多数polyfills只是猜测它并且不准确。因此,请尝试放弃对 IE11 的支持,或者使用库并为某些不准确做好准备。
3)我现在只想找出他们的时区偏移量(例如-8:00)
你可能不应该。我想不出你需要这个的任何真正原因。同样,知道今天的这个值,并不能告诉你明天这个值是多少。
无论如何,您总是可以通过
new Date().getTimezoneOffset() / 60
在客户端。
注意:.getTimezoneOffset() 不是日期对象的时区偏移量,您正在调用它。请阅读文档。
准确地说,它采用传递的 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
有图书馆吗?
这是支持时区交换的 a list of libraries。
但是,请记住,您必须先解决设计问题,然后再寻找代码。时区周围的事情很容易变得复杂,您必须知道“PDT”和“GMT-8”不是为用户存储在数据库中的全年信息/有价值的时区值。 :)
JavaScript:
var d = 新日期(); var n = d.getTimezoneOffset(); var timezone = n / -60;控制台.log(时区);
尝试这个 :
new Date().toLocaleString("en-US",Intl.DateTimeFormat().resolvedOptions().timeZone)
这将在您的客户端浏览器上查找 timeZone。
"2/18/2021, 11:04:34 AM"
Date.prototype.toLocaleString
的第二个参数必须是一个对象:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。您正在传递一个字符串。否决。
Intl.DateTimeFormat().resolvedOptions().timeZone
使用 moment.js:
moment().format('zz');
Z
和 ZZ
使用 momentjs,您可以找到当前时区为
console.log(moment().utcOffset()); // (-240, -120, -60, 0, 60, 120, 240 等)
jstimezone 也有很多错误且无人维护 (https://bitbucket.org/pellepim/jstimezonedetect/issues?status=new&status=open)
在 new Date(
) 上,您可以获得偏移量,以获取您可以执行的时区名称:
new Date().toString().replace(/(.*\((.*)\).*)/, '$2');
您会在日期末尾获得 ()
之间的值,即时区的名称。
Date.toString()
不可靠,MDN 解释了原因。我已将他们文档中的相关部分粘贴在 my answer 中。此外,这个答案已经给出了至少 3 次。
这将是我的解决方案:
// For time zone:
const timeZone = /\((.*)\)/.exec(new Date().toString())[1];
// Offset hours:
const offsetHours = new Date().getTimezoneOffset() / 60;
console.log(`${timeZone}, ${offsetHours}hrs`);
es-Mx
(13% 的美国人口讲西班牙语)并且您的代码给出的时区是 'hora estándar del Pacífico'
。 2) 您正在运行的 offsetHours
取决于一年中的时间,而不是全年可靠。 3)您是否从另一个 StackOverflow 答案中复制了此片段并按住鼠标按钮有点长?最后一行是什么?
这可能不是最优雅的解决方案,但它是最通用的。
这使用 Intl.DateTimeFormat
的 timeZoneName
属性
function getTimeZone(zoneName = "long") { // 设置格式化程序 let formatter = new Intl.DateTimeFormat(undefined, { timeZoneName: zoneName }); // 在当前日期运行格式化程序 return formatter.formatToParts(Date.now()) // 从格式化程序中提取实际值,这是我能找到的唯一可靠方法 .find(formatted => formatted.type === " timeZoneName")['value']; } // console.log 每种类型的 (const zoneName of ['short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric']) { console.log(`${zoneName}: ${getTimeZone(zoneName)}`) } /* short: CDT long: 中央夏令时 shortOffset: GMT-5 longOffset: GMT-05:00 shortGeneric: CT longGeneric: 中央时间 */
这不仅得到格式化的 GMT 偏移时间(即 GMT-5),而且得到时区的俗名(即中央夏令时)
这种方法唯一不能做的就是获取 IANA 时区。为此,我推荐 top answer。
据我所知,DateTimeFormat
无法进行自定义格式设置,因此使用了 formatToParts
,这似乎是获得just时区的唯一可靠方法.
需要注意的重要一点是,只有 short
和 long
是在 current ECMAscript specs 中正式定义的,其他 4 个选项是 a proposal 的一部分,即 only somewhat standard,在撰写本文时,在 safari 中明显缺失,尽管 {4 }
new Date().getTimezoneOffset() / 60
会给出。
America/Los_Angeles
是两个不同的东西。 (并且 IANA 时区不仅仅是“城市名称”)。将一个用于另一个可能是 7% 的错误!我建议在这里阅读我的详细答案:stackoverflow.com/a/69961228
使用它来将 OffSet 转换为正数:
var offset = new Date().getTimezoneOffset();
console.log(offset);
this.timeOffSet = offset + (-2*offset);
console.log(this.timeOffSet);
x + (-2*x)
=== -x
。
一旦我完成了这个“简单”的任务并使用了 (new Date()).getTimezoneOffset()
- 这里广泛推荐的方法。但事实证明,解决方案并不完全正确。在我的情况下,由于某些未记录的原因,当 new Date(0)
返回 GMT+0300 时,new Date()
返回 GMT+0200,这是正确的。从那以后我总是用
(new Date(0)).getTimezoneOffset()
以获得正确的时移。
为什么不直接使用:
function timezoneOffset(date: Date) {
return 6000 * ((date.getUTCHours() - date.getHours()) * 60 + ((date.getUTCMinutes() - date.getMinutes())))
}
date.getTimezoneOffset()
是完全受支持 (caniuse.com/mdn-javascript_builtins_date_gettimezoneoffset) 的内置函数,它正是这样做的。 2)您应该始终使用内置函数,因为例如在这里,您在代码的第一个数字中错过了额外的 0
,而您没有注意到它。我希望它不在某处生产。
这是通过将时区传递给函数来查找偏远国家 TimezoneOffset 的解决方案。在此示例中,“亚洲/加尔各答”是时区
function getTimezoneOffset(timezone) {
LocalDate = new Date();
LocalDate.setMilliseconds(0);
const LocalOffset = LocalDate.getTimezoneOffset();
RemoteLocaleStr = LocalDate.toLocaleString('en-US', {timeZone: timezone});
RemoteDate = new Date(RemoteLocaleStr);
diff = (LocalDate.getTime()-RemoteDate.getTime()) / 1000 / 60 ;
RemoteOffset = LocalOffset + diff;
return RemoteOffset;
}
console.log(getTimezoneOffset('Asia/Calcutta'));
-(new Date(new Date().toLocaleString("en-us", { timeZone: "Asia/Calcutta" })) - new Date(new Date().toLocaleString("en-us", { timeZone: "UTC" }))) / 60000
你只需要包含 moment.js 和 jstz.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.6/jstz.min.js"></script>
在那之后
<script>
$(function(){
var currentTimezone = jstz.determine();
var timezone = currentTimezone.name();
alert(timezone);
});
</script>
这对我来说是非常好的工作:
// Translation to offset in Unix Timestamp
let timeZoneOffset = ((new Date().getTimezoneOffset())/60)*3600;
不定期副业成功案例分享