6 điểm bởi GN⁺ 2026-01-13 | 1 bình luận | Chia sẻ qua WhatsApp
  • Chỉ ra tính không hoàn chỉnh và thiếu nhất quán của đối tượng Date hiện có trong JavaScript, đồng thời giới thiệu Temporal API sẽ thay thế nó
  • Date hoạt động như một đối tượng có thể thay đổi (mutable object), lệch với khái niệm ngày tháng thực tế, đồng thời có các vấn đề cấu trúc như lỗi phân tích cú pháp và giới hạn trong xử lý múi giờ
  • Temporal cung cấp mô hình xử lý ngày·giờ mới dựa trên tính bất biến, bao gồm các lớp được phân chia chi tiết như PlainDate, ZonedDateTime, Duration
  • Các phương thức của Temporal không sửa đổi đối tượng hiện có mà trả về đối tượng mới, cho phép phép toán chaining rõ ràng và an toàn
  • Temporal hiện đang ở giai đoạn chuẩn hóa 3 (Stage 3) và được hỗ trợ thử nghiệm trên các trình duyệt hiện đại như Chrome và Firefox

Vấn đề của đối tượng Date trong JavaScript

  • Hàm khởi tạo Date gây nhầm lẫn vì quy tắc phân tích cú pháp không nhất quánđánh chỉ số thiếu trực quan
    • Ví dụ: tháng (month) bắt đầu từ 0, nhưng ngày (day) và năm (year) lại bắt đầu từ 1
    • Chuỗi "99" được hiểu là năm 1999, còn "100" lại được hiểu là năm 0100, cho thấy sự thiếu nhất quán
  • Date được thiết kế xoay quanh thời gian (time) và được lưu trữ nội bộ dưới dạng Unix timestamp (đơn vị mili giây)
  • Hỗ trợ múi giờ (time zone) bị hạn chế và không nhận biết được giờ mùa hè (DST) hay lịch phi Gregory
  • Vì những giới hạn này, việc phụ thuộc vào các thư viện bên thứ ba lớn như Moment.js, date-fns là chuyện phổ biến, và điều đó dẫn tới suy giảm hiệu năng

Xung đột giữa tính bất biến và khái niệm tham chiếu

  • Giá trị nguyên thủy (primitive) trong JavaScript là bất biến và được lưu trực tiếp theo giá trị, còn đối tượng (object) được lưu bằng tham chiếu (reference) nên có thể bị thay đổi
  • Date là một đối tượng được tạo thông qua constructor, vì vậy nó có thể thay đổi
    • Ví dụ: khi gọi setMonth() hoặc setDate(), đối tượng gốc sẽ bị sửa đổi trực tiếp
  • Điều này dẫn đến những thay đổi giá trị ngoài dự kiến giữa các biến cùng tham chi chiếu tới một đối tượng
    • Ví dụ: nếu một hàm nhận today làm đối số và sửa ngày bên trong, thì today gốc cũng sẽ bị thay đổi

Temporal: API ngày·giờ mới

  • Temporalnamespace object chứ không phải constructor, có cấu trúc tương tự Math
    • Các thành phần chính: PlainDate, PlainDateTime, PlainTime, ZonedDateTime, Duration, Now
  • Temporal.Now trả về thời điểm hiện tại dưới nhiều dạng khác nhau
    • plainDateISO() → ngày theo định dạng ISO
    • zonedDateTimeISO() → thời điểm có bao gồm múi giờ
  • Các đối tượng Temporal cung cấp hệ thống phương thức rõ ràng
    • Có thể thực hiện phép toán theo đơn vị một cách tường minh như add({ days: 1 }), subtract({ years: 2 })
    • Không sửa đổi đối tượng hiện có mà trả về đối tượng mới, duy trì tính bất biến

