增强版时区劫持(改进UI)

更可靠的页面时区修改方法,带有友好的选择界面

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         增强版时区劫持(改进UI)
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  更可靠的页面时区修改方法,带有友好的选择界面
// @author       kendrick
// @match        *://*.sankuai.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 定义可用时区及其偏移量(分钟)
    const TIMEZONES = {
        'default': { name: '系统默认', offset: null },
        'china': { name: '中国 (UTC+8)', offset: -480 },
        'europe': { name: '欧洲 (UTC+1)', offset: -60 },
        'middleeast': { name: '中东 (UTC+3)', offset: -180 }
    };

    // 获取保存的时区设置,默认为系统时区
    const selectedTimezone = GM_getValue('selectedTimezone', 'default');

    // 添加CSS样式
    GM_addStyle(`
        .tz-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 10000;
        }
        .tz-modal {
            background: white;
            border-radius: 8px;
            padding: 20px;
            width: 300px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
        .tz-modal-title {
            font-size: 18px;
            font-weight: bold;
            margin-bottom: 15px;
            text-align: center;
            color: #333;
        }
        .tz-options {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .tz-option {
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.2s;
            background: #f8f8f8;
            text-align: center;
        }
        .tz-option:hover {
            background: #e8f0fe;
            border-color: #4285f4;
        }
        .tz-option.selected {
            background: #e8f0fe;
            border-color: #4285f4;
            position: relative;
        }
        .tz-option.selected::after {
            content: "✓";
            position: absolute;
            right: 10px;
            color: #4285f4;
        }
        .tz-indicator {
            position: fixed;
            bottom: 10px;
            right: 10px;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 5px 10px;
            border-radius: 3px;
            font-size: 12px;
            z-index: 9999;
            cursor: pointer;
            opacity: 0.7;
            transition: opacity 0.3s;
        }
        .tz-indicator:hover {
            opacity: 1;
        }
    `);

    // 如果选择了默认时区,则不进行劫持
    if (selectedTimezone === 'default') {
        setupMenuCommands();
        return;
    }

    // 获取目标时区偏移
    const targetOffset = TIMEZONES[selectedTimezone].offset;

    // 执行注入脚本
    injectScript();

    // 设置菜单命令
    setupMenuCommands();

    // 在页面完全加载后添加时区指示器
    window.addEventListener('load', function() {
        addTimezoneIndicator();
    });

    // 注入脚本到页面
    function injectScript() {
        const scriptText = `
            (function() {
                // 保存原始Date对象
                const OriginalDate = Date;

                // 目标时区偏移(分钟)
                const targetOffset = ${targetOffset};

                // 获取本地时区偏移
                const localOffset = new OriginalDate().getTimezoneOffset();

                // 计算时区差异(分钟)
                const offsetDiff = localOffset - targetOffset;

                // 创建自定义Date构造函数
                function CustomDate() {
                    // 处理不同构造函数调用情况
                    if (arguments.length === 0) {
                        // 无参数调用 - 返回当前时间,但调整为目标时区
                        const date = new OriginalDate();
                        date.setTime(date.getTime() + offsetDiff * 60 * 1000);
                        return date;
                    } else {
                        // 有参数调用 - 正常创建日期对象
                        return new (Function.prototype.bind.apply(
                            OriginalDate,
                            [null].concat(Array.prototype.slice.call(arguments))
                        ))();
                    }
                }

                // 复制静态方法
                CustomDate.UTC = OriginalDate.UTC;
                CustomDate.parse = OriginalDate.parse;

                // 修改now方法以反映目标时区
                CustomDate.now = function() {
                    return OriginalDate.now() + offsetDiff * 60 * 1000;
                };

                // 继承原型
                CustomDate.prototype = OriginalDate.prototype;

                // 修改getTimezoneOffset方法
                const originalGetTimezoneOffset = OriginalDate.prototype.getTimezoneOffset;
                OriginalDate.prototype.getTimezoneOffset = function() {
                    return targetOffset;
                };

                // 覆盖全局Date对象
                window.Date = CustomDate;

                // 输出调试信息
                console.log('[时区修改器] 已将时区修改为: ${TIMEZONES[selectedTimezone].name}');

                // 验证劫持是否成功
                const testDate = new Date();
                console.log('[时区修改器] 当前时间:', testDate.toLocaleString());
                console.log('[时区修改器] 时区偏移:', testDate.getTimezoneOffset(), '分钟');
            })();
        `;

        // 创建脚本元素
        const scriptElement = document.createElement('script');
        scriptElement.textContent = scriptText;

        // 确保脚本尽早执行
        if (document.documentElement) {
            document.documentElement.appendChild(scriptElement);
            document.documentElement.removeChild(scriptElement);
        } else {
            // 如果documentElement不可用,则等待它可用
            const observer = new MutationObserver(function(mutations, obs) {
                if (document.documentElement) {
                    document.documentElement.appendChild(scriptElement);
                    document.documentElement.removeChild(scriptElement);
                    obs.disconnect();
                }
            });

            observer.observe(document, {
                childList: true,
                subtree: true
            });
        }
    }

    // 创建时区选择器对话框
    function createTimezoneSelector() {
        // 创建已有的模态框覆盖层
        if (document.querySelector('.tz-modal-overlay')) {
            return;
        }

        const overlay = document.createElement('div');
        overlay.className = 'tz-modal-overlay';

        const modal = document.createElement('div');
        modal.className = 'tz-modal';

        const title = document.createElement('div');
        title.className = 'tz-modal-title';
        title.textContent = '选择时区';

        const options = document.createElement('div');
        options.className = 'tz-options';

        // 创建时区选项
        for (const [key, value] of Object.entries(TIMEZONES)) {
            const option = document.createElement('div');
            option.className = 'tz-option';
            if (key === selectedTimezone) {
                option.classList.add('selected');
            }
            option.textContent = value.name;
            option.dataset.timezone = key;

            option.addEventListener('click', function() {
                const timezone = this.dataset.timezone;
                GM_setValue('selectedTimezone', timezone);
                overlay.remove();
                alert(`已将时区设置为: ${TIMEZONES[timezone].name}\n请刷新页面以应用更改`);
                location.reload();
            });

            options.appendChild(option);
        }

        // 组装模态框
        modal.appendChild(title);
        modal.appendChild(options);
        overlay.appendChild(modal);

        // 点击覆盖层关闭模态框
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) {
                overlay.remove();
            }
        });

        document.body.appendChild(overlay);
    }

    // 设置油猴菜单命令
    function setupMenuCommands() {
        // 时区选择菜单
        GM_registerMenuCommand("选择时区", function() {
            if (document.body) {
                createTimezoneSelector();
            } else {
                // 如果body还不存在,等待DOM加载完成
                window.addEventListener('DOMContentLoaded', createTimezoneSelector);
            }
        });

        // 显示当前时区设置
        GM_registerMenuCommand(`当前时区: ${TIMEZONES[selectedTimezone].name}`, function(){});
    }

    // 添加时区指示器
    function addTimezoneIndicator() {
        if (selectedTimezone === 'default') return;

        const indicator = document.createElement('div');
        indicator.textContent = `🌐 ${TIMEZONES[selectedTimezone].name}`;
        indicator.className = 'tz-indicator';

        indicator.addEventListener('click', function() {
            createTimezoneSelector();
        });

        document.body.appendChild(indicator);
    }
})();