时区转换工具+PWA离线网页

时区转换工具+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离线网页

五、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 应用,功能完整、结构清晰。
适用于快速使用、系统集成或离线访问,具备良好扩展性。

发表评论

评论已关闭。

相关文章