Lập trình · 21/10/2026

Kiểm tra ngày trong JavaScript: tạo được Date chưa có nghĩa đầu vào hợp lệ

Ngày 30 tháng 2 không tồn tại nhưng một phép tạo Date lại cho ra ngày trong tháng 3. Date có cơ chế chuẩn hóa thành phần ngày; có được một object Date chưa chứng minh chuỗi người dùng nhập là ngày hợp lệ. Với trường chỉ chứa ngày, cần xác định cả định dạng lẫn ý nghĩa dữ liệu.

Kiểm tra ngày trong JavaScript: tạo được Date chưa có nghĩa đầu vào hợp lệ

Ngày 30 tháng 2 không tồn tại nhưng một phép tạo Date lại cho ra ngày trong tháng 3. Date có cơ chế chuẩn hóa thành phần ngày; có được một object Date chưa chứng minh chuỗi người dùng nhập là ngày hợp lệ. Với trường chỉ chứa ngày, cần xác định cả định dạng lẫn ý nghĩa dữ liệu.

1. Thành phần vượt phạm vi có thể được chuẩn hóa

const date = new Date(Date.UTC(2026, 1, 30));
console.log(date.toISOString()); // 2026-03-02T00:00:00.000Z
console.log(Number.isFinite(date.getTime())); // true

Tháng trong lời gọi Date.UTC dùng chỉ mục từ 0, nên 1 là tháng 2. Ngày 30 được chuẩn hóa sang tháng 3. Timestamp hữu hạn cho biết object biểu diễn được một thời điểm, không xác nhận bộ thành phần ban đầu tồn tại đúng như đã nhập.

2. Invalid Date vẫn là object truthy

const date = new Date('not-a-date');
console.log(Boolean(date)); // true
console.log(Number.isNaN(date.getTime())); // true
try {
  date.toISOString();
} catch (error) {
  console.log(error instanceof RangeError); // true
}

Kiểm tra if (date) không phát hiện ngày sai. Kiểm tra timestamp trước khi gọi toISOString để tránh lỗi xuất hiện muộn ở bước gửi dữ liệu hoặc ghi log. Cũng không nên dựa vào việc parser có nhận những định dạng tùy ý như 20/10/2026 hay không.

3. Kiểm tra định dạng và đối chiếu ngày sau phân tích

Helper sau nhận chuỗi đúng 10 ký tự theo YYYY-MM-DD, dùng năm ISO bốn chữ số và không trim. Nó kiểm tra bằng thời điểm UTC tạm thời rồi trả lại chuỗi ngày ban đầu. Quy tắc ngày được kiểm tra riêng với quy tắc nghiệp vụ như hạn cuối có được ở quá khứ không.

function validateDateOnly(text) {
  if (typeof text !== 'string' || text.length !== 10 ||
      !/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(text)) {
    throw new TypeError('Expected YYYY-MM-DD');
  }
  const expected = text + 'T00:00:00.000Z';
  const date = new Date(expected);
  if (!Number.isFinite(date.getTime()) || date.toISOString() !== expected) {
    throw new RangeError('Invalid calendar date');
  }
  return text;
}
console.log(validateDateOnly('2024-02-29')); // 2024-02-29
try {
  validateDateOnly('2026-02-30');
} catch (error) {
  console.log(error instanceof RangeError); // true
}

Nếu parser chuẩn hóa một ngày sang ngày khác, so sánh ISO sẽ phát hiện khác biệt. Nếu parser tạo Invalid Date, kiểm tra hữu hạn dừng trước khi gọi toISOString. Chuỗi rỗng, thiếu số 0 đầu hoặc có khoảng trắng bị từ chối theo hợp đồng hẹp này.

4. Ngày lịch không tự mang ý nghĩa thời điểm

const instant = new Date('2026-10-20T00:00:00.000Z');
function dayIn(timeZone) {
  return new Intl.DateTimeFormat('en-GB', {
    timeZone, day: '2-digit'
  }).formatToParts(instant).find(part => part.type === 'day').value;
}
console.log(dayIn('UTC')); // 20
console.log(dayIn('America/Los_Angeles')); // 19

Ví dụ dùng thời điểm cố định để minh họa cùng timestamp có thể hiển thị sang ngày trước ở múi giờ khác. Nếu dữ liệu là ngày sinh hoặc ngày trên biểu mẫu, giữ contract ngày lịch thay vì tự coi nó là nửa đêm UTC rồi format theo múi giờ người xem.

Nếu tính năng thật sự cần thời điểm hẹn, phải xác định giờ và múi giờ theo nghiệp vụ. Thời điểm UTC tạm dùng trong helper chỉ để kiểm tra ngày, không tự quyết định múi giờ của một cuộc hẹn.

5. Bộ ca kiểm thử nên giữ

  • Ngày nhuận hợp lệ và ngày 29 tháng 2 năm không nhuận.
  • Ngày 31 ở tháng chỉ có 30 ngày.
  • Tháng 00, tháng 13, ngày 00 và ngày 32.
  • Chuỗi rỗng, khoảng trắng, ký tự xuống dòng và thiếu số 0.
  • Giữ nguyên ngày lịch khi chuyển qua API.
  • Chỉ format timestamp với múi giờ đã chọn rõ.

Tám khối ví dụ Việt–Anh đã chạy bằng Node.js, kèm kiểm tra năm nhuận và đầu vào sai. Hai múi giờ trong ví dụ được chỉ định tường minh; chưa kiểm thử form trình duyệt hay API thực tế. Tham khảo MDN Date, toISOString và Intl.DateTimeFormat.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.