本文 AI 產出,尚未審核

JavaScript 日期與時間(Date & Time)── 建立 Date 物件

簡介

在前端與後端的程式開發中,日期與時間是最常被使用的資料型別之一。無論是顯示使用者的最後登入時間、計算兩個事件之間的間隔,或是產生具備時區資訊的報表,都離不開 Date 物件。

JavaScript 內建的 Date 類別提供了完整的日期/時間操作介面,但若對它的建立方式不熟悉,往往會出現時區錯誤、月份錯位等常見問題。本文將從最基礎的 建立 Date 物件 開始說明,帶你一步步掌握正確的使用方法,並透過實務範例展示如何在真實專案中運用。


核心概念

1. 為什麼要使用 new Date()

Date 是 JavaScript 的原生建構子,使用 new Date() 會回傳一個 可變動(mutable)的物件,內部保存了自 1970 年 1 月 1 日 00:00:00 UTC 起的毫秒數(稱為 timestamp)。透過這個時間戳,我們可以:

  • 取得或設定年、月、日、時、分、秒、毫秒
  • 轉換成不同的字串格式(ISO、RFC、Locale)
  • 計算時間差、做日期加減運算

注意Date 物件本身是 可變 的,對同一個實例的修改會直接影響原始值。若需要不可變的時間點,請使用 Date.now() 取得純數值,或在修改前先 clone

2. 建立方式概覽

建構子呼叫方式 產生的 Date 內容 典型使用情境
new Date() 目前本機時間 取得現在時間、顯示即時時鐘
new Date(value) value 為時間戳(毫秒)或 ISO 字串 由伺服器傳回的毫秒數或 ISO 8601 字串建立
new Date(dateString) 解析字串(支援 ISO、RFC、簡易格式) 直接使用固定字串如 '2024-12-31'
new Date(year, monthIndex, day, hour, minute, second, millisecond) 依參數建立,月以 0 起始(0=Jan) 手動組合日期、時間,常用於表單或日曆功能
Date.now()(非建構子) 直接回傳時間戳(毫秒) 需要純數值作為鍵值或計算時

下面分別說明每種方式的細節與注意點。


2.1 new Date():取得現在時間

// 取得目前本機時間的 Date 物件
const now = new Date();

console.log(now);               // 2025-11-19T07:45:32.123Z(依執行環境而異)
console.log(now.toString());    // Fri Nov 19 2025 15:45:32 GMT+0800 (Taipei Standard Time)

new Date() 會自動以 本機時區 產生時間。如果要取得 UTC 時間,可使用 now.toISOString()now.getUTC*() 系列方法。


2.2 new Date(value):從時間戳或 ISO 字串建立

// 1. 以毫秒時間戳建立(1970-01-01 起算)
const timestamp = 1735689600000; // 2025-01-01T00:00:00.000Z
const dateFromTs = new Date(timestamp);
console.log(dateFromTs.toUTCString()); // "Wed, 01 Jan 2025 00:00:00 GMT"

// 2. 以 ISO 8601 字串建立
const isoString = '2025-11-19T12:30:00+08:00';
const dateFromIso = new Date(isoString);
console.log(dateFromIso.toString()); // "Fri Nov 19 2025 12:30:00 GMT+0800 (Taipei Standard Time)"
  • 只要傳入 有效的數值(毫秒)或 ISO 8601 格式字串,Date 會自動解析為正確的時間點。
  • 非 ISO 格式的字串(如 '2025/11/19')在不同瀏覽器的解析結果可能不一致,建議統一使用 ISO

2.3 new Date(dateString):字串解析

// 簡易的日期字串(不含時區資訊)
// 會以本機時區當作解析基準
const simple = new Date('2025-12-31');
console.log(simple.toString()); // "Thu Dec 31 2025 00:00:00 GMT+0800 (Taipei Standard Time)"

// RFC 2822 格式
const rfc = new Date('Fri, 19 Nov 2025 15:00:00 GMT');
console.log(rfc.toUTCString()); // "Fri, 19 Nov 2025 15:00:00 GMT"

⚠️ 常見陷阱

  • new Date('2025-12-31') 只包含日期,時間會被補成 00:00:00
  • 若字串缺少時區資訊,會以 本機時區 解析,跨時區系統時需特別留意。

