KoderKoder.ai
Bảng giáDoanh nghiệpGiáo dụcDành cho nhà đầu tư
Đăng nhậpBắt đầu

Sản phẩm

Bảng giáDoanh nghiệpDành cho nhà đầu tư

Tài nguyên

Liên hệHỗ trợGiáo dụcBlog

Pháp lý

Chính sách bảo mậtĐiều khoản sử dụngBảo mậtChính sách sử dụng chấp nhận đượcBáo cáo vi phạm

Mạng xã hội

LinkedInTwitter
Koder.ai
Ngôn ngữ

© 2026 Koder.ai. Bảo lưu mọi quyền.

Trang chủ›Blog›Định dạng và Chuyển đổi Thời gian trong JavaScript: Những cạm bẫy thường gặp
06 thg 9, 2025·8 phút

Định dạng và Chuyển đổi Thời gian trong JavaScript: Những cạm bẫy thường gặp

Tìm hiểu cách định dạng và chuyển đổi thời gian trong JavaScript mà không gặp bất ngờ: timestamp, chuỗi ISO, múi giờ, DST, quy tắc phân tích và các mẫu đáng tin cậy.

Định dạng và Chuyển đổi Thời gian trong JavaScript: Những cạm bẫy thường gặp

Những vấn đề thường gặp với thời gian trong JavaScript

Các lỗi liên quan thời gian trong JavaScript hiếm khi rõ ràng như “đồng hồ sai.” Thay vào đó chúng xuất hiện dưới dạng những dịch chuyển nhỏ gây bối rối: một ngày đúng trên máy của bạn nhưng sai trên máy đồng nghiệp, một phản hồi API trông ổn cho tới khi hiển thị ở múi giờ khác, hoặc một báo cáo “lép vế một đơn vị” quanh thay đổi mùa.

Các triệu chứng phổ biến nhất

Bạn thường sẽ nhận ra một (hoặc nhiều) trong số này:

  • Lệch một giờ: đặc biệt quanh Daylight Saving Time, hoặc khi một giá trị bị chuyển đổi lẫn giữa thời gian cục bộ và UTC một cách vô ý.
  • Lệch một ngày: một giá trị chỉ ngày (ví dụ “2025-12-23”) xuất hiện là ngày trước/kế tiếp tùy theo múi giờ.
  • Múi giờ sai: thời gian trông đúng, nhưng offset (ví dụ +02:00) không phải thứ bạn mong đợi.
  • Định dạng không nhất quán: “chạy được trên Chrome” nhưng khác trên Safari, hoặc server và trình duyệt không đồng ý cách phân tích một chuỗi.

Tại sao điều này xảy ra: “thời gian” có thể mang nhiều nghĩa

Một nguồn đau đầu lớn là từ việc từ thời gian có thể ám chỉ nhiều khái niệm khác nhau:

  • Một khoảnh khắc (instant): một thời điểm cụ thể trên toàn cầu (ví dụ “2025-12-23T10:00:00Z”). Đây thường là thứ bạn muốn dùng cho logging, sự kiện, và lưu trữ API.
  • Một ngày lịch: một ngày trên lịch không kèm múi giờ (ví dụ sinh nhật, ngày hóa đơn). Xử lý nó như một instant có thể làm dịch ngày qua ranh giới ngày.
  • Thời gian đồng hồ địa phương (wall-clock time): “9:00 AM ở Berlin,” phụ thuộc luật múi giờ và thay đổi DST.

Date tích hợp của JavaScript cố gắng bao phủ tất cả, nhưng nó chủ yếu biểu diễn một instant trong khi liên tục thúc bạn về hiển thị cục bộ, khiến việc chuyển đổi vô ý trở nên dễ dàng.

Hướng dẫn này tập trung vào điều gì

Hướng dẫn này thực tế: làm sao để có chuyển đổi dự đoán được giữa trình duyệt và server, chọn định dạng an toàn hơn (như ISO 8601), và nhận diện những bẫy kinh điển (giây vs mili giây, UTC vs local, và khác biệt khi phân tích). Mục tiêu không phải lý thuyết nhiều hơn—mà là giảm những bất ngờ kiểu “tại sao lại dịch?”

