基于JavaScript編寫一個(gè)時(shí)間戳轉(zhuǎn)換工具
一、起因:為什么需要這個(gè)工具
在現(xiàn)代 Web 開(kāi)發(fā)中,時(shí)間戳(Timestamp)是系統(tǒng)間傳遞時(shí)間信息的標(biāo)準(zhǔn)格式。無(wú)論是調(diào)用后端 API、記錄日志、還是處理緩存策略,我們常常需要將人類可讀的時(shí)間(如 2025-01-01 12:30:45)轉(zhuǎn)換為 Unix 時(shí)間戳(秒)或 JavaScript 時(shí)間戳(毫秒)。
然而,在以下場(chǎng)景中,這一過(guò)程變得繁瑣:
- 接口調(diào)試:使用 Postman、curl 或?yàn)g覽器 DevTools 直接調(diào)用接口時(shí),無(wú)法運(yùn)行代碼生成時(shí)間戳;
- 臨時(shí)測(cè)試:需要快速獲取某個(gè)特定時(shí)間點(diǎn)的時(shí)間戳,但不想啟動(dòng) IDE 或編寫腳本;
- 跨時(shí)區(qū)協(xié)作:團(tuán)隊(duì)成員對(duì)“當(dāng)前時(shí)間”的理解存在偏差,需統(tǒng)一標(biāo)準(zhǔn);
- 非開(kāi)發(fā)者參與:產(chǎn)品經(jīng)理或測(cè)試人員需要提供時(shí)間參數(shù),但不熟悉時(shí)間戳概念。
因此,我決定開(kāi)發(fā)一個(gè)純前端、零依賴、開(kāi)箱即用的時(shí)間戳轉(zhuǎn)換工具。它無(wú)需安裝、不依賴任何庫(kù)、可在任意瀏覽器中運(yùn)行,且兼顧專業(yè)性與易用性。
效果預(yù)覽:

