{"id":10357,"date":"2026-02-10T11:55:32","date_gmt":"2026-02-10T02:55:32","guid":{"rendered":"https:\/\/rakkokeyword.com\/techo\/?p=10357"},"modified":"2026-02-10T11:56:29","modified_gmt":"2026-02-10T02:56:29","slug":"tool-visual-timezone-converter","status":"publish","type":"post","link":"https:\/\/rakkokeyword.com\/techo\/tool-visual-timezone-converter\/","title":{"rendered":"\u6642\u5dee\u8a08\u7b97\u30c4\u30fc\u30eb"},"content":{"rendered":"\n<div id=\"visual_timezone_tool\">\n    <style>\n        \/* --- Base & Reset --- *\/\n        #visual_timezone_tool {\n            width: 100%;\n            max-width: 600px;\n            margin: 0 auto;\n            font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n            color: #333;\n            background: #fff;\n            box-sizing: border-box;\n            user-select: none;\n        }\n        #visual_timezone_tool * { box-sizing: border-box; }\n        \n        .vtt_card {\n            border: 1px solid #e0e0e0;\n            border-radius: 16px;\n            padding: 20px 20px 15px 20px;\n            box-shadow: 0 4px 20px rgba(0,0,0,0.05);\n            background: #fff;\n        }\n\n        .vtt_row {\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-end;\n            padding: 5px 0;\n            position: relative;\n        }\n        \n        .vtt_city_area {\n            flex: 1;\n            display: flex;\n            flex-direction: column;\n            justify-content: center;\n        }\n\n        .vtt_select_wrapper {\n            position: relative;\n            display: inline-block;\n            max-width: 220px;\n        }\n        .vtt_select {\n            appearance: none;\n            -webkit-appearance: none;\n            border: 1px solid transparent;\n            background: transparent;\n            font-size: 16px;\n            font-weight: bold;\n            color: #13284B;\n            padding: 5px 24px 5px 0;\n            cursor: pointer;\n            width: 100%;\n            border-radius: 4px;\n            transition: background 0.2s;\n        }\n        .vtt_select:hover {\n            background: #f0f0f0;\n            border-color: #ddd;\n            padding-left: 5px;\n        }\n        .vtt_select_wrapper::after {\n            content: '';\n            position: absolute;\n            right: 5px;\n            top: 55%;\n            transform: translateY(-50%);\n            border-left: 4px solid transparent;\n            border-right: 4px solid transparent;\n            border-top: 5px solid #13284B;\n            pointer-events: none;\n        }\n\n        .vtt_date_badge {\n            font-size: 12px;\n            font-weight: bold;\n            color: #555;\n            background: #f0f2f5;\n            padding: 2px 8px;\n            border-radius: 10px;\n            display: inline-block;\n            margin-top: 2px;\n            align-self: flex-start;\n        }\n\n        .vtt_time_display {\n            text-align: right;\n            min-width: 90px;\n        }\n        .vtt_time_main {\n            font-size: 32px;\n            font-weight: 700;\n            font-feature-settings: \"tnum\";\n            letter-spacing: -1px;\n            color: #111;\n            line-height: 1;\n            user-select: text;\n        }\n        .vtt_time_sub {\n            font-size: 11px;\n            color: #888;\n            margin-top: 4px;\n        }\n\n        \/* --- Slider Area --- *\/\n        .vtt_slider_container {\n            margin: 20px 0 32px 0;\n            position: relative;\n            padding: 16px 14px 0 14px;\n            height: 72px;\n        }\n        \n        .vtt_tracks_wrapper {\n            position: absolute;\n            top: 50%; left: 14px; right: 14px;\n            transform: translateY(-50%);\n            display: flex;\n            flex-direction: column;\n            gap: 6px;\n            z-index: 1;\n        }\n        \n        .vtt_track_row {\n            display: flex;\n            align-items: center;\n        }\n        \n        .vtt_track_line {\n            flex: 1;\n            height: 10px;\n            border-radius: 5px;\n            background: #ddd;\n            position: relative;\n            overflow: hidden;\n        }\n\n        \/* Now marker \u2014 position set by JS via CSS variable *\/\n        .vtt_connector {\n            position: absolute;\n            top: 0; bottom: -6px;\n            width: 2px;\n            background: rgba(0,0,0,0.18);\n            z-index: 4;\n            pointer-events: none;\n        }\n        .vtt_connector::before {\n            content: '\u73fe\u5728';\n            position: absolute;\n            top: -2px;\n            left: 50%;\n            transform: translateX(-50%);\n            font-size: 9px;\n            color: #999;\n            white-space: nowrap;\n        }\n\n        \/* Date boundary markers *\/\n        .vtt_date_markers_container {\n            position: absolute;\n            left: 14px; right: 14px;\n            top: 0; bottom: 0;\n            pointer-events: none;\n            z-index: 3;\n        }\n        .vtt_date_marker {\n            position: absolute;\n            width: 0;\n            border-left: 1px dashed rgba(0,0,0,0.22);\n            pointer-events: none;\n        }\n        .vtt_date_markers_base .vtt_date_marker {\n            top: 12px;\n            bottom: 50%;\n        }\n        .vtt_date_markers_base .vtt_date_marker_label {\n            position: absolute;\n            top: -13px;\n            left: 50%;\n            transform: translateX(-50%);\n            font-size: 8px;\n            color: #aaa;\n            white-space: nowrap;\n            font-weight: bold;\n        }\n        .vtt_date_markers_target .vtt_date_marker {\n            top: 50%;\n            bottom: -6px;\n        }\n        .vtt_date_markers_target .vtt_date_marker_label {\n            position: absolute;\n            bottom: -13px;\n            left: 50%;\n            transform: translateX(-50%);\n            font-size: 8px;\n            color: #aaa;\n            white-space: nowrap;\n            font-weight: bold;\n        }\n\n        \/* Range Input *\/\n        .vtt_range_input {\n            position: absolute;\n            top: 0; left: 0; right: 0; bottom: 0;\n            width: 100%;\n            height: 100%;\n            margin: 0;\n            -webkit-appearance: none;\n            background: transparent;\n            z-index: 5;\n            cursor: grab;\n        }\n        .vtt_range_input:active { cursor: grabbing; }\n        \n        .vtt_range_input::-webkit-slider-thumb {\n            -webkit-appearance: none;\n            height: 40px;\n            width: 14px;\n            border-radius: 4px;\n            background: #fff;\n            border: 2px solid #333;\n            box-shadow: 0 2px 6px rgba(0,0,0,0.3);\n            cursor: grab;\n            margin-top: 0; \n        }\n        .vtt_range_input::-moz-range-thumb {\n            height: 40px;\n            width: 14px;\n            border-radius: 4px;\n            background: #fff;\n            border: 2px solid #333;\n            box-shadow: 0 2px 6px rgba(0,0,0,0.3);\n            cursor: grab;\n        }\n\n        \/* --- Footer --- *\/\n        .vtt_footer {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            border-top: 1px solid #f0f0f0;\n            padding-top: 15px;\n            margin-top: 5px;\n            flex-wrap: wrap;\n            gap: 8px;\n        }\n        .vtt_btn_group {\n            display: flex;\n            gap: 8px;\n            flex-wrap: wrap;\n        }\n        .vtt_btn {\n            background: #f0f2f5;\n            border: 1px solid transparent;\n            padding: 8px 12px;\n            border-radius: 6px;\n            font-size: 13px;\n            font-weight: bold;\n            color: #555;\n            cursor: pointer;\n            transition: 0.2s;\n            text-decoration: none;\n        }\n        .vtt_btn:hover {\n            background: #e4e6e9;\n            color: #111;\n        }\n        .vtt_btn.vtt_btn_active {\n            background: #13284B;\n            color: #fff;\n        }\n        .vtt_diff_badge {\n            font-size: 13px;\n            font-weight: bold;\n            color: #13284B;\n        }\n\n        \/* --- DateTime Picker Panel --- *\/\n        .vtt_picker_panel {\n            display: none;\n            margin-top: 12px;\n            padding: 14px;\n            background: #f8f9fb;\n            border-radius: 10px;\n            border: 1px solid #e0e0e0;\n        }\n        .vtt_picker_panel.vtt_open { display: block; }\n        .vtt_picker_row {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            flex-wrap: wrap;\n        }\n        .vtt_picker_label {\n            font-size: 12px;\n            font-weight: bold;\n            color: #555;\n            min-width: 80px;\n        }\n        .vtt_picker_input {\n            font-family: inherit;\n            font-size: 14px;\n            padding: 6px 10px;\n            border: 1px solid #ccc;\n            border-radius: 6px;\n            background: #fff;\n            color: #333;\n        }\n        .vtt_picker_input:focus {\n            outline: none;\n            border-color: #13284B;\n        }\n        .vtt_picker_actions {\n            display: flex;\n            gap: 8px;\n            margin-top: 10px;\n        }\n        .vtt_picker_apply {\n            background: #13284B;\n            color: #fff;\n            border: none;\n            padding: 7px 16px;\n            border-radius: 6px;\n            font-size: 13px;\n            font-weight: bold;\n            cursor: pointer;\n        }\n        .vtt_picker_apply:hover { opacity: 0.85; }\n\n        \/* Legend *\/\n        .vtt_legend {\n            margin-top: 15px;\n            text-align: center;\n            font-size: 11px;\n            color: #888;\n            line-height: 1.6;\n        }\n        .vtt_dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 3px; vertical-align: middle; }\n        .vtt_bg_day { background-color: #FFB74D; }\n        .vtt_bg_night { background-color: #3F51B5; }\n        .vtt_bg_mid { background: linear-gradient(90deg, #3F51B5, #FFB74D); }\n\n        @media (max-width: 400px) {\n            .vtt_time_main { font-size: 26px; }\n            .vtt_btn { font-size: 11px; padding: 7px 6px; }\n        }\n    <\/style>\n\n    <div class=\"vtt_card\">\n        \n        <div class=\"vtt_row\">\n            <div class=\"vtt_city_area\">\n                <div class=\"vtt_select_wrapper\">\n                    <select id=\"vtt_select_base\" class=\"vtt_select\" aria-label=\"\u5909\u63db\u5143\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\"><\/select>\n                <\/div>\n                <span id=\"vtt_date_base\" class=\"vtt_date_badge\">\u2013\/\u2013<\/span>\n            <\/div>\n            <div class=\"vtt_time_display\">\n                <div id=\"vtt_time_base\" class=\"vtt_time_main\">\u2013:\u2013<\/div>\n                <div class=\"vtt_time_sub\">\u5909\u63db\u5143<\/div>\n            <\/div>\n        <\/div>\n\n        <div class=\"vtt_slider_container\">\n            <div id=\"vtt_now_marker\" class=\"vtt_connector\"><\/div>\n            <div id=\"vtt_date_markers_base\" class=\"vtt_date_markers_container vtt_date_markers_base\"><\/div>\n            <div id=\"vtt_date_markers_target\" class=\"vtt_date_markers_container vtt_date_markers_target\"><\/div>\n            <div class=\"vtt_tracks_wrapper\">\n                <div class=\"vtt_track_row\">\n                    <div id=\"vtt_track_base\" class=\"vtt_track_line\"><\/div>\n                <\/div>\n                <div class=\"vtt_track_row\">\n                    <div id=\"vtt_track_target\" class=\"vtt_track_line\"><\/div>\n                <\/div>\n            <\/div>\n            <input type=\"range\" id=\"vtt_slider\" class=\"vtt_range_input\" min=\"-720\" max=\"1440\" value=\"0\" step=\"15\" aria-label=\"\u6642\u523b\u30b9\u30e9\u30a4\u30c0\u30fc\">\n        <\/div>\n\n        <div class=\"vtt_row\">\n            <div class=\"vtt_city_area\">\n                <div class=\"vtt_select_wrapper\">\n                    <select id=\"vtt_select_target\" class=\"vtt_select\" aria-label=\"\u5909\u63db\u5148\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\"><\/select>\n                <\/div>\n                <span id=\"vtt_date_target\" class=\"vtt_date_badge\">\u2013\/\u2013<\/span>\n            <\/div>\n            <div class=\"vtt_time_display\">\n                <div id=\"vtt_time_target\" class=\"vtt_time_main\">\u2013:\u2013<\/div>\n                <div class=\"vtt_time_sub\">\u5909\u63db\u5148<\/div>\n            <\/div>\n        <\/div>\n\n        <div class=\"vtt_footer\">\n            <div class=\"vtt_btn_group\">\n                <button id=\"vtt_btn_reset\" class=\"vtt_btn\">\u73fe\u5728\u6642\u523b\u306b\u623b\u3059<\/button>\n                <button id=\"vtt_btn_swap\" class=\"vtt_btn\">\u4e0a\u4e0b\u3092\u5165\u308c\u66ff\u3048<\/button>\n                <button id=\"vtt_btn_pick\" class=\"vtt_btn\">\u65e5\u6642\u3092\u6307\u5b9a<\/button>\n            <\/div>\n            <span id=\"vtt_diff_info\" class=\"vtt_diff_badge\">\u6642\u5dee: \u2013\u6642\u9593<\/span>\n        <\/div>\n\n        <div id=\"vtt_picker_panel\" class=\"vtt_picker_panel\">\n            <div class=\"vtt_picker_row\">\n                <span class=\"vtt_picker_label\">\u5909\u63db\u5143\u306e\u65e5\u6642<\/span>\n                <input type=\"date\" id=\"vtt_pick_date\" class=\"vtt_picker_input\">\n                <input type=\"time\" id=\"vtt_pick_time\" class=\"vtt_picker_input\">\n            <\/div>\n            <div class=\"vtt_picker_actions\">\n                <button id=\"vtt_pick_apply\" class=\"vtt_picker_apply\">\u3053\u306e\u65e5\u6642\u3067\u8a08\u7b97<\/button>\n                <button id=\"vtt_pick_cancel\" class=\"vtt_btn\">\u30ad\u30e3\u30f3\u30bb\u30eb<\/button>\n            <\/div>\n        <\/div>\n\n        <div class=\"vtt_legend\">\n            <span class=\"vtt_dot vtt_bg_day\"><\/span>\u9023\u7d61OK\uff089\u301c18\u6642\uff09\u3000\n            <span class=\"vtt_dot vtt_bg_mid\"><\/span>\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8\u3000\n            <span class=\"vtt_dot vtt_bg_night\"><\/span>\u7761\u7720\u4e2d\uff0822\u301c7\u6642\uff09\n        <\/div>\n    <\/div>\n\n    <script>\n    (function() {\n        \/* ========== Config ========== *\/\n        const CITIES = [\n            { id: \"Asia\/Tokyo\", name: \"\ud83c\uddef\ud83c\uddf5 \u65e5\u672c (\u6771\u4eac)\" },\n            { id: \"America\/New_York\", name: \"\ud83c\uddfa\ud83c\uddf8 \u30cb\u30e5\u30fc\u30e8\u30fc\u30af\" },\n            { id: \"America\/Los_Angeles\", name: \"\ud83c\uddfa\ud83c\uddf8 \u30ed\u30b5\u30f3\u30bc\u30eb\u30b9\" },\n            { id: \"Europe\/London\", name: \"\ud83c\uddec\ud83c\udde7 \u30ed\u30f3\u30c9\u30f3\" },\n            { id: \"Europe\/Paris\", name: \"\ud83c\uddeb\ud83c\uddf7 \u30d1\u30ea\" },\n            { id: \"Asia\/Shanghai\", name: \"\ud83c\udde8\ud83c\uddf3 \u5317\u4eac \/ \u4e0a\u6d77\" },\n            { id: \"Asia\/Seoul\", name: \"\ud83c\uddf0\ud83c\uddf7 \u30bd\u30a6\u30eb\" },\n            { id: \"Asia\/Singapore\", name: \"\ud83c\uddf8\ud83c\uddec \u30b7\u30f3\u30ac\u30dd\u30fc\u30eb\" },\n            { id: \"Australia\/Sydney\", name: \"\ud83c\udde6\ud83c\uddfa \u30b7\u30c9\u30cb\u30fc\" },\n            { id: \"Pacific\/Honolulu\", name: \"\ud83c\uddfa\ud83c\uddf8 \u30cf\u30ef\u30a4\" },\n            { id: \"Asia\/Bangkok\", name: \"\ud83c\uddf9\ud83c\udded \u30d0\u30f3\u30b3\u30af\" },\n            { id: \"Europe\/Berlin\", name: \"\ud83c\udde9\ud83c\uddea \u30d9\u30eb\u30ea\u30f3\" },\n            { id: \"Asia\/Dubai\", name: \"\ud83c\udde6\ud83c\uddea \u30c9\u30d0\u30a4\" },\n            { id: \"Asia\/Kolkata\", name: \"\ud83c\uddee\ud83c\uddf3 \u30a4\u30f3\u30c9\" },\n            { id: \"America\/Sao_Paulo\", name: \"\ud83c\udde7\ud83c\uddf7 \u30b5\u30f3\u30d1\u30a6\u30ed\" }\n        ];\n\n        const C_NIGHT = [63, 81, 181];\n        const C_DAY   = [255, 183, 77];\n        const WAKE_START   = 7;\n        const ACTIVE_START = 9;\n        const ACTIVE_END   = 18;\n        const SLEEP_START  = 22;\n        const WINDOW_PAST   = 12;\n        const WINDOW_FUTURE = 24;\n        const WINDOW_HOURS  = WINDOW_PAST + WINDOW_FUTURE; \/\/ 36\n        const SLIDER_PADDING = 14; \/\/ px, matches CSS padding\n\n        \/* ========== Elements ========== *\/\n        const elSelBase     = document.getElementById('vtt_select_base');\n        const elSelTarget   = document.getElementById('vtt_select_target');\n        const elTimeBase    = document.getElementById('vtt_time_base');\n        const elDateBase    = document.getElementById('vtt_date_base');\n        const elTimeTarget  = document.getElementById('vtt_time_target');\n        const elDateTarget  = document.getElementById('vtt_date_target');\n        const elSlider      = document.getElementById('vtt_slider');\n        const elTrackBase   = document.getElementById('vtt_track_base');\n        const elTrackTarget = document.getElementById('vtt_track_target');\n        const elBtnReset    = document.getElementById('vtt_btn_reset');\n        const elBtnSwap     = document.getElementById('vtt_btn_swap');\n        const elBtnPick     = document.getElementById('vtt_btn_pick');\n        const elDiffInfo    = document.getElementById('vtt_diff_info');\n        const elPickerPanel = document.getElementById('vtt_picker_panel');\n        const elPickDate    = document.getElementById('vtt_pick_date');\n        const elPickTime    = document.getElementById('vtt_pick_time');\n        const elPickApply   = document.getElementById('vtt_pick_apply');\n        const elPickCancel  = document.getElementById('vtt_pick_cancel');\n        const elMarkersBase   = document.getElementById('vtt_date_markers_base');\n        const elMarkersTarget = document.getElementById('vtt_date_markers_target');\n        const elNowMarker     = document.getElementById('vtt_now_marker');\n\n        \/* ========== State ========== *\/\n        let baseNow    = new Date();\n        let offsetMins = 0;\n        let isLive     = true;   \/\/ Fix #1: live-follow flag\n        let bgDirty    = true;   \/\/ Perf: only repaint heavy stuff when needed\n\n        function markBgDirty() { bgDirty = true; }\n\n        \/* ========== Intl Formatter Cache ========== *\/\n        const fmtCache = new Map();\n        function getFormatter(zone) {\n            if (!fmtCache.has(zone)) {\n                fmtCache.set(zone, new Intl.DateTimeFormat('en-US', {\n                    timeZone: zone,\n                    year: 'numeric', month: '2-digit', day: '2-digit',\n                    hour: '2-digit', minute: '2-digit', second: '2-digit',\n                    hourCycle: 'h23'\n                }));\n            }\n            return fmtCache.get(zone);\n        }\n\n        function getZoneParts(dateObj, zone) {\n            const parts = getFormatter(zone).formatToParts(dateObj);\n            const map = {};\n            for (const p of parts) {\n                if (p.type !== 'literal') map[p.type] = p.value;\n            }\n            return {\n                y: +map.year, m: +map.month, d: +map.day,\n                h: +map.hour, min: +map.minute, sec: +map.second\n            };\n        }\n\n        \/* Fix #2: DST-safe UTC offset calculation *\/\n        function getOffsetMinutes(dateObj, zone) {\n            const z = getZoneParts(dateObj, zone);\n            const asUTC = Date.UTC(z.y, z.m - 1, z.d, z.h, z.min, z.sec);\n            return (asUTC - dateObj.getTime()) \/ 60000;\n        }\n\n        \/* Fix #3: Robust local-time-in-zone \u2192 Date conversion *\/\n        function zonedLocalToDate(local, zone) {\n            let utcMs = Date.UTC(local.y, local.m - 1, local.d, local.h, local.min, 0);\n\n            for (let i = 0; i < 6; i++) {\n                const guess = new Date(utcMs);\n                const offMin = getOffsetMinutes(guess, zone);\n                const nextUtcMs = Date.UTC(local.y, local.m - 1, local.d, local.h, local.min, 0) - offMin * 60000;\n                if (Math.abs(nextUtcMs - utcMs) < 500) {\n                    utcMs = nextUtcMs;\n                    break;\n                }\n                utcMs = nextUtcMs;\n            }\n\n            const result = new Date(utcMs);\n            const z = getZoneParts(result, zone);\n            const ok = (z.y === local.y &#038;&#038; z.m === local.m &#038;&#038; z.d === local.d\n                     &#038;&#038; z.h === local.h &#038;&#038; z.min === local.min);\n            return { date: result, ok };\n        }\n\n        \/* ========== Init ========== *\/\n        function init() {\n            const opts = CITIES.map(c => `<option value=\"${c.id}\">${c.name}<\/option>`).join('');\n            elSelBase.innerHTML = opts;\n            elSelTarget.innerHTML = opts;\n            \n            elSelBase.value = \"Asia\/Tokyo\";\n            elSelTarget.value = \"America\/New_York\";\n\n            elSelBase.addEventListener('change', () => { markBgDirty(); updateDisplay(); });\n            elSelTarget.addEventListener('change', () => { markBgDirty(); updateDisplay(); });\n            elBtnSwap.addEventListener('click', swap);\n\n            \/\/ Slider with rAF throttle\n            let rafPending = false;\n            elSlider.addEventListener('input', () => {\n                offsetMins = parseInt(elSlider.value, 10);\n                if (!rafPending) {\n                    rafPending = true;\n                    requestAnimationFrame(() => {\n                        updateDisplay();\n                        rafPending = false;\n                    });\n                }\n            });\n            \n            elBtnReset.addEventListener('click', reset);\n            elBtnPick.addEventListener('click', togglePicker);\n            elPickCancel.addEventListener('click', closePicker);\n            elPickApply.addEventListener('click', applyPicker);\n\n            \/\/ Fix #1: only auto-update when isLive\n            setInterval(() => {\n                if (isLive && offsetMins === 0) {\n                    baseNow = new Date();\n                    markBgDirty();\n                    updateDisplay();\n                }\n            }, 60000);\n\n            \/\/ Set \"now\" marker position from JS\n            updateNowMarkerPosition();\n\n            reset();\n        }\n\n        \/* ========== Now Marker Position (from JS, not hardcoded CSS) ========== *\/\n        function updateNowMarkerPosition() {\n            const nowPct = WINDOW_PAST \/ WINDOW_HOURS; \/\/ 12\/36 = 0.3333\n            elNowMarker.style.left = `calc(${SLIDER_PADDING}px + (100% - ${SLIDER_PADDING * 2}px) * ${nowPct})`;\n        }\n\n        \/* ========== Actions ========== *\/\n        function reset() {\n            isLive = true;\n            baseNow = new Date();\n            offsetMins = 0;\n            elSlider.value = 0;\n            closePicker();\n            markBgDirty();\n            updateDisplay();\n        }\n\n        function swap() {\n            const tmp = elSelBase.value;\n            elSelBase.value = elSelTarget.value;\n            elSelTarget.value = tmp;\n            markBgDirty();\n            updateDisplay();\n        }\n\n        \/* ========== DateTime Picker ========== *\/\n        function togglePicker() {\n            const isOpen = elPickerPanel.classList.toggle('vtt_open');\n            elBtnPick.classList.toggle('vtt_btn_active', isOpen);\n            if (isOpen) {\n                const vDate = new Date(baseNow.getTime() + offsetMins * 60000);\n                const bZone = elSelBase.value;\n                const b = getZoneParts(vDate, bZone);\n                elPickDate.value = `${b.y}-${String(b.m).padStart(2,'0')}-${String(b.d).padStart(2,'0')}`;\n                elPickTime.value = `${String(b.h).padStart(2,'0')}:${String(b.min).padStart(2,'0')}`;\n            }\n        }\n\n        function closePicker() {\n            elPickerPanel.classList.remove('vtt_open');\n            elBtnPick.classList.remove('vtt_btn_active');\n        }\n\n        function applyPicker() {\n            const dateVal = elPickDate.value;\n            const timeVal = elPickTime.value;\n            if (!dateVal || !timeVal) return;\n\n            const [y, mo, d] = dateVal.split('-').map(Number);\n            const [h, mi] = timeVal.split(':').map(Number);\n            const bZone = elSelBase.value;\n\n            \/\/ Fix #3: robust conversion\n            const { date, ok } = zonedLocalToDate({ y, m: mo, d, h, min: mi }, bZone);\n            baseNow = date;\n\n            \/\/ Fix #1: stop live-follow\n            isLive = false;\n            offsetMins = 0;\n            elSlider.value = 0;\n            closePicker();\n            markBgDirty();\n            updateDisplay();\n        }\n\n        \/* ========== Gradient Logic ========== *\/\n        function getIntensity(rawHour) {\n            const h = ((rawHour % 24) + 24) % 24;\n            if (h >= ACTIVE_START && h <= ACTIVE_END) return 1;\n            if (h >= SLEEP_START || h <= WAKE_START) return 0;\n            if (h > WAKE_START && h < ACTIVE_START) {\n                return (h - WAKE_START) \/ (ACTIVE_START - WAKE_START);\n            }\n            if (h > ACTIVE_END && h < SLEEP_START) {\n                return 1 - (h - ACTIVE_END) \/ (SLEEP_START - ACTIVE_END);\n            }\n            return 0;\n        }\n\n        function blendColor(t) {\n            const r = Math.round(C_NIGHT[0] + (C_DAY[0] - C_NIGHT[0]) * t);\n            const g = Math.round(C_NIGHT[1] + (C_DAY[1] - C_NIGHT[1]) * t);\n            const b = Math.round(C_NIGHT[2] + (C_DAY[2] - C_NIGHT[2]) * t);\n            return `rgb(${r},${g},${b})`;\n        }\n\n        function updateTrack(el, zone) {\n            const nowData = getZoneParts(baseNow, zone);\n            const currentHour = nowData.h + (nowData.min \/ 60);\n            const windowStart = currentHour - WINDOW_PAST;\n\n            const NUM_SAMPLES = 72;\n            const stops = [];\n\n            for (let i = 0; i <= NUM_SAMPLES; i++) {\n                const h = windowStart + (i \/ NUM_SAMPLES) * WINDOW_HOURS;\n                const pct = ((i \/ NUM_SAMPLES) * 100).toFixed(1);\n                const intensity = getIntensity(h);\n                stops.push(`${blendColor(intensity)} ${pct}%`);\n            }\n\n            el.style.background = `linear-gradient(90deg, ${stops.join(', ')})`;\n        }\n\n        \/* ========== Date Boundary Markers ========== *\/\n        function renderMidnights(container, zone) {\n            container.innerHTML = '';\n            const nowData = getZoneParts(baseNow, zone);\n            const currentHour = nowData.h + (nowData.min \/ 60);\n            const windowStart = currentHour - WINDOW_PAST;\n            const windowEnd   = windowStart + WINDOW_HOURS;\n\n            const firstMidnight = Math.ceil(windowStart \/ 24) * 24;\n\n            for (let mh = firstMidnight; mh < windowEnd; mh += 24) {\n                if (mh <= windowStart) continue;\n                const pct = ((mh - windowStart) \/ WINDOW_HOURS) * 100;\n\n                const hoursFromNow = mh - currentHour;\n                const midnightDate = new Date(baseNow.getTime() + hoursFromNow * 3600000);\n                const mData = getZoneParts(midnightDate, zone);\n\n                const marker = document.createElement('div');\n                marker.className = 'vtt_date_marker';\n                marker.style.left = pct + '%';\n\n                const label = document.createElement('span');\n                label.className = 'vtt_date_marker_label';\n                label.textContent = `${mData.m}\/${mData.d}`;\n                marker.appendChild(label);\n\n                container.appendChild(marker);\n            }\n        }\n\n        function updateDateMarkers() {\n            renderMidnights(elMarkersBase, elSelBase.value);\n            renderMidnights(elMarkersTarget, elSelTarget.value);\n        }\n\n        \/* ========== Helpers ========== *\/\n        function formatTime(h, m) {\n            return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}`;\n        }\n\n        \/* ========== Main Update ========== *\/\n        function updateDisplay() {\n            const vDate = new Date(baseNow.getTime() + offsetMins * 60000);\n            const bZone = elSelBase.value;\n            const tZone = elSelTarget.value;\n            \n            \/\/ Light: time &#038; date labels\n            const b = getZoneParts(vDate, bZone);\n            elTimeBase.textContent = formatTime(b.h, b.min);\n            elDateBase.textContent = `${b.m}\/${b.d}`;\n            \n            const t = getZoneParts(vDate, tZone);\n            elTimeTarget.textContent = formatTime(t.h, t.min);\n            elDateTarget.textContent = `${t.m}\/${t.d}`;\n\n            \/\/ Fix #2: DST-aware diff based on vDate\n            const baseOff   = getOffsetMinutes(vDate, bZone);\n            const targetOff = getOffsetMinutes(vDate, tZone);\n            let diff = (targetOff - baseOff) \/ 60;\n            diff = Math.round(diff * 10) \/ 10;\n            const sign = diff >= 0 ? '+' : '';\n            elDiffInfo.textContent = `\u6642\u5dee: ${sign}${diff}\u6642\u9593`;\n\n            \/\/ Slider aria\n            elSlider.setAttribute('aria-valuetext',\n                `${b.m}\/${b.d} ${formatTime(b.h, b.min)} \u2192 ${t.m}\/${t.d} ${formatTime(t.h, t.min)}`);\n\n            \/\/ Heavy: tracks & markers only when dirty\n            if (bgDirty) {\n                updateTrack(elTrackBase, bZone);\n                updateTrack(elTrackTarget, tZone);\n                updateDateMarkers();\n                bgDirty = false;\n            }\n        }\n\n        \/* ========== Boot ========== *\/\n        if (document.readyState === 'loading') {\n            document.addEventListener('DOMContentLoaded', init);\n        } else {\n            init();\n        }\n    })();\n    <\/script>\n<\/div>\n<br>\n\n\n\n<p>\u65e5\u672c\u3068\u6d77\u5916\u306e\u300c\u73fe\u5728\u6642\u523b\u300d\u3068\u300c\u6642\u5dee\u300d\u3092\u77ac\u6642\u306b\u8a08\u7b97\u3057\u3001<strong><span class=\"keiko_yellow\">\u4e92\u3044\u306e\u6d3b\u52d5\u6642\u9593\u304c\u91cd\u306a\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u76f4\u611f\u7684\u306b\u63a2\u305b\u308b<\/span><\/strong>\u7121\u6599\u306eWEB\u30c4\u30fc\u30eb\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-0\">\u3053\u306e\u30c4\u30fc\u30eb\u3067\u3067\u304d\u308b\u3053\u3068<\/h3>\n\n\n\n<p>\u3053\u306e\u30c4\u30fc\u30eb\u306f\u3001\u5358\u306a\u308b\u6642\u523b\u5909\u63db\u3060\u3051\u3067\u306a\u304f\u3001\u8996\u899a\u7684\u306b\u300c\u6642\u9593\u306e\u6d41\u308c\u300d\u3092\u628a\u63e1\u3067\u304d\u308b\u306e\u304c\u7279\u5fb4\u3067\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>2\u90fd\u5e02\u9593\u306e\u6642\u523b\u3092\u4e26\u5217\u8868\u793a<\/strong> <br>\u65e5\u672c\uff08\u307e\u305f\u306f\u4efb\u610f\u306e\u90fd\u5e02\uff09\u3068\u3001\u76f8\u624b\u56fd\u306e\u73fe\u5728\u6642\u523b\u3092\u4e0a\u4e0b\u306b\u4e26\u3079\u3066\u6bd4\u8f03\u3067\u304d\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u30b9\u30e9\u30a4\u30c0\u30fc\u3067\u672a\u6765\u30fb\u904e\u53bb\u3092\u30b7\u30df\u30e5\u30ec\u30fc\u30b7\u30e7\u30f3<\/strong> <br>\u4e2d\u592e\u306e\u30b9\u30e9\u30a4\u30c0\u30fc\u3092\u5de6\u53f3\u306b\u52d5\u304b\u3059\u3060\u3051\u3067\u3001\u6642\u9593\u3092\u9032\u3081\u305f\u308a\u623b\u3057\u305f\u308a\u3067\u304d\u307e\u3059\u3002\u300c\u65e5\u672c\u306e\u660e\u65e5\u306e\u671d9\u6642\u306f\u3001\u30cb\u30e5\u30fc\u30e8\u30fc\u30af\u3067\u4f55\u6642\uff1f\u300d\u3068\u3044\u3063\u305f\u7591\u554f\u304c\u8a08\u7b97\u4e0d\u8981\u3067\u89e3\u6c7a\u3057\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u9023\u7d61\u306b\u6700\u9069\u306a\u300c\u91cd\u306a\u308b\u6642\u9593\u300d\u306e\u767a\u898b<\/strong> <br>\u65e5\u4e2d\uff089\u6642\u301c18\u6642\uff09\u306f\u30aa\u30ec\u30f3\u30b8\u8272\u3001\u591c\u9593\u306f\u7d3a\u8272\u3067\u8868\u793a\u3055\u308c\u307e\u3059\u3002<strong><span class=\"keiko_yellow\">\u4e0a\u4e0b\u306e\u30d0\u30fc\u3067\u300c\u30aa\u30ec\u30f3\u30b8\u8272\u304c\u91cd\u306a\u3063\u3066\u3044\u308b\u90e8\u5206\u300d\u3092\u63a2\u305b\u3070\u3001\u7121\u7406\u306e\u306a\u3044\u4f1a\u8b70\u6642\u9593\u3084\u96fb\u8a71\u306e\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u4e00\u77ac\u3067\u898b\u3064\u3051\u3089\u308c\u307e\u3059\u3002<\/span><\/strong><\/li>\n\n\n\n<li><strong>\u30b5\u30de\u30fc\u30bf\u30a4\u30e0\u306e\u81ea\u52d5\u8a08\u7b97<\/strong> <br><strong><span class=\"keiko_yellow\">\u30a2\u30e1\u30ea\u30ab\u3084\u30e8\u30fc\u30ed\u30c3\u30d1\u306a\u3069\u3067\u5b9f\u65bd\u3055\u308c\u308b\u30b5\u30de\u30fc\u30bf\u30a4\u30e0\uff08\u590f\u6642\u9593\uff09\u3082\u81ea\u52d5\u3067\u9069\u7528\u3055\u308c\u307e\u3059\u3002<\/span><\/strong>\u9762\u5012\u306a\u88dc\u6b63\u8a08\u7b97\u306f\u5fc5\u8981\u3042\u308a\u307e\u305b\u3093\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">\u3053\u306e\u30c4\u30fc\u30eb\u304c\u5f79\u7acb\u3064\u3068\u304d\u306f\u3069\u3093\u306a\u3068\u304d\uff1f<\/h3>\n\n\n\n<p>\u8a08\u7b97\u6a5f\u3092\u53e9\u304b\u306a\u304f\u3066\u3082\u300c\u76ee\u3067\u898b\u3066\u308f\u304b\u308b\u300d\u305f\u3081\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u30b7\u30fc\u30f3\u3067\u7279\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u6d77\u5916\u62e0\u70b9\u3068\u306eWEB\u4f1a\u8b70\u8abf\u6574<\/strong> <br>\u300c\u3053\u3061\u3089\u306e\u59cb\u696d\u6642\u9593\uff089\u6642\uff09\u306f\u3001\u5411\u3053\u3046\u306e\u4f55\u6642\u304b\uff1f\u300d\u300c\u304a\u4e92\u3044\u306b\u6b8b\u696d\u3084\u65e9\u671d\u51fa\u52e4\u306b\u306a\u3089\u305a\u306b\u8a71\u305b\u308b\u6642\u9593\u306f\u3069\u3053\u304b\uff1f\u300d\u3092\u63a2\u3059\u969b\u306b\u6700\u9069\u3067\u3059\u3002<\/li>\n\n\n\n<li><strong>\u6d77\u5916\u5728\u4f4f\u306e\u5bb6\u65cf\u30fb\u53cb\u4eba\u3078\u306e\u9023\u7d61<\/strong> <br>\u76f8\u624b\u304c\u5bdd\u3066\u3044\u308b\u6642\u9593\uff08\u7d3a\u8272\u306e\u30be\u30fc\u30f3\uff09\u3092\u907f\u3051\u3066\u3001\u78ba\u5b9f\u306b\u8d77\u304d\u3066\u3044\u308b\u6642\u9593\uff08\u30aa\u30ec\u30f3\u30b8\u8272\u306e\u30be\u30fc\u30f3\uff09\u306b\u9023\u7d61\u3092\u5165\u308c\u305f\u3044\u3068\u304d\u306b\u4fbf\u5229\u3067\u3059\u3002<\/li>\n\n\n\n<li><strong>\u6d77\u5916\u5e02\u5834\u3084\u30a4\u30d9\u30f3\u30c8\u306e\u78ba\u8a8d<\/strong> <br>\u6d77\u5916\u306e\u682a\u5f0f\u5e02\u5834\u304c\u958b\u304f\u6642\u9593\u3084\u3001\u30b9\u30dd\u30fc\u30c4\u30a4\u30d9\u30f3\u30c8\u306e\u958b\u59cb\u6642\u9593\u304c\u3001\u65e5\u672c\u6642\u9593\u3067\u3044\u3064\u306b\u306a\u308b\u304b\u3092\u76f4\u611f\u7684\u306b\u628a\u63e1\u3067\u304d\u307e\u3059\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">\u6642\u5dee\u8a08\u7b97\u3068\u30b5\u30de\u30fc\u30bf\u30a4\u30e0\u306e\u57fa\u790e\u77e5\u8b58<\/h3>\n\n\n\n<p>\u6d77\u5916\u3068\u306e\u6642\u9593\u3092\u8003\u3048\u308b\u969b\u3001\u77e5\u3063\u3066\u304a\u304f\u3068\u4fbf\u5229\u306a\u4e88\u5099\u77e5\u8b58\u3067\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>UTC\uff08\u5354\u5b9a\u4e16\u754c\u6642\uff09\u3068JST\uff08\u65e5\u672c\u6a19\u6e96\u6642\uff09<\/strong> <br>\u4e16\u754c\u306e\u6642\u9593\u306e\u57fa\u6e96\u3068\u306a\u308b\u306e\u304cUTC\u3067\u3059\u3002\u65e5\u672c\uff08JST\uff09\u306fUTC\u3088\u308a9\u6642\u9593\u9032\u3093\u3067\u3044\u308b\u305f\u3081\u300cUTC+9\u300d\u3068\u8868\u8a18\u3055\u308c\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u65e5\u4ed8\u5909\u66f4\u7dda\u306e\u7f60<\/strong> <br>\u6642\u5dee\u304c\u5927\u304d\u3044\u56fd\uff08\u30a2\u30e1\u30ea\u30ab\u3084\u30d6\u30e9\u30b8\u30eb\u306a\u3069\uff09\u306e\u5834\u5408\u3001\u65e5\u672c\u3068\u306f\u300c\u65e5\u4ed8\u300d\u304c\u7570\u306a\u308b\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u306e\u30c4\u30fc\u30eb\u3067\u306f\u3001\u65e5\u4ed8\u304c\u5909\u308f\u308b\u30dd\u30a4\u30f3\u30c8\u306b\u70b9\u7dda\u3092\u8868\u793a\u3057\u3001\u65e5\u4ed8\u306e\u30ba\u30ec\u3082\u53ef\u8996\u5316\u3057\u3066\u3044\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u30b5\u30de\u30fc\u30bf\u30a4\u30e0\uff08Daylight Saving Time\uff09<\/strong> <br>\u6b27\u7c73\u3092\u4e2d\u5fc3\u306b\u3001\u590f\u306e\u9593\u3060\u3051\u6642\u8a08\u30921\u6642\u9593\u9032\u3081\u308b\u5236\u5ea6\u3067\u3059\u3002\u671f\u9593\u4e2d\u306f\u65e5\u672c\u3068\u306e\u6642\u5dee\u304c\u5909\u308f\u308b\u305f\u3081\u6ce8\u610f\u304c\u5fc5\u8981\u3067\u3059\u304c\u3001\u672c\u30c4\u30fc\u30eb\u3067\u306f\u90fd\u5e02\u3092\u9078\u3076\u3060\u3051\u3067\u81ea\u52d5\u7684\u306b\u30b5\u30de\u30fc\u30bf\u30a4\u30e0\u9069\u7528\u5f8c\u306e\u6642\u9593\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">\u30d3\u30b8\u30cd\u30b9\u30a2\u30ef\u30fc\uff08\u6d3b\u52d5\u6642\u9593\uff09\u306e\u898b\u65b9<\/h3>\n\n\n\n<p>\u3053\u306e\u30c4\u30fc\u30eb\u3067\u306f\u3001\u4e00\u822c\u7684\u306a\u751f\u6d3b\u30ea\u30ba\u30e0\u3092\u8272\u3067\u8868\u73fe\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u30aa\u30ec\u30f3\u30b8\u8272\uff089:00 \u301c 18:00\uff09<\/strong> <br>\u4e00\u822c\u7684\u306a\u30d3\u30b8\u30cd\u30b9\u30a2\u30ef\u30fc\u3001\u307e\u305f\u306f\u6d3b\u52d5\u6642\u9593\u5e2f\u3067\u3059\u3002\u9023\u7d61\u3084\u4f1a\u8b70\u306b\u6700\u3082\u9069\u3057\u3066\u3044\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u30b0\u30e9\u30c7\u30fc\u30b7\u30e7\u30f3\uff087:00\u301c9:00 \/ 18:00\u301c22:00\uff09<\/strong> <br>\u8d77\u5e8a\u5f8c\u306e\u6e96\u5099\u6642\u9593\u3084\u3001\u4ed5\u4e8b\u7d42\u308f\u308a\u306e\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8\u30bf\u30a4\u30e0\u3067\u3059\u3002\u7dca\u6025\u6642\u306a\u3089\u9023\u7d61\u53ef\u80fd\u306a\u7bc4\u56f2\u3067\u3059\u3002<\/li>\n\n\n\n<li><strong>\u7d3a\u8272\uff0822:00 \u301c 7:00\uff09<\/strong> <br>\u4e00\u822c\u7684\u306a\u7761\u7720\u30fb\u4f11\u606f\u6642\u9593\u5e2f\u3067\u3059\u3002\u3053\u306e\u6642\u9593\u5e2f\u3078\u306e\u9023\u7d61\u3084\u4f1a\u8b70\u8a2d\u5b9a\u306f\u907f\u3051\u308b\u306e\u304c\u7121\u96e3\u3067\u3059\u3002<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u2013\/\u2013 \u2013:\u2013 \u5909\u63db\u5143 \u2013\/\u2013 \u2013:\u2013 \u5909\u63db\u5148 \u73fe\u5728\u6642\u523b\u306b\u623b\u3059 \u4e0a\u4e0b\u3092\u5165\u308c\u66ff\u3048 \u65e5\u6642\u3092\u6307\u5b9a \u6642\u5dee: \u2013\u6642\u9593 \u5909\u63db\u5143\u306e\u65e5\u6642 \u3053\u306e\u65e5\u6642\u3067\u8a08\u7b97 \u30ad\u30e3\u30f3\u30bb\u30eb \u9023\u7d61OK\uff089\u301c18\u6642\uff09\u3000 \u30d7\u30e9\u30a4\u30d9\u30fc\u30c8\u3000 \u7761\u7720\u4e2d\uff0822\u301c7\u6642\uff09 \u65e5\u672c\u3068 &#8230; <\/p>\n","protected":false},"author":4,"featured_media":10384,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13,39],"tags":[],"class_list":{"0":"post-10357","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-tool","8":"category-life","9":"entry"},"_links":{"self":[{"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/posts\/10357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/comments?post=10357"}],"version-history":[{"count":5,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/posts\/10357\/revisions"}],"predecessor-version":[{"id":10403,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/posts\/10357\/revisions\/10403"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/media\/10384"}],"wp:attachment":[{"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/media?parent=10357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/categories?post=10357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rakkokeyword.com\/techo\/wp-json\/wp\/v2\/tags?post=10357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}