Các kiểu dữ liệu thời gian: Timestamp, Date và Chuỗi

Lỗi thời gian trong JavaScript thường bắt đầu bằng việc trộn các biểu diễn trông giống nhau nhưng không phải.

Ba dạng bạn sẽ gặp nhiều nhất

1) Epoch milliseconds (number)

Một số thuần như 1735689600000 thường là “mili giây kể từ 1970-01-01T00:00:00Z”. Nó biểu diễn một instant mà không kèm định dạng hay múi giờ.

2) Đối tượng Date (wrapper quanh một instant)

Một Date lưu cùng loại instant như một timestamp. Phần gây nhầm: khi bạn in một Date, JavaScript định dạng nó theo quy tắc cục bộ của môi trường trừ khi bạn yêu cầu khác.

3) Chuỗi định dạng (dành cho người đọc)

Chuỗi như "2025-01-01", "01/01/2025 10:00", hoặc "2025-01-01T00:00:00Z" không phải một thứ duy nhất. Một số rõ ràng (ISO 8601 với Z), số khác phụ thuộc ngôn ngữ, và một vài chuỗi không bao gồm múi giờ.

“Instant” vs “hiển thị cho người dùng”

  • Instant: “thời điểm chính xác trên toàn cầu” (tốt nhất lưu dưới dạng epoch ms hoặc chuỗi ISO UTC).
  • Hiển thị cho người dùng: “điều người dùng nên thấy” (phụ thuộc locale và múi giờ).

Một instant có thể hiển thị khác nhau theo múi giờ:

const instant = new Date("2025-01-01T00:00:00Z");

instant.toLocaleString("en-US", { timeZone: "UTC" });
// "1/1/2025, 12:00:00 AM"

instant.toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
// "12/31/2024, 4:00:00 PM" (previous day)

Chọn một “nguồn chân lý” (source of truth)

Chọn một biểu diễn nội bộ duy nhất (thường là epoch milliseconds hoặc UTC ISO 8601) và giữ nhất quán trên toàn app và API. Chỉ chuyển đổi đến/đi Date và chuỗi định dạng ở biên: khi nhận input và khi hiển thị UI.

Timestamps: Giây vs Mili giây (dễ nhầm)

“Timestamp” thường nghĩa là epoch time (còn gọi Unix time): số đơn vị kể từ 1970-01-01 00:00:00 UTC. Khó khăn: các hệ thống khác nhau dùng đơn vị khác nhau.

Date của JavaScript là nguồn gây nhầm vì nó dùng mili giây. Nhiều API, cơ sở dữ liệu, và log dùng giây.

Quy tắc thực hành

  • Unix timestamp (giây): 1704067200
  • JavaScript timestamp (mili giây): 1704067200000

Cùng một thời điểm, nhưng phiên bản mili có ba chữ số 0 thêm vào.

Chuyển đổi an toàn (giây ↔ mili giây)

Dùng phép nhân/chia rõ ràng để đơn vị hiển nhiên:

// seconds -> Date
const seconds = 1704067200;
const d1 = new Date(seconds * 1000);

// milliseconds -> Date
const ms = 1704067200000;
const d2 = new Date(ms);

// Date -> seconds
const secondsOut = Math.floor(d2.getTime() / 1000);

// Date -> milliseconds
const msOut = d2.getTime();

Lỗi kinh điển: truyền giây vào Date()

Trông có vẻ hợp lý nhưng sai khi ts ở đơn vị giây:

const ts = 1704067200;      // seconds
const d = new Date(ts);     // WRONG: treated as milliseconds

Kết quả sẽ là một ngày ở 1970, vì 1,704,067,200 mili giây chỉ khoảng 19 ngày sau epoch.

Kiểm tra nhanh khi debug

Khi không chắc đơn vị, thêm các bảo vệ nhanh:

