时区转换工具+PWA离线网页
一、时区转换工具对比
| 工具 | 说明 |
|---|---|
| Date | 原生 JS API,有限的时区支持,无法指定时区,仅使用本地时区。 |
| Intl.DateTimeFormat | 原生格式化显示,可指定时区,但不能修改时区逻辑。 |
| luxon | 强烈推荐,现代、轻量、功能强,原生支持时区、时间戳、格式化等。 |
| dayjs + timezone 插件 | 类似 moment,更现代,但时区支持需插件。 |
| moment-timezone | 功能全面但体积大,moment 官方已不推荐用于新项目。 |
二、Luxon 使用示例
1. 美国时间 -> 中国时间
import { DateTime } from 'luxon' const usTime = DateTime.fromISO('2025-04-01T11:11:00', { zone: 'America/Los_Angeles' }) const timestamp = usTime.toMillis() const cnTime = usTime.setZone('Asia/Shanghai') console.log('美国时间:', usTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')) console.log('时间戳:', timestamp) console.log('对应的中国时间:', cnTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ'))
美国时间:2025-04-01 11:11:00 GMT-7 时间戳:1743521460000 对应的中国时间:2025-04-02 02:11:00 GMT+8
2. 中国时间 -> 美国时间
const cn = DateTime.fromISO('2025-04-01T11:11:00', { zone: 'Asia/Shanghai' }) const us = cn.setZone('America/Los_Angeles') console.log('中国时间:', cn.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')) console.log('对应的美国时间:', us.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')) console.log('时间戳(UTC):', cn.toMillis())
3. 转换逻辑总结
| 场景 | 方法 |
|---|---|
| 指定时区的时间 → 时间戳 | DateTime.fromISO(...).toMillis() |
| 时间戳 → 指定时区时间 | DateTime.fromMillis(...).setZone(...) |
| 不同时区之间转换 | .setZone(...) |
| 时间格式化 | .toFormat('yyyy-MM-dd HH:mm:ss') 等 |
4. 常用时区 ID 表
| 名称 | IANA 时区 ID |
|---|---|
| 北京/上海(Asia/Shanghai) | Asia/Shanghai |
| 香港(Asia/Hong_Kong) | Asia/Hong_Kong |
| 日本(Asia/Tokyo) | Asia/Tokyo |
| 韩国(Asia/Seoul) | Asia/Seoul |
| 新加坡(Asia/Singapore) | Asia/Singapore |
| 印度(Asia/Kolkata) | Asia/Kolkata |
| 美国西部 - 洛杉矶(America/Los_Angeles) | America/Los_Angeles |
| 美国中部 - 芝加哥(America/Chicago) | America/Chicago |
| 美国东部 - 纽约(America/New_York) | America/New_York |
| 英国(Europe/London) | Europe/London |
| 德国(Europe/Berlin) | Europe/Berlin |
| 法国(Europe/Paris) | Europe/Paris |
| 澳大利亚 - 悉尼(Australia/Sydney) | Australia/Sydney |
| 新西兰(Pacific/Auckland) | Pacific/Auckland |
| 夏威夷(Pacific/Honolulu) | Pacific/Honolulu |
| UTC(协调世界时) | UTC |
三、时区转换脚本
1. NodeJS 脚本(使用 luxon)
const { DateTime } = require('luxon') function convertTime({ timeStr = '2025-04-01 11:11:00', fromZone = 'America/Los_Angeles', toZone = 'Asia/Shanghai' }) { const fromTime = DateTime.fromFormat(timeStr, 'yyyy-MM-dd HH:mm:ss', { zone: fromZone }) const toTime = fromTime.setZone(toZone) console.log(`原始时间 (${fromZone}):`, fromTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')) console.log(`时间戳(UTC 毫秒):`, fromTime.toMillis()) console.log(`转换后 (${toZone}):`, toTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')) } // 修改这里的参数即可 convertTime({ timeStr: '2025-04-01 11:11:00', fromZone: 'America/Los_Angeles', toZone: 'Asia/Shanghai' })
2. Python 脚本(使用 pytz)
pip install pytz
from datetime import datetime import pytz def convert_time(time_str='2025-04-01 11:11:00', from_zone='America/Los_Angeles', to_zone='Asia/Shanghai'): from_tz = pytz.timezone(from_zone) to_tz = pytz.timezone(to_zone) naive_dt = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S') from_dt = from_tz.localize(naive_dt) to_dt = from_dt.astimezone(to_tz) print(f'原始时间 ({from_zone}): {from_dt.strftime("%Y-%m-%d %H:%M:%S %Z%z")}') print(f'时间戳(UTC 秒): {int(from_dt.timestamp())}') print(f'转换后 ({to_zone}): {to_dt.strftime("%Y-%m-%d %H:%M:%S %Z%z")}') # 修改参数即可 convert_time( time_str='2025-04-01 11:11:00', from_zone='America/Los_Angeles', to_zone='Asia/Shanghai' )
四、网页小工具
使用 Luxon + HTML 原生控件制作的小工具,支持:
- 输入时间
- 原始/目标时区选择
- 时间戳显示
- 一键复制
1. 代码展示
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>🌏 时区时间转换工具</title> <script src="https://cdn.jsdelivr.net/npm/luxon@3/build/global/luxon.min.js"></script> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 2rem; max-width: 700px; margin: auto; background: #f8f9fa; } h2 { text-align: center; margin-bottom: 2rem; } label { font-weight: bold; margin-top: 1rem; display: block; } input, select, button { width: 100%; padding: 0.6rem; margin: 0.4rem 0 1rem; border: 1px solid #ccc; border-radius: 5px; font-size: 1rem; } button { background: #007bff; color: white; cursor: pointer; border: none; transition: background 0.3s; } button:hover { background: #0056b3; } .result { background: #fff; border-left: 5px solid #007bff; padding: 1rem; margin-top: 1rem; border-radius: 5px; white-space: pre-wrap; font-size: 0.95rem; } .error { color: red; margin-top: 1rem; } .copy-btn { margin-top: 0.5rem; background: #28a745; } .copy-btn:hover { background: #1e7e34; } table { width: 100%; border-collapse: collapse; margin-top: 2rem; background: #fff; } th, td { border: 1px solid #ddd; padding: 0.6rem; text-align: left; } th { background-color: #007bff; color: white; } </style> </head> <body> <h2>🕒 时区转换小工具</h2> <label for="inputDate">选择时间</label> <input type="datetime-local" id="inputDate" /> <label for="fromZone">原始时区</label> <select id="fromZone"></select> <label for="toZone">目标时区</label> <select id="toZone"></select> <button onclick="convertTime()">转换时间</button> <div class="result" id="output">👇 转换结果将在这里显示</div> <button class="copy-btn" onclick="copyResult()">📋 复制结果</button> <div class="error" id="error"></div> <script> const { DateTime } = luxon const timezones = [ { label: '北京(Asia/Shanghai)', value: 'Asia/Shanghai' }, { label: '香港(Asia/Hong_Kong)', value: 'Asia/Hong_Kong' }, { label: '日本(Asia/Tokyo)', value: 'Asia/Tokyo' }, { label: '韩国(Asia/Seoul)', value: 'Asia/Seoul' }, { label: '新加坡(Asia/Singapore)', value: 'Asia/Singapore' }, { label: '印度(Asia/Kolkata)', value: 'Asia/Kolkata' }, { label: '美国西部 - 洛杉矶(America/Los_Angeles)', value: 'America/Los_Angeles' }, { label: '美国中部 - 芝加哥(America/Chicago)', value: 'America/Chicago' }, { label: '美国东部 - 纽约(America/New_York)', value: 'America/New_York' }, { label: '英国(Europe/London)', value: 'Europe/London' }, { label: '德国(Europe/Berlin)', value: 'Europe/Berlin' }, { label: '法国(Europe/Paris)', value: 'Europe/Paris' }, { label: '澳大利亚 - 悉尼(Australia/Sydney)', value: 'Australia/Sydney' }, { label: '新西兰(Pacific/Auckland)', value: 'Pacific/Auckland' }, { label: '夏威夷(Pacific/Honolulu)', value: 'Pacific/Honolulu' }, { label: 'UTC(协调世界时)', value: 'UTC' }, ] function renderTimezoneOptions() { const fromSelect = document.getElementById('fromZone') const toSelect = document.getElementById('toZone') const tableBody = document.getElementById('timezoneTable') timezones.forEach(({ label, value }) => { const opt1 = new Option(label, value) const opt2 = new Option(label, value) fromSelect.appendChild(opt1) toSelect.appendChild(opt2) }) fromSelect.value = 'Asia/Shanghai' toSelect.value = 'America/Los_Angeles' } document.addEventListener('DOMContentLoaded', () => { renderTimezoneOptions() const now = new Date() const local = now.toISOString().slice(0, 16) document.getElementById('inputDate').value = local }) function convertTime() { const input = document.getElementById('inputDate').value const fromZone = document.getElementById('fromZone').value const toZone = document.getElementById('toZone').value const output = document.getElementById('output') const error = document.getElementById('error') error.textContent = '' if (!input) { error.textContent = '❌ 请选择一个时间' return } try { const dt = DateTime.fromISO(input, { zone: fromZone }) const toTime = dt.setZone(toZone) const result = ` 🌍 原始时间(${fromZone}): ${dt.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')} 🕗 时间戳(UTC 毫秒): ${dt.toMillis()} ➡️ 转换后时间(${toZone}): ${toTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')} `.trim() output.textContent = result } catch (e) { error.textContent = '❌ 转换失败,请检查输入' } } function copyResult() { const result = document.getElementById('output').textContent if (!result || result.includes('将在这里显示')) return navigator.clipboard.writeText(result).then(() => { alert('✅ 已复制到剪贴板!') }) } </script> </body> </html>
2. 示例截图