2.4 new Date(year, monthIndex, …):逐參數建立

// 注意:月份是 0 起始(0 = 1 月,11 = 12 月)
const birthday = new Date(1990, 4, 21, 8, 30, 0); // 1990-05-21 08:30:00
console.log(birthday.toLocaleString('zh-TW', { timeZone: 'Asia/Taipei' }));
// "1990/5/21 下午 8:30:00"

2.4.1 為什麼月份是 0 起始?

JavaScript 的設計者在早期為了與 Array 的索引保持一致,將月份設計為 0‑11。這是新手最常踩的坑,以下示範說明:

const march = new Date(2025, 2, 1); // 2025-03-01,因為 2 代表 3 月
console.log(march.getMonth()); // 2(仍是 0 起始)

若要顯示給使用者,務必使用 getMonth() + 1toLocaleString() 直接格式化。


2.5 Date.now():取得純時間戳

const nowTs = Date.now(); // 例如 1737367532123
// 可直接用於排序、作為唯一鍵值等
const logs = [
  { id: 1, time: 1737367000000 },
  { id: 2, time: nowTs },
];
logs.sort((a, b) => a.time - b.time);

Date.now() 不會產生 Date 物件,對效能要求高的迴圈或計時器特別有用。


程式碼範例

以下提供 5 個實務上常見 的建立 Date 方式,並加上詳細註解。

範例 1:顯示即時時鐘(每秒更新)

function startClock(containerId) {
  const el = document.getElementById(containerId);
  function update() {
    const now = new Date();                 // 取得現在時間
    // 以台北時區的本地化字串顯示
    el.textContent = now.toLocaleString('zh-TW', {
      hour12: false,
      timeZone: 'Asia/Taipei'
    });
  }
  update();                 // 立即顯示一次
  setInterval(update, 1000); // 每秒更新
}

實務意義:在儀表板、聊天訊息時間戳等 UI 中,常需要即時更新的時間顯示。


範例 2:將伺服器傳回的毫秒時間戳轉成可讀格式

// 假設從 API 取得的資料
fetch('/api/events')
  .then(r => r.json())
  .then(data => {
    data.forEach(event => {
      // event.time 為毫秒時間戳
      const d = new Date(event.time);
      const formatted = d.toLocaleDateString('zh-TW', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        hour12: false,
        timeZone: 'Asia/Taipei'
      });
      console.log(`事件 ${event.id} 發生於 ${formatted}`);
    });
  });

重點:使用 toLocaleDateString 可以一次完成日期與時間的本地化格式化。


範例 3:手動建立「生日」Date 物件,並計算年齡

function calculateAge(year, month, day) {
  // month 為 1~12,需減 1 轉為 0 起始
  const birth = new Date(year, month - 1, day);
  const now = new Date();

  let age = now.getFullYear() - birth.getFullYear();
  // 若當年尚未過生日,年齡減 1
  if (
    now.getMonth() < birth.getMonth() ||
    (now.getMonth() === birth.getMonth() && now.getDate() < birth.getDate())
  ) {
    age--;
  }
  return age;
}
console.log(calculateAge(1995, 8, 15)); // 依執行時間不同而異

應用:會員系統、保險投保年齡驗證等場景。


範例 4:跨時區顯示同一瞬間的本地時間

const utcNow = new Date('2025-11-19T04:00:00Z'); // UTC 時間

// 以不同時區格式化
const taipei = utcNow.toLocaleString('zh-TW', { timeZone: 'Asia/Taipei' });
const newYork = utcNow.toLocaleString('en-US', { timeZone: 'America/New_York' });
const london = utcNow.toLocaleString('en-GB', { timeZone: 'Europe/London' });

console.log('台北:', taipei);   // 台北: 2025/11/19 12:00:00
console.log('紐約:', newYork); // 紐約: 11/18/2025, 11:00:00 PM
console.log('倫敦:', london);  // 倫敦: 19/11/2025, 04:00:00

技巧toLocaleStringtimeZone 參數讓我們可以在同一 Date 物件上呈現多個時區的時間,避免手動計算時差。


範例 5:使用 Date.now() 實作簡易防止重複提交的 Token

function generateSubmitToken() {
  // 使用當前毫秒數 + 隨機字元組成唯一字串
  return `${Date.now().toString(36)}-${Math.random().toString(36).substr(2,5)}`;
}