function asDateFromUnknownEpoch(x) {
  // heuristic thô: giây ~1e9-1e10, mili giây ~1e12-1e13
  if (x < 1e11) return new Date(x * 1000); // giả sử là giây
  return new Date(x);                      // giả sử là mili giây
}

const input = Number(valueFromApi);
console.log({ input, digits: String(Math.trunc(input)).length });
console.log('as ISO:', asDateFromUnknownEpoch(input).toISOString());

Nếu số chữ số ~10 thì có lẽ là giây. Nếu ~13 thì là mili giây. In toISOString() khi debug: nó rõ ràng và giúp bạn phát hiện sai đơn vị ngay.

Thời gian cục bộ vs UTC: tại sao output dịch

Date của JavaScript có thể gây rối vì nó lưu một instant duy nhất, nhưng có thể hiển thị instant đó ở các múi giờ khác nhau.

Nội bộ, Date về cơ bản là “mili giây kể từ epoch Unix (1970-01-01T00:00:00Z)”. Con số đó biểu diễn một thời điểm UTC. “Sự dịch” xảy ra khi bạn yêu cầu JavaScript định dạng thời điểm đó theo thời gian cục bộ (dựa trên cài đặt máy) so với UTC.

Getter cục bộ vs getter UTC

Nhiều API Date có biến thể cục bộ và UTC. Chúng trả về các giá trị khác nhau cho cùng một instant:

const d = new Date('2025-01-01T00:30:00Z');

d.getHours();      // giờ theo múi giờ *cục bộ*
d.getUTCHours();   // giờ theo UTC

d.toString();      // chuỗi thời gian local
d.toISOString();   // UTC (luôn kết thúc với Z)

Nếu máy bạn ở New York (UTC-5), thời gian UTC đó có thể hiển thị là “19:30” vào ngày trước đó ở local. Trên server đặt về UTC, nó sẽ xuất là “00:30”. Cùng một instant, hiển thị khác nhau.

Tại sao log trông “sai”

Log thường dùng Date#toString() hoặc nội suy Date một cách ngầm định, và đó là chuỗi theo múi giờ môi trường. Điều này khiến cùng mã có thể in các timestamp khác nhau trên laptop, CI và production.

Hướng dẫn thực tế

Lưu và truyền thời gian dưới dạng UTC (ví dụ epoch mili giây hoặc ISO 8601 với Z). Chuyển đổi sang múi giờ người dùng chỉ khi hiển thị:

  • Với API: ưu tiên toISOString() hoặc gửi epoch mili giây
  • Với UI: định dạng theo múi giờ người dùng bằng Intl.DateTimeFormat

Nếu bạn phát triển nhanh (ví dụ với workflow tạo ứng dụng trong Koder.ai), nên gắn quy ước này vào contract API từ đầu: đặt tên trường rõ ràng (createdAtMs, createdAtIso) và giữ server (Go + PostgreSQL) và client (React) đồng ý về ý nghĩa mỗi trường.

Chuỗi ISO 8601: Định dạng an toàn nhất cho API

Nếu cần chuyển ngày/giờ giữa trình duyệt, server và DB, chuỗi ISO 8601 là mặc định an toàn. Chúng rõ ràng, được hỗ trợ rộng rãi, và (quan trọng nhất) mang theo thông tin múi giờ.

Dùng UTC rõ ràng hoặc offset rõ ràng

Hai định dạng trao đổi tốt:

  • UTC (khuyến nghị khi bạn không quan tâm múi giờ cục bộ): 2025-03-04T12:30:00Z
  • Thời gian cục bộ kèm offset (khi thời gian đồng hồ địa phương quan trọng): 2025-03-04T12:30:00+02:00

Z nghĩa là gì?

Z là viết tắt của Zulu time, tên khác của UTC. Vậy 2025-03-04T12:30:00Z là “12:30 theo UTC”.

Khi nào offset như +02:00 quan trọng?

