首页 > 解决方案 > 尝试开发 Chrome 扩展以自由更改 TimeZone,但它不起作用

问题描述

我正在开发 chrome 扩展。我想更改 Chrome 网页的时区,但我不能。

我的代码用于覆盖网页 javascript 日期对象。当我将此代码(web-accessible-resources)放在页面上下文中时,页面的时区更改为“Etc/Greenwich”。

我想自由设置保存在 chrome-storage 中的参数并更改页面时区,但我不知道该怎么做。

谁能给我一些建议?

网络可访问资源

(function (o) {
  const convertToGMT = function (n) {
    const format = function (v) {return (v < 10 ? '0' : '') + v};
    return (n <= 0 ? '+' : '-') + format(Math.abs(n) / 60 | 0) + format(Math.abs(n) % 60);
  };
  //
  const resolvedOptions = Intl.DateTimeFormat().resolvedOptions();
  const {
    toJSON, getYear, getMonth, getHours, toString, getMinutes, getSeconds, getUTCMonth, getFullYear, getUTCHours,
    getUTCFullYear, getMilliseconds, getTimezoneOffset, getUTCMilliseconds, toLocaleTimeString, toLocaleDateString,
    toISOString, toGMTString, toUTCString, toTimeString, toDateString, getUTCSeconds, getUTCMinutes, toLocaleString,
    getDay, getUTCDate, getUTCDay, getDate
  } = Date.prototype;
  //
  Object.defineProperty(Date.prototype, '_offset', {'configurable': true, get() {return getTimezoneOffset.call(this)}});
  Object.defineProperty(Date.prototype, '_date', {'configurable': true, get() {
    return this._nd !== undefined ? this._nd : new Date(this.getTime() + (this._offset - o.value) * 60 * 1000);
  }});
  //
  Object.defineProperty(Date.prototype, 'toJSON', {"value": function () {return toJSON.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getDay', {"value": function () {return getDay.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getDate', {"value": function () {return getDate.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getYear', {"value": function () {return getYear.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getTimezoneOffset', {"value": function () {return Number(o.value)}});
  Object.defineProperty(Date.prototype, 'getMonth', {"value": function () {return getMonth.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getHours', {"value": function () {return getHours.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCDay', {"value": function () {return getUTCDay.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCDate', {"value": function () {return getUTCDate.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getMinutes', {"value": function () {return getMinutes.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getSeconds', {"value": function () {return getSeconds.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCMonth', {"value": function () {return getUTCMonth.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCHours', {"value": function () {return getUTCHours.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getFullYear', {"value": function () {return getFullYear.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toISOString', {"value": function () {return toISOString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toGMTString', {"value": function () {return toGMTString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toUTCString', {"value": function () {return toUTCString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toDateString', {"value": function () {return toDateString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toTimeString', {"value": function () {return toTimeString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCSeconds', {"value": function () {return getUTCSeconds.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCMinutes', {"value": function () {return getUTCMinutes.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCFullYear', {"value": function () {return getUTCFullYear.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toLocaleString', {"value": function () {return toLocaleString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getMilliseconds', {"value": function () {return getMilliseconds.call(this._date)}});
  Object.defineProperty(Date.prototype, 'getUTCMilliseconds', {"value": function () {return getUTCMilliseconds.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toLocaleTimeString', {"value": function () {return toLocaleTimeString.call(this._date)}});
  Object.defineProperty(Date.prototype, 'toLocaleDateString', {"value": function () {return toLocaleDateString.call(this._date)}});
  //
  Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {"value": function () {return Object.assign(resolvedOptions, {"timeZone": o.name})}});
  Object.defineProperty(Date.prototype, 'toString', {'value': function () {
    return toString.call(this._date).replace(convertToGMT(this._offset), convertToGMT(o.value)).replace(/\(.*\)/, '(' + o.name.replace(/\//g, ' ') + ' Standard Time)');
  }});
  //
  document.documentElement.dataset.ctzscriptallow = true;
})({'name':'Etc/Greenwich','value':0})

内容脚本

let s = document.createElement('script');
s.src = chrome.runtime.getURL('change-timezone.js');

s.onload = function() {
  this.remove();
};
(document.head || document.documentElement).appendChild(s);

标签: javascriptgoogle-chromegoogle-chrome-extensiontimezonetimezone-offset

解决方案


谁能给我一些建议?

是的,我可以: 这很难。

  • 使用 Chrome 扩展来更改时区的想法是一个很好的想法。我个人会喜欢这个扩展。然而,这并不是一项微不足道的努力。您需要编写的代码量远远超过 StackOverflow 问题的容量。我建议你开始一个 GitHub 项目并寻找合作者。您可能能够就特定问题获得有关 StackOverflow 的帮助,但不太可能针对整个项目。它太宽泛了。

  • 在您的代码中,您执行此操作的位置:

    new Date(this.getTime() + (this._offset - o.value) * 60 * 1000)
    

    这看起来像是试图更改时区,但重要的是你要明白它没有。相反,它所做的是更改嵌入在日期对象中的时间戳。这不会改变时区,它会设置不同的时间点

  • 这种方法被称为“Epoch Shifting”,它被 Moment.js 等库内部使用。他们可以使用这种技术是因为三件事:

    1. 移位的Date对象仅在内部使用,从不直接暴露给最终用户。
    2. 一旦对象被移动,就永远不会使用移动对象上的基于本地时间的函数Date(例如getHours或)。toString仅使用基于 UTC 的函数(例如getUTCHourstoISOString)。
    3. 选择移位的偏移量是转换的偏移量,而不是之前的偏移量。这变得复杂,但在过渡附近很重要。单元测试可以揭示这里的挑战。
  • Epoch Shifting 通常作为时区问题的解决方案提出,但是当考虑Date对象如何工作时,这是幼稚的。首先,请记住,Date对象中的时间戳总是在外部和Date对象中的其他函数中解释为 UTC。此外,Date对象使用的本地时区直接来自底层本机代码(通常通过特定于操作系统的函数),不会暴露给 JavaScript 进行修改。

  • 换句话说,没有 JavaScript 代码可以更改Date对象使用的时区。仅当满足上述所有三点时,Epoch Shifting 才是有效的方法。通常应在用户代码中避免使用它。

  • 除了Date对象的功能之外,您还必须找到一种方法来覆盖Intl.DateTimeFormat,以便默认解析时区是您正在设置的时区。

  • 使用任何方法,您都需要访问时区数据。您可以自带(例如 Moment-Timezone 所做的),或者您可以尝试从 Intl 对象中提取它(例如 Luxon 所做的)。


推荐阅读