// 表單送出前呼叫
const token = generateSubmitToken();
console.log('提交 Token:', token);
// => 1kz2e5c-9xv7q

實務價值:在需要防止重複提交(例如付款、訂單)時,快速產生唯一識別碼。


常見陷阱與最佳實踐

陷阱 說明 解決方案
月份 0 起始 new Date(2025, 11, 1) 產生 12 月 1 日,new Date(2025, 12, 1) 會自動跳到次年 1 月 1 日。 建立日期前先 month - 1,或使用 Date.UTC() 直接以 1 起始的月份建立 UTC 日期。
字串解析不一致 new Date('2025/11/19') 在 Chrome 會正確解析,但在 Safari 可能得到 Invalid Date 統一使用 ISO 8601YYYY-MM-DDTHH:mm:ss.sssZ)或手動分割字串後使用參數建構子。
時區自動轉換 new Date('2025-11-19') 會以本機時區解讀,導致跨時區顯示錯誤。 若要固定為 UTC,使用 new Date(Date.UTC(year, month, day));或在格式化時指定 timeZone
Date 物件可變 let d = new Date(); d.setFullYear(2000); 會改變原始物件。 若需要保留原始值,先 const clone = new Date(d.getTime()); 再操作 clone。
毫秒精度與性能 在大量計算(如動畫)中頻繁呼叫 new Date() 會產生額外 GC。 使用 performance.now()(高解析度)或 Date.now() 取得純數值,減少物件建立。

最佳實踐小結

  1. 統一使用 ISO 8601:避免跨瀏覽器解析差異。
  2. 明確指定時區:在 toLocaleStringtoLocaleDateStringtoLocaleTimeString 時使用 timeZone 參數。
  3. 封裝日期工具:將常用的建立、格式化、計算功能抽成模組(如 dateUtils.js),提升可讀性與維護性。
  4. 避免直接操作全域 Date:盡量使用 const now = Date.now(); 取得時間戳,再根據需求轉換。
  5. 測試跨時區情境:特別是涉及國際化的應用,務必在不同時區的環境下驗證結果。

實際應用場景

場景 需求 建議的 Date 建立方式
訂單系統:記錄下單時間、計算付款期限(48 小時) 必須精確到毫秒,並避免時區漂移 使用 Date.now() 取得時間戳,存入資料庫;取出後 new Date(timestamp) 轉為 Date 物件計算。
行事曆 UI:顯示每月日曆,支援跨月切換 需要根據月份產生首日與最後一日 new Date(year, monthIndex, 1) 產生當月第一天;new Date(year, monthIndex + 1, 0) 取得當月最後一天(day=0 會回到前一月的最後一天)。
國際化報表:同一筆交易在不同國家顯示本地時間 必須根據使用者時區渲染 先以 UTC 時間儲存(new Date(Date.UTC(...))),顯示時使用 toLocaleString('zh-TW', { timeZone: userTZ })
倒數計時:活動倒數 10 天 5 小時 需要持續更新且效能佳 使用 Date.now()setInterval,每秒計算 target - Date.now(),不必每次都建立 Date 物件。
日誌系統:高頻率寫入時間戳 需要低延遲、低記憶體占用 直接使用 Date.now(),避免 new Date() 產生額外物件。

總結

建立 Date 物件是 JavaScript 中處理時間的第一步,也是最容易產生錯誤的環節。本文從 基礎的 new Date()時間戳與字串解析逐參數建立,到 Date.now() 的差異,提供了完整的概念說明與實務範例。透過 統一使用 ISO 8601明確指定時區避免月份 0 起始的陷阱,以及 封裝工具函式 的最佳實踐,你可以在任何前端或後端專案中自信地操作日期與時間。

關鍵要點

  • 建立 時,優先選擇 new Date(value)(時間戳或 ISO)或 new Date(year, monthIndex, …),避免瀏覽器差異。
  • 格式化 時,使用 toLocaleString 並指定 timeZone,確保跨時區正確顯示。
  • 計算 時,盡量操作毫秒時間戳(Date.now()),減少不必要的物件建立。

掌握了這些技巧,你就能在專案中正確、有效率地處理所有日期與時間相關的需求。祝開發順利,時間掌控在你手中!