Offset quan trọng khi một sự kiện gắn với bối cảnh múi giờ địa phương (hẹn giờ, đặt chỗ, giờ mở cửa). 2025-03-04T12:30:00+02:00 mô tả một khoảnh khắc sớm hơn 2 giờ so với UTC, và không cùng instant với 2025-03-04T12:30:00Z.

Tránh chuỗi mơ hồ

Chuỗi như 03/04/2025 là cạm bẫy: là 4 tháng 3 hay 3 tháng 4? Người dùng và môi trường khác nhau hiểu khác nhau. Ưu tiên 2025-03-04 (ISO date) hoặc full ISO datetime.

Round-trip an toàn (string → Date → string)

const iso = "2025-03-04T12:30:00Z";
const d = new Date(iso);
const back = d.toISOString();

console.log(iso);  // 2025-03-04T12:30:00Z
console.log(back); // 2025-03-04T12:30:00.000Z

Hành vi “round-trip” này là thứ bạn muốn cho API: nhất quán, có thể dự đoán và nhận biết múi giờ.

Bẫy khi phân tích: Date.parse và khác biệt giữa trình duyệt

Sửa lỗi phân tích ngày
Mô tả chuỗi đầu vào của bạn và nhận mã phân tích an toàn, tránh Date.parse.
Bắt đầu xây dựng

Date.parse() có vẻ tiện: đưa chuỗi, nhận timestamp. Vấn đề là với những chuỗi không rõ ràng theo ISO 8601, việc phân tích phụ thuộc heuristic của trình duyệt. Những heuristic này khác nhau giữa engine và phiên bản, nghĩa là cùng một input có thể parse khác (hoặc không parse) tùy nơi chạy mã.

Tại sao Date.parse() khác nhau

JavaScript chỉ chuẩn hóa việc parse đáng tin cậy cho chuỗi dạng ISO 8601 (và ngay cả khi đó, chi tiết như múi giờ vẫn quan trọng). Với các định dạng “thân thiện”—như "03/04/2025", "March 4, 2025", hoặc "2025-3-4"—trình duyệt có thể hiểu:

  • Thứ tự tháng/ngày dựa trên giả định locale
  • Thiếu múi giờ có thể được coi là thời gian cục bộ ở engine này, nhưng bị từ chối ở engine khác
  • Chuỗi hơi sai định dạng có thể được coi là “đủ gần”… cho tới khi không

Nếu bạn không thể dự đoán chính xác hình dạng chuỗi, bạn không thể dự đoán kết quả.

Trường hợp bất ngờ: YYYY-MM-DD

Bẫy phổ biến là dạng plain "YYYY-MM-DD" (ví dụ "2025-01-15"). Nhiều dev mong đợi nó được hiểu là đêm khuya local. Thực tế, một số môi trường coi dạng này là đêm khuya UTC.

Sự khác biệt này quan trọng: đêm khuya UTC chuyển sang thời gian cục bộ có thể thành ngày trước ở các múi giờ âm (ví dụ châu Mỹ) hoặc dịch giờ bất ngờ. Đây là cách dễ dàng để có lỗi “tại sao ngày bị lệch một ngày?”.

Danh sách kiểm tra phân tích: input người dùng vs input server

Với input server/API:

  • Ưu tiên ISO 8601 đầy đủ với múi giờ rõ ràng, ví dụ 2025-01-15T13:45:00Z hoặc 2025-01-15T13:45:00+02:00.
  • Đối với giá trị chỉ ngày, xử lý như dữ liệu, không phải một khoảnh khắc. Nếu đó là sinh nhật hay hạn chót, giữ nguyên chuỗi ("YYYY-MM-DD") và tránh chuyển nó thành Date trừ khi bạn xác định múi giờ.

Với input người dùng:

  • Đừng chấp nhận định dạng mơ hồ như 03/04/2025 trừ khi UI ép ý nghĩa.
  • Ưu tiên input điều khiển (date picker) trả về định dạng biết trước.
  • Nếu phải parse text tự do, định nghĩa và ép các định dạng được chấp nhận ngay từ đầu.

Dùng quy tắc rõ ràng (không phải heuristic)