二、需求與設(shè)計(jì)原則
2.1 功能需求
雙向轉(zhuǎn)換:
- 時(shí)間 → 時(shí)間戳(支持毫秒/秒兩種單位)
- 時(shí)間戳 → 可讀時(shí)間(自動(dòng)識(shí)別秒或毫秒)
極致交互體驗(yàn):
- 點(diǎn)擊
<input type="date">或<input type="time">的任意位置,立即彈出原生選擇器(現(xiàn)代瀏覽器) - 同時(shí)允許直接手動(dòng)輸入合法值(如
2025-12-31、14:30:25) - 不強(qiáng)制格式,但需有容錯(cuò)能力
用戶體驗(yàn):
- 實(shí)時(shí)反饋:輸入無(wú)效時(shí)立即提示
- 一鍵復(fù)制:所有輸出結(jié)果均可一鍵復(fù)制到剪貼板
- 響應(yīng)式布局:適配桌面與移動(dòng)設(shè)備
健壯性:
- 嚴(yán)格校驗(yàn)輸入合法性
- 處理邊界情況(如空值、超大數(shù)、非法字符)
- 避免靜默失敗,確保用戶知曉操作結(jié)果
2.2 設(shè)計(jì)原則
- 回歸原生 + 漸進(jìn)增強(qiáng):優(yōu)先使用
<input type="date">和<input type="time">,利用瀏覽器內(nèi)置 UI;通過(guò).showPicker()API 增強(qiáng)點(diǎn)擊體驗(yàn); - 最小干預(yù):不添加額外按鈕或復(fù)雜包裝,保持界面簡(jiǎn)潔;
- 向后兼容:舊版瀏覽器降級(jí)為原生行為,不影響核心功能;
- 代碼自包含:?jiǎn)?HTML 文件,無(wú)外部依賴,便于分享與部署。
三、技術(shù)實(shí)現(xiàn)
3.1 界面結(jié)構(gòu)
工具分為兩個(gè)主要區(qū)域:
時(shí)間轉(zhuǎn)時(shí)間戳
- 兩個(gè)輸入框:
<input type="date">+<input type="time" step="1"> - 單位切換開(kāi)關(guān):毫秒(JavaScript) / 秒(Unix)
- 結(jié)果顯示與復(fù)制按鈕
時(shí)間戳轉(zhuǎn)時(shí)間
- 一個(gè)文本輸入框(支持?jǐn)?shù)字輸入)
- 三個(gè)輸出字段:本地可讀時(shí)間、毫秒、秒
- 對(duì)應(yīng)復(fù)制按鈕與狀態(tài)提示
關(guān)鍵細(xì)節(jié):<input type="time"> 設(shè)置 step="1" 以支持秒級(jí)精度(默認(rèn)只到分鐘)。
3.2點(diǎn)擊輸入框即可彈出選擇器
解決方案:使用.showPicker()API
現(xiàn)代瀏覽器(Chrome ≥96, Safari ≥16.4, Firefox ≥121)提供了 HTMLInputElement.showPicker() 方法,允許 JavaScript 主動(dòng)觸發(fā)原生選擇器。
function enhancePicker(input) {
input.addEventListener('click', () => {
if (typeof input.showPicker === 'function') {
try {
input.showPicker(); // 主動(dòng)喚出選擇器
} catch (e) {
// 安全環(huán)境可能阻止,靜默忽略
}
}
});
}
// 應(yīng)用到日期和時(shí)間輸入框
enhancePicker(dateInput);
enhancePicker(timeInput);
- 效果:用戶點(diǎn)擊輸入框任意位置,立即彈出原生 UI;
- 兼容性:不支持的瀏覽器(如舊版 Edge)自動(dòng)降級(jí),仍可通過(guò)小箭頭操作;
- 不干擾手輸:用戶依然可以自由鍵入
2025-06-15,一切照常工作。
3.3 手動(dòng)輸入與選擇器的融合
無(wú)論用戶是通過(guò)選擇器選中日期,還是直接在輸入框中鍵入 2025-06-15,都會(huì)觸發(fā) input 事件。我們只需在回調(diào)中讀取 .value 并嘗試解析即可。
智能解析策略
function parseUserInput(dateStr, timeStr) {
const today = new Date();
const fallbackDate = dateStr || today.toISOString().slice(0, 10); // YYYY-MM-DD
const fallbackTime = timeStr || '00:00:00';
let isoStr = `${fallbackDate}T${fallbackTime}`;
let date = new Date(isoStr);
if (isNaN(date.getTime()) && dateStr) {
// 若時(shí)間部分非法,嘗試僅用日期
date = new Date(`${dateStr}T00:00:00`);
}
return isNaN(date.getTime()) ? null : date;
}
- 自動(dòng)補(bǔ)全缺失部分(如只有日期則時(shí)間設(shè)為
00:00:00) - 容忍時(shí)間格式不完整(如
14:30被瀏覽器自動(dòng)補(bǔ)為14:30:00) - 若整體解析失敗但日期有效,則忽略時(shí)間部分
3.4 時(shí)間戳解析邏輯
對(duì)于時(shí)間戳輸入,需處理以下情況:
| 輸入示例 | 處理方式 |
|---|---|
| 1704067200 | 判定為秒 → ×1000 → 毫秒 |
| 1704067200000 | 判定為毫秒 → 直接使用 |
| abc123def | 提取數(shù)字 → 123 → 判定為秒 |
| -123 | 允許(歷史時(shí)間),但需在有效范圍內(nèi) |
有效范圍校驗(yàn):JavaScript 的 Date 對(duì)象有效范圍為 ±100,000,000 天(約 ±273,972 年),對(duì)應(yīng)毫秒值約為 ±8.64e15。我們?cè)O(shè)置安全邊界:
if (ms < -8640000000000000 || ms > 8640000000000000) {
// 超出范圍
}
3.5 用戶反饋機(jī)制
- 成功狀態(tài):綠色提示(如“ 已復(fù)制”),1.5 秒后恢復(fù)
- 錯(cuò)誤狀態(tài):紅色提示(如“請(qǐng)輸入有效日期”),持續(xù)顯示直至修正
- 空狀態(tài):清空提示,避免干擾
四、完整源碼
以下為可直接保存為 .html 文件并在瀏覽器中打開(kāi)的代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>時(shí)間戳轉(zhuǎn)換工具</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', sans-serif;
background-color: #f8fafc;
color: #1e293b;
line-height: 1.5;
padding: 24px;
}
.container {
max-width: 680px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 32px;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #0f172a;
}
.card {
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
padding: 24px;
margin-bottom: 24px;
}
.section-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: #334155;
}
.form-row {
display: flex;
gap: 16px;
margin-bottom: 20px;
}
@media (max-width: 600px) {
.form-row {
flex-direction: column;
gap: 12px;
}
}
.field {
flex: 1;
}
.field label {
display: block;
font-size: 14px;
font-weight: 500;
margin-bottom: 6px;
color: #475569;
}
input[type="date"],
input[type="time"],
input[type="text"] {
width: 100%;
padding: 10px 12px;
border: 1px solid #cbd5e1;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.2s;
}
input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}
.unit-toggle {
display: inline-flex;
background: #f1f5f9;
border-radius: 8px;
padding: 4px;
font-size: 14px;
}
.unit-option {
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
user-select: none;
transition: all 0.15s;
}
.unit-option.active {
background: white;
color: #1d4ed8;
font-weight: 600;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.result-line {
display: flex;
gap: 12px;
align-items: center;
margin-top: 8px;
}
.result-line label {
font-size: 14px;
color: #64748b;
font-weight: 500;
min-width: 50px;
}
.result-line input[readonly] {
flex: 1;
background: #f8fafc;
color: #1e293b;
font-family: monospace;
font-size: 15px;
padding: 10px 12px;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
.copy-btn {
padding: 10px 16px;
background: #e2e8f0;
color: #475569;
border: none;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
min-width: 72px;
white-space: nowrap;
}
.copy-btn:hover {
background: #cbd5e1;
}
.copy-btn.copied {
background: #10b981 !important;
color: white !important;
}
.message {
height: 20px;
text-align: center;
font-size: 14px;
margin-top: 12px;
}
.message.error {
color: #ef4444;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>時(shí)間戳轉(zhuǎn)換工具</h1>
</header>
<!-- 時(shí)間 → 時(shí)間戳 -->
<div class="card">
<div class="section-title">?? 時(shí)間轉(zhuǎn)時(shí)間戳</div>
<div class="form-row">
<div class="field">
<label for="customDate">日期</label>
<input type="date" id="customDate" />
</div>
<div class="field">
<label for="customTime">時(shí)間</label>
<input type="time" id="customTime" step="1" />
</div>
</div>
<div class="field">
<label>輸出單位</label>
<div class="unit-toggle" id="unitToggle">
<div class="unit-option active" data-value="ms">毫秒(JavaScript)</div>
<div class="unit-option" data-value="s">秒(Unix)</div>
</div>
</div>
<div class="result-line">
<label>結(jié)果</label>
<input type="text" id="generatedOutput" readonly />
<button class="copy-btn" id="copyGenerated">復(fù)制</button>
</div>
<div class="message" id="msgGen"></div>
</div>
<!-- 時(shí)間戳 → 時(shí)間 -->
<div class="card">
<div class="section-title">?? 時(shí)間戳轉(zhuǎn)時(shí)間</div>
<div class="field">
<label for="inputTimestamp">輸入時(shí)間戳(支持秒或毫秒)</label>
<input type="text" id="inputTimestamp" placeholder="例如:1664294400 或 1664294400000" />
</div>
<div class="result-line">
<label>本地時(shí)間</label>
<input type="text" id="readableTime" readonly />
<button class="copy-btn" id="copyReadable">復(fù)制</button>
</div>
<div class="result-line">
<label>毫秒</label>
<input type="text" id="outputMs" readonly />
<button class="copy-btn" id="copyParsedMs">復(fù)制</button>
</div>
<div class="result-line">
<label>秒</label>
<input type="text" id="outputSec" readonly />
<button class="copy-btn" id="copyParsedSec">復(fù)制</button>
</div>
<div class="message" id="msgParse"></div>
</div>
</div>
<script>
// 初始化為當(dāng)前時(shí)間
const now = new Date();
document.getElementById('customDate').valueAsDate = now;
document.getElementById('customTime').value = now.toTimeString().slice(0, 8);
const dateInput = document.getElementById('customDate');
const timeInput = document.getElementById('customTime');
const unitToggle = document.getElementById('unitToggle');
const generatedOutput = document.getElementById('generatedOutput');
const msgGen = document.getElementById('msgGen');
// ? 關(guān)鍵增強(qiáng):點(diǎn)擊輸入框任意位置,主動(dòng)喚出選擇器(現(xiàn)代瀏覽器)
function enhancePicker(input) {
// 點(diǎn)擊輸入框時(shí)嘗試喚出原生選擇器
input.addEventListener('click', () => {
if (typeof input.showPicker === 'function') {
try {
input.showPicker();
} catch (e) {
// 某些環(huán)境可能不允許(如安全限制),靜默忽略
}
}
});
}
// 應(yīng)用增強(qiáng)
enhancePicker(dateInput);
enhancePicker(timeInput);
function parseUserInput(dateStr, timeStr) {
if (!dateStr && !timeStr) return null;
const today = new Date();
const fallbackDate = dateStr || today.toISOString().slice(0, 10);
const fallbackTime = timeStr || '00:00:00';
let isoStr = `${fallbackDate}T${fallbackTime}`;
let date = new Date(isoStr);
if (isNaN(date.getTime()) && dateStr) {
date = new Date(`${dateStr}T00:00:00`);
}
return isNaN(date.getTime()) ? null : date;
}
function updateFromDateTime() {
const dateStr = dateInput.value.trim();
const timeStr = timeInput.value.trim();
const date = parseUserInput(dateStr, timeStr);
if (!date) {
generatedOutput.value = '';
showMessage(msgGen, '請(qǐng)輸入有效日期或時(shí)間', true);
return;
}
const ms = date.getTime();
const sec = Math.floor(ms / 1000);
const activeUnit = unitToggle.querySelector('.unit-option.active').dataset.value;
generatedOutput.value = activeUnit === 'ms' ? ms : sec;
showMessage(msgGen, '');
}
// 監(jiān)聽(tīng)輸入變化(包括手輸和選擇器)
dateInput.addEventListener('input', updateFromDateTime);
timeInput.addEventListener('input', updateFromDateTime);
unitToggle.querySelectorAll('.unit-option').forEach(btn => {
btn.addEventListener('click', () => {
unitToggle.querySelectorAll('.unit-option').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
updateFromDateTime();
});
});
function showMessage(el, text, isError = false) {
el.textContent = text;
el.className = 'message' + (isError ? ' error' : '');
}
function animateCopyButton(btn, successText = '? 已復(fù)制') {
const original = btn.textContent;
btn.classList.add('copied');
btn.textContent = successText;
setTimeout(() => {
btn.classList.remove('copied');
btn.textContent = original;
}, 1500);
}
const copyMap = {
copyGenerated: () => generatedOutput.value,
copyReadable: () => readableTime.value,
copyParsedMs: () => outputMs.value,
copyParsedSec: () => outputSec.value
};
Object.keys(copyMap).forEach(id => {
document.getElementById(id).addEventListener('click', async () => {
const text = copyMap[id]();
if (!text) {
showMessage(document.getElementById('msgGen'), '無(wú)內(nèi)容可復(fù)制', true);
return;
}
try {
await navigator.clipboard.writeText(text);
animateCopyButton(document.getElementById(id));
} catch (err) {
showMessage(document.getElementById('msgGen'), '復(fù)制失敗', true);
}
});
});
// 時(shí)間戳解析
const inputTimestamp = document.getElementById('inputTimestamp');
const readableTime = document.getElementById('readableTime');
const outputMs = document.getElementById('outputMs');
const outputSec = document.getElementById('outputSec');
const msgParse = document.getElementById('msgParse');
inputTimestamp.addEventListener('input', () => {
let raw = inputTimestamp.value.trim();
if (!raw) {
readableTime.value = '';
outputMs.value = '';
outputSec.value = '';
showMessage(msgParse, '');
return;
}
const numStr = raw.replace(/[^0-9]/g, '');
if (!numStr) {
showMessage(msgParse, '請(qǐng)輸入數(shù)字', true);
readableTime.value = '';
outputMs.value = '';
outputSec.value = '';
return;
}
let num = Number(numStr);
if (isNaN(num)) {
showMessage(msgParse, '無(wú)效數(shù)字', true);
readableTime.value = '';
outputMs.value = '';
outputSec.value = '';
return;
}
let ms = numStr.length <= 10 ? num * 1000 : num;
if (ms < -8640000000000000 || ms > 8640000000000000) {
showMessage(msgParse, '時(shí)間戳超出有效范圍', true);
readableTime.value = '';
outputMs.value = '';
outputSec.value = '';
return;
}
const date = new Date(ms);
if (isNaN(date.getTime())) {
showMessage(msgParse, '無(wú)法解析時(shí)間', true);
readableTime.value = '';
outputMs.value = '';
outputSec.value = '';
return;
}
readableTime.value = date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZoneName: 'short'
});
outputMs.value = ms;
outputSec.value = Math.floor(ms / 1000);
showMessage(msgParse, '');
});
</script>
</body>
</html>
五、使用說(shuō)明
5.1 時(shí)間轉(zhuǎn)時(shí)間戳
- 點(diǎn)擊“日期”輸入框任意位置 → 彈出日歷(現(xiàn)代瀏覽器),或直接輸入
YYYY-MM-DD - 點(diǎn)擊“時(shí)間”輸入框任意位置 → 彈出時(shí)間選擇器,或輸入
HH:mm:ss - 選擇輸出單位(默認(rèn)毫秒)
- 結(jié)果自動(dòng)顯示,點(diǎn)擊“復(fù)制”即可粘貼到接口參數(shù)中
注意:若只填寫日期,時(shí)間默認(rèn)為 00:00:00;若只填時(shí)間,日期默認(rèn)為當(dāng)天。
5.2 時(shí)間戳轉(zhuǎn)時(shí)間
- 在輸入框中粘貼時(shí)間戳(如
1704067200或1704067200000) - 工具自動(dòng)識(shí)別單位并顯示:
- 本地可讀時(shí)間(含時(shí)區(qū))
- 對(duì)應(yīng)的毫秒值
- 對(duì)應(yīng)的秒值
- 點(diǎn)擊任一“復(fù)制”按鈕獲取所需格式
六、兼容性說(shuō)明
| 功能 | Chrome ≥96 | Safari ≥16.4 | Firefox ≥121 | 舊版瀏覽器 |
|---|---|---|---|---|
| 點(diǎn)擊輸入框彈出選擇器 | ? | ? | ? | ?(需點(diǎn)小箭頭) |
| 手動(dòng)輸入 | ? | ? | ? | ? |
| 時(shí)間戳轉(zhuǎn)換 | ? | ? | ? | ? |
| 一鍵復(fù)制 | ? | ? | ? | ?(需 HTTPS 或 localhost) |
即使在不支持 .showPicker() 的環(huán)境中,工具依然完全可用,只是選擇器喚出方式略有不同。
附:時(shí)間戳參考
| 日期時(shí)間 | 毫秒(JavaScript) | 秒(Unix) |
|---|---|---|
| 2025-01-01 00:00:00 | 1735660800000 | 1735660800 |
| 當(dāng)前時(shí)間(示例) | 1704067200000 | 1704067200 |
以上就是基于JavaScript編寫一個(gè)時(shí)間戳轉(zhuǎn)換工具的詳細(xì)內(nèi)容,更多關(guān)于JavaScript時(shí)間戳轉(zhuǎn)換工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- JavaScript將時(shí)間戳轉(zhuǎn)換為日期格式的多種轉(zhuǎn)換方法
- 利用JavaScript實(shí)現(xiàn)時(shí)間戳功能的5種方法詳解
- JavaScript?中的時(shí)間戳操作和使用詳解
- JavaScript時(shí)間戳與時(shí)間的轉(zhuǎn)化常用方法
- JavaScript時(shí)間戳與時(shí)間相互轉(zhuǎn)換的常用方法
- JavaScript獲取和操作時(shí)間戳的用法詳解
- javascript日期字符串轉(zhuǎn)換為時(shí)間戳的5種方法總結(jié)
- JS時(shí)間戳與日期格式的轉(zhuǎn)換小結(jié)
相關(guān)文章
javascript在IE下trim函數(shù)無(wú)法使用的解決方法
這篇文章主要介紹了javascript在IE下trim函數(shù)無(wú)法使用的解決方法,分別敘述了javascript以及jQuery下的解決方案,對(duì)于WEB前端javascript設(shè)計(jì)人員進(jìn)行瀏覽器兼容性調(diào)試有不錯(cuò)的借鑒價(jià)值,需要的朋友可以參考下2014-09-09
JavaScript浮點(diǎn)數(shù)進(jìn)行整數(shù)轉(zhuǎn)換的三種方法
在 JavaScript 開(kāi)發(fā)中,對(duì)浮點(diǎn)數(shù)進(jìn)行整數(shù)轉(zhuǎn)換是一項(xiàng)基礎(chǔ)且頻繁的操作,無(wú)論是進(jìn)行數(shù)值計(jì)算、數(shù)據(jù)處理還是 UI 渲染,選擇正確的取整方法都至關(guān)重要,本文給大家介紹了JavaScript浮點(diǎn)數(shù)進(jìn)行整數(shù)轉(zhuǎn)換的三種方法,需要的朋友可以參考下2025-12-12
JavaScript實(shí)現(xiàn)定時(shí)隱藏與顯示圖片的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)定時(shí)隱藏與顯示圖片的方法,可實(shí)現(xiàn)javascript定時(shí)關(guān)閉圖片的功能,涉及javascript針對(duì)頁(yè)面元素屬性定時(shí)操作的相關(guān)技巧,需要的朋友可以參考下2015-08-08
JavaScript setTimeout和setInterval的使用方法 說(shuō)明
兩個(gè)函數(shù)都是可以用來(lái)實(shí)現(xiàn)一段時(shí)間后執(zhí)行一段javascript代碼的效果。兩個(gè)函數(shù)都有兩個(gè)參數(shù),前面的都是執(zhí)行表達(dá)式,后面的是隔的秒數(shù)。2010-03-03
Javacript實(shí)現(xiàn)顏色梯度變化和漸變的效果代碼
用js對(duì)導(dǎo)航欄的顏色做了梯度的變化處理,通過(guò)處理..獲取兩種顏色在變化時(shí)的各種顏色字符串,并且字符串的個(gè)數(shù),即獲取的頻率可以調(diào)節(jié)2013-05-05
JS的函數(shù)調(diào)用棧stack size的計(jì)算方法
本篇文章給大家分享了關(guān)于JS的函數(shù)調(diào)用棧stack size的計(jì)算方法的相關(guān)知識(shí)點(diǎn),有興趣的朋友參考學(xué)習(xí)下。2018-06-06
嵌入式iframe子頁(yè)面與父頁(yè)面js通信的方法
這篇文章主要介紹了嵌入式iframe子頁(yè)面與父頁(yè)面js通信的方法,實(shí)例分析了嵌入式iframe子頁(yè)面與父頁(yè)面js通信的常用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-01-01