Cách Temporal hoạt động và ưu điểm

  • Các đối tượng Temporal vẫn là kiểu object, nhưng tuân theo mẫu sử dụng bất biến được thiết kế có chủ đích
    • Ví dụ: today.add({ days: 1 }) sẽ trả về một đối tượng ngày mới, còn today gốc không thay đổi
  • Cung cấp cú pháp ngắn gọn và rõ ràng hơn so với Date
    • Ví dụ:
      const today = Temporal.Now.plainDateISO();  
      console.log(`Tomorrow will be ${ today.add({ days: 1 }) }. Today is ${ today }.`);  
      // 결과: Tomorrow will be 2026-01-01. Today is 2025-12-31.  
      
  • Phù hợp với các nhu cầu hiện đại như chỉ định múi giờ, tính khoảng thời gian, duy trì định dạng ISO
  • Có thể diễn đạt ngắn gọn các phép tính ngày phức tạp thông qua method chaining như add, subtract, since, until

Tình hình chuẩn hóa và triển vọng sắp tới

  • Temporal đã đạt tới giai đoạn đề xuất ECMAScript 3 (Stage 3), tức là đã ở trạng thái khuyến nghị triển khai trên trình duyệt
  • ChromeFirefox đã bắt đầu hỗ trợ thử nghiệm, các trình duyệt khác cũng dự kiến sẽ triển khai
  • Các nhà phát triển có thể tham gia cải thiện đặc tả ngay từ bây giờ thông qua kiểm thử và phản hồi
  • Date vẫn sẽ tiếp tục tồn tại, nhưng trong tương lai Temporal được kỳ vọng sẽ trở thành cách xử lý ngày mặc định
  • Bài viết kết lại rằng: “Lẽ ra phải thay thế từ năm 1995, nhưng dù muộn thì Temporal.Now vẫn là thời điểm tốt nhất”