Thay vì trông chờ Date.parse() “tự suy luận”, chọn một trong các mẫu sau:

  • Chỉ chấp nhận ISO 8601 từ server; từ chối mọi thứ khác.
  • Parse thủ công các định dạng đã biết (chia chuỗi rồi dùng new Date(year, monthIndex, day) cho ngày local).
  • Lưu và truyền timestamp (epoch mili giây) cho các instant chính xác, và định dạng khi hiển thị.

Khi dữ liệu thời gian quan trọng, “nó parse trên máy tôi” là chưa đủ—hãy làm cho quy tắc parse rõ ràng và nhất quán.

Định dạng cho người dùng với Intl.DateTimeFormat

Nếu mục tiêu là “hiển thị ngày/giờ theo cách người dùng mong đợi”, công cụ tốt nhất trong JavaScript là Intl.DateTimeFormat. Nó dùng quy tắc locale của người dùng (thứ tự, dấu phân cách, tên tháng) và tránh cách dựng chuỗi mong manh như month + '/' + day.

Tại sao nó thắng việc tự xây chuỗi

Tự format thường cố định kiểu US, quên số 0 ở đầu, hoặc tạo kết quả 24/12 giờ gây nhầm. Intl.DateTimeFormat cũng cho bạn rõ ràng múi giờ nào đang hiển thị—rất quan trọng khi dữ liệu lưu dưới UTC nhưng UI cần phản ánh thời gian cục bộ.

Các tuỳ chọn hay dùng

Với “chỉ hiển thị đẹp”, dateStyle và timeStyle là đơn giản nhất:

const d = new Date('2025-01-05T16:30:00Z');

// Locale người dùng + múi giờ local của người dùng
console.log(new Intl.DateTimeFormat(undefined, {
  dateStyle: 'medium',
  timeStyle: 'short'
}).format(d));

// Ép một múi giờ cụ thể (tốt cho thời gian sự kiện)
console.log(new Intl.DateTimeFormat('en-GB', {
  dateStyle: 'full',
  timeStyle: 'short',
  timeZone: 'UTC'
}).format(d));

Nếu bạn cần chu kỳ giờ nhất quán (ví dụ toggle 24/12), dùng hour12:

console.log(new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: '2-digit',
  hour12: true
}).format(d));

Mẫu UI thực tế

Chọn một hàm định dạng cho mỗi “loại” timestamp trong UI (thời gian tin nhắn, log, bắt đầu sự kiện), và giữ quyết định timeZone có chủ ý:

  • Dùng thời gian cục bộ cho “khi nó xảy ra với tôi”.
  • Dùng múi giờ cố định (thường UTC) cho audit log, sự kiện server, hoặc phối hợp đa nhóm.

Cách này cho bạn output nhất quán, thân thiện locale mà không cần duy trì bộ chuỗi format mong manh.

Daylight Saving Time: lỗi ẩn lệch một giờ

Chuẩn hóa timestamp cho toàn hệ thống
Tạo helper chia sẻ cho seconds vs milliseconds để mọi dịch vụ dùng cùng đơn vị.
Tạo dự án

Daylight Saving Time (DST) là khi múi giờ thay đổi offset UTC (thường một giờ) vào ngày nhất định. Phức tạp nằm ở chỗ: DST không chỉ “thay offset”—mà còn thay đổi sự tồn tại của một số thời điểm local.

Giờ địa phương bị mất và bị trùng

Khi đồng hồ nhảy lên (spring forward), một khoảng thời gian local không xảy ra. Ví dụ, ở nhiều vùng, đồng hồ nhảy từ 01:59 lên 03:00, nên 02:30 local là “không tồn tại.”

Khi đồng hồ quay lại (fall back), một khoảng local xảy ra hai lần. Ví dụ, 01:30 có thể xuất hiện một lần trước chuyển và một lần sau, nghĩa là cùng thời gian đồng hồ có thể ám chỉ hai instant khác nhau.

Cộng 24 giờ vs “cùng giờ địa phương ngày mai”