五、PWA 应用支持
1. PWA 结构
. ├── icons │ ├── time_192.png │ └── time_512.png ├── index.html ├── luxon.min.js ├── manifest.json └── service-worker.js
2. manifest.json
用于定义名称、图标、启动方式等:
{ "name": "时区时间转换工具", "short_name": "时区转换", "start_url": "./index.html", "display": "standalone", "background_color": "#ffffff", "theme_color": "#007bff", "description": "支持多时区时间互转、时间戳生成的轻量工具", "icons": [ { "src": "icons/time_192.png", "sizes": "192x192", "type": "image/png" }, { "src": "icons/time_512.png", "sizes": "512x512", "type": "image/png" } ] }
3. service-worker.js
实现核心缓存功能,支持离线访问:
const CACHE_NAME = 'timezone-converter-0.0.1' const urlsToCache = [ './', './index.html', './manifest.json', './service-worker.js', './icons/time_192.png', './icons/time_512.png', './luxon.min.js' ] // 安装时预缓存核心资源 self.addEventListener('install', event => { self.skipWaiting() event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)) ) }) // 激活时清除旧缓存 self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))) ) ) self.clients.claim() }) // 拦截所有请求,优先从缓存读取,失败则网络请求 self.addEventListener('fetch', event => { const request = event.request event.respondWith( caches.match(request).then(cachedResponse => { if (cachedResponse) return cachedResponse return fetch(request) .then(networkResponse => { if ( networkResponse && networkResponse.status === 200 && request.url.startsWith(self.location.origin) ) { const cloned = networkResponse.clone() caches.open(CACHE_NAME).then(cache => { cache.put(request, cloned) }) } return networkResponse }) .catch(() => { if (request.headers.get('accept')?.includes('text/html')) { return caches.match('./index.html') } }) }) ) })
4. html 文件
整合 Luxon + PWA 注册逻辑
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>🌏 时区时间转换工具1</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#007bff" /> <link rel="manifest" href="manifest.json" /> <link rel="icon" href="icons/time_192.png" /> <link rel="apple-touch-icon" href="icons/time_512.png" /> <script src="./luxon.min.js"></script> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 2rem; max-width: 700px; margin: auto; background: #f8f9fa; } h2 { text-align: center; margin-bottom: 2rem; } label { font-weight: bold; margin-top: 1rem; display: block; } input, select, button { width: 100%; padding: 0.6rem; margin: 0.4rem 0 1rem; border: 1px solid #ccc; border-radius: 5px; font-size: 1rem; } button { background: #007bff; color: white; cursor: pointer; border: none; transition: background 0.3s; } button:hover { background: #0056b3; } .result { background: #fff; border-left: 5px solid #007bff; padding: 1rem; margin-top: 1rem; border-radius: 5px; white-space: pre-wrap; font-size: 0.95rem; } .error { color: red; margin-top: 1rem; } .copy-btn { margin-top: 0.5rem; background: #28a745; } .copy-btn:hover { background: #1e7e34; } table { width: 100%; border-collapse: collapse; margin-top: 2rem; background: #fff; } th, td { border: 1px solid #ddd; padding: 0.6rem; text-align: left; } th { background-color: #007bff; color: white; } </style> </head> <body> <h2>🕒 时区转换小工具</h2> <label for="inputDate">选择时间</label> <input type="datetime-local" id="inputDate" /> <label for="fromZone">原始时区</label> <select id="fromZone"></select> <label for="toZone">目标时区</label> <select id="toZone"></select> <button onclick="convertTime()">转换时间</button> <div class="result" id="output">👇 转换结果将在这里显示</div> <button class="copy-btn" onclick="copyResult()">📋 复制结果</button> <div class="error" id="error"></div> <script> const { DateTime } = luxon const timezones = [ { label: '北京(Asia/Shanghai)', value: 'Asia/Shanghai' }, { label: '香港(Asia/Hong_Kong)', value: 'Asia/Hong_Kong' }, { label: '日本(Asia/Tokyo)', value: 'Asia/Tokyo' }, { label: '韩国(Asia/Seoul)', value: 'Asia/Seoul' }, { label: '新加坡(Asia/Singapore)', value: 'Asia/Singapore' }, { label: '印度(Asia/Kolkata)', value: 'Asia/Kolkata' }, { label: '美国西部 - 洛杉矶(America/Los_Angeles)', value: 'America/Los_Angeles' }, { label: '美国中部 - 芝加哥(America/Chicago)', value: 'America/Chicago' }, { label: '美国东部 - 纽约(America/New_York)', value: 'America/New_York' }, { label: '英国(Europe/London)', value: 'Europe/London' }, { label: '德国(Europe/Berlin)', value: 'Europe/Berlin' }, { label: '法国(Europe/Paris)', value: 'Europe/Paris' }, { label: '澳大利亚 - 悉尼(Australia/Sydney)', value: 'Australia/Sydney' }, { label: '新西兰(Pacific/Auckland)', value: 'Pacific/Auckland' }, { label: '夏威夷(Pacific/Honolulu)', value: 'Pacific/Honolulu' }, { label: 'UTC(协调世界时)', value: 'UTC' } ] function renderTimezoneOptions() { const fromSelect = document.getElementById('fromZone') const toSelect = document.getElementById('toZone') timezones.forEach(({ label, value }) => { const opt1 = new Option(label, value) const opt2 = new Option(label, value) fromSelect.appendChild(opt1) toSelect.appendChild(opt2) }) fromSelect.value = 'Asia/Shanghai' toSelect.value = 'America/Los_Angeles' } function convertTime() { const input = document.getElementById('inputDate').value const fromZone = document.getElementById('fromZone').value const toZone = document.getElementById('toZone').value const output = document.getElementById('output') const error = document.getElementById('error') error.textContent = '' if (!input) { error.textContent = '❌ 请选择一个时间' return } try { const dt = DateTime.fromISO(input, { zone: fromZone }) const toTime = dt.setZone(toZone) const result = ` 🌍 原始时间(${fromZone}): ${dt.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')} 🕗 时间戳(UTC 毫秒): ${dt.toMillis()} ➡️ 转换后时间(${toZone}): ${toTime.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ')} `.trim() output.textContent = result } catch (e) { error.textContent = '❌ 转换失败,请检查输入' } } function copyResult() { const result = document.getElementById('output').textContent if (!result || result.includes('将在这里显示')) return navigator.clipboard.writeText(result).then(() => { alert('✅ 已复制到剪贴板!') }) } // 初始化 document.addEventListener('DOMContentLoaded', () => { renderTimezoneOptions() const now = new Date() const local = now.toISOString().slice(0, 16) document.getElementById('inputDate').value = local }) // 注册 PWA service worker if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('service-worker.js') .then(() => console.log('✅ Service Worker 注册成功')) .catch(err => console.log('❌ 注册失败:', err)) }) } </script> </body> </html>
5. Live server 启动
启动完成安装到本地即可
六、总结
本项目对比并选用 Luxon 实现多时区转换,支持各时区时间互转。
提供 NodeJS 与 Python 脚本、网页小工具及 PWA 应用,功能完整、结构清晰。
适用于快速使用、系统集成或离线访问,具备良好扩展性。