1 bình luận

 
GN⁺ 2026-01-13
Ý kiến trên Hacker News
  • Bài viết này nói về nhiều hành vi kỳ quặc của constructor Date trong JavaScript
    Đặc biệt, nó giải thích vấn đề định dạng 'YYYY-MM-DD' được diễn giải là nửa đêm UTC, khiến ngày bị lệch một ngày trong múi giờ cục bộ
    Theo ISO 8601 gốc, nếu không chỉ định múi giờ thì phải được coi là giờ địa phương, nhưng do một sai sót khi viết đặc tả ES5 nên nó lại được xử lý thành “Z”(UTC)
    Sau đó đã từng định sửa trong ES2015, nhưng vì vô số website phụ thuộc vào hành vi sai cũ nên nó bị hoàn tác vì lý do tương thích web
    Xem thêm ở phần Broken Parser

    • Giá mà có chỉ thị 'strict datetime' giống như 'use strict'
      Khi đó có thể áp dụng có chọn lọc hành vi đúng mà không gặp vấn đề không tương thích với mã cũ
      Hoặc cũng có thể dùng cách nhập một đối tượng toàn cục đã được sửa qua module nội bộ như import Date from 'browser:date'
    • Trước đây tôi cũng từng tự tách chuỗi để tạo đối tượng ngày không có múi giờ
      Với những giá trị chỉ mang ý nghĩa ngày tháng như sinh nhật, việc thay đổi vì múi giờ là hoàn toàn vô lý
      Tôi nhớ trước kia Outlook lưu sinh nhật kèm múi giờ, nên mỗi lần đổi quốc gia thì sinh nhật lại bị xê dịch một ngày
    • Cụm “bị hiến tế trên bàn thờ của tính tương thích web” thật ấn tượng
      Nhưng liệu có lựa chọn nào khác không? Ép mọi người phải phân nhánh theo phiên bản trình duyệt như thời IE5 có lẽ còn tệ hơn
    • Nếu vào jsdate.wtf thì có thể tự mình trải nghiệm các hành vi quái dị của JS Date
    • Nghĩ đến việc những vấn đề này là nguồn gốc của vô số bug nhỏ thì vừa buồn cười vừa buồn thật
  • Tôi thực sự ghen tị với cách Rails và Ruby xử lý thời gian
    API như Time.current.in_time_zone('America/Los_Angeles') + 3.days - 4.months + 1.hour vừa trực quan vừa mạnh mẽ
    Ruby overload đối tượng Time thành một đối tượng nhất quán, nên hầu như không phải băn khoăn chuyện chuyển đổi hay ép kiểu
    Tôi vẫn nghĩ sẽ tuyệt biết bao nếu trong JS cũng có thể viết đơn giản như new Date().add({ days: 1 })

    • Tuy vậy, cũng có ý kiến nghi ngờ liệu cú pháp như “3.days - 4.months + 1.hour” có thực sự tốt không
      Ngoài ra, việc overload thư viện lõi có thật sự là cách tiếp cận tốt hay không cũng còn gây tranh cãi
  • Thật tiếc là Safari vẫn chưa hỗ trợ Temporal API
    Hy vọng khoảng năm sau sẽ có hỗ trợ

  • Date của JavaScript có nhiều vấn đề, nhưng bản thân việc nó là một đối tượng thì có lẽ không phải vấn đề lớn
    Giá như nó là đối tượng bất biến thì tốt hơn, nhưng một đối tượng có thể thay đổi thì thay đổi cũng không có gì đáng ngạc nhiên

    • Nhưng vấn đề là mã khác có thể ngầm thay đổi đối tượng Date mà tôi đang giữ
      Nguy cơ thực sự của tính khả biến xuất hiện ở thay đổi phi cục bộ, chứ không phải thay đổi tại chỗ
  • Việc Temporal API hoàn toàn không xử lý thông tin về giây nhuận (leap second) gây bất tiện
    Tôi muốn tạo công cụ JS cho tính toán thiên văn, nhưng việc chuyển đổi UTC cần dữ liệu giây nhuận
    Có cách lách như temporal-tai, nhưng phải duy trì tệp giây nhuận ở phía client nên rất phiền
    Vì SOP(chính sách CORS), cũng không thể lấy trực tiếp tệp từ site bên ngoài
    Trình duyệt vốn được cập nhật định kỳ, nên thật khó hiểu vì sao chúng không nhúng sẵn thông tin giây nhuận

    • Việc SOP chặn tệp giây nhuận là một hiểu lầm
      Nếu server đặt header Access-Control-Allow-Origin hoặc cung cấp dưới dạng tệp JS thì vẫn làm được
      Dù vậy, để trình duyệt tự chứa và duy trì dữ liệu giây nhuận có thể là một công việc tốn kém
    • Cách nói “chỉ xử lý UTC” là không chính xác
      UTC vốn là thang thời gian có bao gồm giây nhuận, nên đúng hơn phải nói là nó chỉ xử lý thời gian POSIX
  • Trong ví dụ mã, phải nói là từ "50" chứ không phải "33" mới bị xử lý thành những năm 1900 — đây chỉ là một lỗi gõ đơn giản

  • Tôi đang dùng Temporal polyfill và đến giờ thì rất hài lòng

    • Chỉ có điều dung lượng của nó là 51KB, nên không hẳn là nhẹ (liên kết bundlephobia)
      Với server hoặc ứng dụng lớn thì ổn, nhưng với app nhỏ có thể là gánh nặng
    • Cũng có người hỏi không biết so với moment hay luxon thì thế nào
  • Bài viết kỳ lạ ở chỗ hoàn toàn không nhắc đến Date.now()
    Nếu muốn so sánh với Temporal thì lẽ ra nên giải thích dựa trên Date.now()
    Hàm này trả về thời gian đã trôi qua tính bằng mili giây kể từ ngày 1 tháng 1 năm 1970
    Temporal đúng là có API thân thiện hơn, nhưng về bản chất mục tiêu của nó vẫn là biểu diễn khoảng cách tương đối của thời gian

    • Nhưng bug liên quan đến timestamp được nhắc trong bài cũng có tồn tại
      Vậy thì làm sao chuyển đổi sang định dạng mong muốn mà không phải đi qua Date, điều đó khiến người ta thắc mắc
  • Có người để lại một chỉnh sửa nhỏ nhưng quan trọng rằng không phải “daylight savings time” mà là “daylight saving time”

  • Từ trước đến nay tôi không hề biết JS Date lại tệ đến vậy

    • Điều đáng tiếc hơn nữa là những vấn đề này đã thấy rất rõ từ năm 1995
      Chỉ cần cẩn trọng hơn một chút vào thời điểm đó thì đã có thể giúp vô số lập trình viên tránh được những cái bẫy này