Hai việc này không tương đương quanh ranh DST:

  • Cộng 24 giờ: “chính xác 24 * 60 * 60 giây sau”
  • Ngày mai cùng giờ địa phương: “ngày mai lúc 9:00 AM theo múi giờ này”

Nếu DST bắt đầu tối nay, “ngày mai 9:00” có thể chỉ cách bạn 23 giờ. Nếu DST kết thúc tối nay, có thể là 25 giờ.

// Kịch bản: lên lịch “cùng giờ local ngày mai”
const d = new Date(2025, 2, 8, 9, 0); // Mar 8, 9:00 local

const plus24h = new Date(d.getTime() + 24 * 60 * 60 * 1000);
const nextDaySameLocal = new Date(d);
nextDaySameLocal.setDate(d.getDate() + 1);

// Quanh DST, plus24h và nextDaySameLocal có thể khác nhau 1 giờ.

Tại sao setHours gây bất ngờ

Nếu bạn làm date.setHours(2, 30, 0, 0) vào ngày “spring forward”, JavaScript có thể điều chỉnh nó thành thời gian hợp lệ khác (thường là 03:30), vì 02:30 không tồn tại local.

Cách an toàn hơn

  • Làm toán lượng thời gian bằng UTC cho khoảng thời gian trôi qua (durations): dùng epoch mili giây và các method UTC.
  • Với lên lịch theo local, hãy rõ ràng về ý định: “ngày mai cùng 9:00 local” nên dùng phép toán theo lịch (setDate) thay vì cộng mili giây.
  • Khi trao đổi thời gian qua API, ưu tiên ISO 8601 với offset hoặc Z để instant không mơ hồ.

Durations vs Dates: đừng dùng Date cho bộ đếm thời gian

Một nguồn lỗi phổ biến là dùng Date để biểu diễn thứ không phải khoảnh khắc lịch.

Một timestamp trả lời “khi nào nó xảy ra?” (một instant cụ thể như 2025-12-23T10:00:00Z). Một duration trả lời “kéo dài bao lâu?” (như “3 phút 12 giây”). Hai khái niệm khác nhau, và trộn chúng dẫn đến toán học rối và hiệu ứng múi giờ/DST bất ngờ.

Tại sao Date không phù hợp cho duration

Date luôn biểu diễn một điểm trên timeline so với epoch. Nếu bạn lưu “90 giây” dưới dạng Date, bạn thực ra lưu “1970-01-01 cộng 90 giây” theo một múi giờ. Khi format, nó có thể hiện 01:01:30, dịch 1 giờ, hoặc chọn một ngày bạn không mong.

Với duration, ưu tiên số thuần:

  • Lưu durations dưới dạng giây hoặc mili giây (chọn 1 đơn vị và giữ nguyên).
  • Làm toán với số.
  • Chỉ chuyển thành chuỗi hiển thị ở cuối.

Chuyển giây sang HH:mm:ss

Một formatter đơn giản cho countdown hoặc độ dài media:

function formatHMS(totalSeconds) {
  const s = Math.max(0, Math.floor(totalSeconds));
  const hh = String(Math.floor(s / 3600)).padStart(2, "0");
  const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
  const ss = String(s % 60).padStart(2, "0");
  return `${hh}:${mm}:${ss}`;
}

formatHMS(75);    // "00:01:15" (countdown timer)
formatHMS(5423);  // "01:30:23" (media duration)

Nếu bạn chuyển từ phút, nhân trước (minutes * 60) và giữ giá trị là số cho tới khi render.

So sánh, sắp xếp và phạm vi mà không bị bất ngờ

Khi so sánh thời gian trong JavaScript, cách an toàn nhất là so sánh số, không phải văn bản đã định dạng. Một Date về cơ bản là wrapper quanh số epoch mili giây, nên bạn muốn so sánh thành “số vs số”.

So sánh an toàn (timestamps thắng)

Dùng getTime() (hoặc Date.valueOf()) để so sánh đáng tin cậy:

const a = new Date('2025-01-10T12:00:00Z');
const b = new Date('2025-01-10T12:00:01Z');

if (a.getTime() < b.getTime()) {
  // a is earlier
}

// Cũng được:
if (+a < +b) {
  // unary + gọi valueOf()
}

Tránh so sánh chuỗi đã format như "1/10/2025, 12:00 PM"—chúng phụ thuộc locale và không sắp đúng. Ngoại lệ chính là chuỗi ISO 8601 cùng định dạng và cùng múi giờ (ví dụ tất cả ...Z), vốn có thể so sánh theo thứ tự từ điển.

Sắp xếp và lọc theo phạm vi

Sắp xếp theo thời gian dễ nếu bạn sắp theo epoch mili giây:

items.sort((x, y) => new Date(x.createdAt).getTime() - new Date(y.createdAt).getTime());

Lọc phần tử trong một phạm vi tương tự:

const start = new Date('2025-01-01T00:00:00Z').getTime();
const end   = new Date('2025-02-01T00:00:00Z').getTime();

const inRange = items.filter(i => {
  const t = new Date(i.createdAt).getTime();
  return t >= start && t < end;
});

“Bắt đầu/kết thúc ngày” (local vs UTC)

“Bắt đầu ngày” phụ thuộc bạn muốn là local hay UTC:

// Bắt đầu/kết thúc ngày local
const d = new Date(2025, 0, 10); // Jan 10 trong local
const localStart = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
const localEnd   = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);

// Bắt đầu/kết thúc ngày UTC
const utcStart = new Date(Date.UTC(2025, 0, 10, 0, 0, 0, 0));
const utcEnd   = new Date(Date.UTC(2025, 0, 10, 23, 59, 59, 999));

Chọn một định nghĩa sớm và giữ nguyên xuyên suốt logic so sánh và phạm vi.

Danh sách kiểm tra debug: Cách chẩn đoán lỗi chuyển đổi thời gian

Ra mắt trên domain của bạn
Công bố ứng dụng trên domain tuỳ chỉnh khi UI ngày-giờ đã sẵn sàng.
Go Live

Lỗi thời gian có vẻ ngẫu nhiên cho đến khi bạn xác định bạn có gì (timestamp? chuỗi? Date?) và nơi xảy ra dịch (parse, chuyển múi giờ, định dạng).

1) Ghi nhận “ba góc nhìn” của cùng một thời điểm

Bắt đầu bằng việc log cùng một giá trị theo ba cách khác nhau. Điều này nhanh chóng lộ ra bạn đang thiếu giây hay mili, local hay UTC, hay lỗi parse.

console.log('raw input:', input);

const d = new Date(input);
console.log('toISOString (UTC):', d.toISOString());
console.log('toString (local):', d.toString());
console.log('timezone offset (min):', d.getTimezoneOffset());

Cần nhìn gì:

  • Nếu toISOString() sai lạc lớn (ví dụ năm 1970 hoặc tương lai xa), nghi ngờ giây vs mili giây.
  • Nếu toISOString() đúng nhưng toString() “dịch”, bạn đang thấy hiển thị múi giờ local.
  • Nếu getTimezoneOffset() thay đổi theo ngày, bạn đang qua DST.

2) Xác minh môi trường: múi giờ và locale

Nhiều báo cáo “chạy được trên máy tôi” chỉ là mặc định môi trường khác nhau.

  • Trình duyệt: kiểm tra cài đặt múi giờ OS và ngôn ngữ trình duyệt. Sau đó log:
console.log(Intl.DateTimeFormat().resolvedOptions());
  • Node.js / server: xác nhận múi giờ process:
console.log('TZ:', process.env.TZ);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);

Nếu server chạy UTC nhưng laptop bạn chạy local, output đã format sẽ khác trừ khi bạn chỉ định timeZone rõ ràng.

3) Thêm test ở chỗ thời gian thường vỡ

Viết unit test quanh biên DST và các thời điểm “edge”:

  • Một giờ trước và sau lúc chuyển DST ở vùng liên quan
  • Cuối tháng/năm, và các chuyển 23:30 → 00:30
  • Nhiều múi giờ nếu sản phẩm hỗ trợ

Nếu bạn lặp nhanh, cân nhắc đưa các test này vào scaffold. Ví dụ, khi tạo app React + Go trong Koder.ai, bạn có thể thêm bộ test “hợp đồng thời gian” nhỏ ngay từ đầu (ví dụ payload API + assert parse/format) để bắt regressions trước khi deploy.

Danh sách kiểm tra trước khi phát hành

  • Inputs có hợp đồng rõ: epoch milliseconds hoặc ISO 8601 có offset.
  • Không có chuỗi mơ hồ như "2025-03-02 10:00".
  • Khi format, luôn chỉ định locale và (khi cần) timeZone.
  • Tests bao phủ biên DST cho các vùng mục tiêu.

Các mẫu khuyến nghị để xử lý thời gian đáng tin cậy

Xử lý thời gian đáng tin phần lớn dựa vào chọn một “nguồn chân lý” và giữ nhất quán từ lưu trữ đến hiển thị.

Một tập best practices đơn giản

Lưu và tính toán bằng UTC. Xem thời gian local của người dùng là chi tiết trình bày.

Truyền ngày giữa hệ thống bằng chuỗi ISO 8601 kèm offset rõ (ưu tiên Z). Nếu gửi epoch số, ghi chú đơn vị và giữ nhất quán (mili giây là mặc định phổ biến trong JS).

Dùng Intl.DateTimeFormat (hoặc toLocaleString) để format cho người dùng, và truyền timeZone rõ khi cần output xác định (ví dụ luôn hiển thị UTC hoặc vùng kinh doanh cụ thể).

Hướng dẫn quyết định: DB vs API vs UI

  • Database: lưu một instant dưới dạng UTC (epoch mili giây hoặc datetime UTC). Tránh datetime local trừ khi miền của bạn thực sự lưu thời gian đồng hồ (ví dụ “cửa hàng mở lúc 09:00”).
  • API boundary: ưu tiên ISO 8601 có Z (ví dụ 2025-12-23T10:15:00Z). Nếu dùng epoch, đặt tên trường rõ như createdAtMs để đơn vị hiển nhiên.
  • UI: lấy instant UTC đã lưu và format cho locale người dùng. Với UI lên lịch, ghi rõ múi giờ và giữ chuyển đổi rõ ràng.

Khi nào nên dùng thư viện

Cân nhắc một thư viện chuyên cho date-time nếu bạn cần sự kiện định kỳ, luật múi giờ phức tạp, toán DST-safe (“cùng giờ local ngày mai”), hoặc nhiều parse từ input không nhất quán. Giá trị nằm ở API rõ ràng hơn và ít edge-case hơn.

Nếu bạn muốn đi sâu, tham khảo thêm các hướng dẫn liên quan đến thời gian trong blog. Nếu bạn đang đánh giá công cụ hoặc tùy chọn hỗ trợ, xem phần định giá.

Mục lục
Những vấn đề thường gặp với thời gian trong JavaScriptCác kiểu dữ liệu thời gian: Timestamp, Date và ChuỗiTimestamps: Giây vs Mili giây (dễ nhầm)Thời gian cục bộ vs UTC: tại sao output dịchChuỗi ISO 8601: Định dạng an toàn nhất cho APIBẫy khi phân tích: Date.parse và khác biệt giữa trình duyệtĐịnh dạng cho người dùng với Intl.DateTimeFormatDaylight Saving Time: lỗi ẩn lệch một giờDurations vs Dates: đừng dùng Date cho bộ đếm thời gianSo sánh, sắp xếp và phạm vi mà không bị bất ngờDanh sách kiểm tra debug: Cách chẩn đoán lỗi chuyển đổi thời gianCác mẫu khuyến nghị để xử lý thời gian đáng tin cậy
Chia sẻ
Koder.ai
Build your own app with Koder today!

The best way to understand the power of Koder is to see it for yourself.

Start FreeBook a Demo