Tiếng Việt

Hướng dẫn toàn diện về cách sử dụng Temporal API của JavaScript để tính toán khoảng thời gian chính xác và trực quan, bao gồm mọi thứ từ tạo thời lượng cơ bản đến số học và định dạng nâng cao.

JavaScript Temporal Duration: Làm chủ các phép tính khoảng thời gian

Temporal API của JavaScript giới thiệu một cách hiện đại và mạnh mẽ để xử lý ngày tháng, thời gian và khoảng thời gian. Đối tượng Temporal.Duration biểu thị một khoảng thời gian, cung cấp một cách tiếp cận rõ ràng và trực quan để thực hiện các phép tính với khoảng thời gian. Bài viết này đi sâu vào chi tiết của Temporal.Duration, trình bày cách tạo, thao tác và định dạng thời lượng cho các trường hợp sử dụng khác nhau.

Temporal.Duration là gì?

Temporal.Duration biểu thị một khoảng thời gian, biểu thị nó theo năm, tháng, ngày, giờ, phút, giây và phần nhỏ của giây (mili giây, micro giây, nano giây). Không giống như các đối tượng Date biểu thị một thời điểm cụ thể, Temporal.Duration biểu thị một lượng thời gian. Nó tuân thủ định dạng thời lượng ISO 8601 (ví dụ: P1Y2M10DT2H30M biểu thị 1 năm, 2 tháng, 10 ngày, 2 giờ và 30 phút). Temporal API được thiết kế trực quan và ít gây ra lỗi hơn đối tượng Date cũ.

Tạo đối tượng Temporal.Duration

Có một số cách để tạo đối tượng Temporal.Duration:

1. Từ một đối tượng đơn giản

Bạn có thể tạo một thời lượng bằng cách truyền một đối tượng với các thuộc tính mong muốn:

const duration = new Temporal.Duration(1, 2, 10, 2, 30, 0, 0, 0);
console.log(duration.toString()); // Output: P1Y2M10DT2H30M

Điều này tạo ra một thời lượng là 1 năm, 2 tháng, 10 ngày, 2 giờ và 30 phút. Lưu ý rằng các đối số tương ứng với thứ tự sau: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds.

2. Từ chuỗi ISO 8601

Bạn cũng có thể tạo một thời lượng từ một chuỗi thời lượng ISO 8601 bằng cách sử dụng Temporal.Duration.from():

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.toString()); // Output: P1Y2M10DT2H30M

Điều này đặc biệt hữu ích khi xử lý thời lượng được lưu trữ ở định dạng chuỗi hoặc nhận từ nguồn bên ngoài.

3. Sử dụng các phương thức add()subtract() với Temporal.Instant, Temporal.ZonedDateTime, v.v.

Khi bạn thêm hoặc trừ Temporal.Duration từ các loại Temporal khác (như Temporal.Instant hoặc Temporal.ZonedDateTime), một Temporal.Duration sẽ được trả về đại diện cho sự khác biệt giữa hai thời điểm nếu sau đó bạn trừ chúng. Ví dụ:

const now = Temporal.Now.zonedDateTimeISO();
const later = now.add({ hours: 5 });
const duration = later.since(now);
console.log(duration.toString()); // Output: PT5H

Truy cập các thành phần thời lượng

Bạn có thể truy cập các thành phần riêng lẻ của một đối tượng Temporal.Duration bằng cách sử dụng các thuộc tính của nó:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.years);      // Output: 1
console.log(duration.months);     // Output: 2
console.log(duration.days);       // Output: 10
console.log(duration.hours);      // Output: 2
console.log(duration.minutes);     // Output: 30
console.log(duration.seconds);     // Output: 0
console.log(duration.milliseconds); // Output: 0
console.log(duration.microseconds); // Output: 0
console.log(duration.nanoseconds);  // Output: 0

Thực hiện các phép tính số học với thời lượng

Các đối tượng Temporal.Duration hỗ trợ phép cộng và phép trừ bằng cách sử dụng các phương thức add()subtract(). Các phương thức này trả về một đối tượng Temporal.Duration mới biểu thị kết quả của thao tác.

const duration1 = Temporal.Duration.from("P1Y2M");
const duration2 = Temporal.Duration.from("P3M4D");

const addedDuration = duration1.add(duration2);
console.log(addedDuration.toString()); // Output: P1Y5M4D

const subtractedDuration = duration1.subtract(duration2);
console.log(subtractedDuration.toString()); // Output: P10M26D

Bạn cũng có thể xâu chuỗi các phương thức này để tính toán phức tạp hơn:

const duration = Temporal.Duration.from("P1D").add({ hours: 12 }).subtract({ minutes: 30 });
console.log(duration.toString()); // Output: P1DT11H30M

Phương thức negated() trả về một đối tượng Temporal.Duration mới với tất cả các thành phần bị phủ định:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const negatedDuration = duration.negated();
console.log(negatedDuration.toString()); // Output: -P1Y2M10DT2H30M

Phương thức abs() trả về một đối tượng Temporal.Duration mới với tất cả các thành phần là giá trị dương (giá trị tuyệt đối):

const duration = Temporal.Duration.from("-P1Y2M10DT2H30M");
const absoluteDuration = duration.abs();
console.log(absoluteDuration.toString()); // Output: P1Y2M10DT2H30M

Phương thức with() cho phép bạn tạo một phiên bản Temporal.Duration mới với một số hoặc tất cả các thuộc tính được thay đổi thành giá trị mới. Nếu một giá trị không được chỉ định trong đối tượng đối số, thì giá trị ban đầu của thời lượng sẽ được sử dụng. Ví dụ:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const newDuration = duration.with({ years: 2, days: 5 });
console.log(newDuration.toString()); // Output: P2Y2M5DT2H30M

Chuẩn hóa thời lượng

Thời lượng đôi khi có thể được biểu thị ở dạng không chuẩn hóa (ví dụ: P1Y12M, có thể được đơn giản hóa thành P2Y). Phương thức normalized() cố gắng đơn giản hóa một thời lượng thành dạng nhỏ gọn nhất của nó. Tuy nhiên, nó yêu cầu một ngày tham chiếu để xử lý sự phức tạp của độ dài tháng khác nhau. Để chuẩn hóa đúng cách, bạn sẽ cần một phiên bản Temporal.PlainDate, Temporal.ZonedDateTime hoặc Temporal.Instant.

Ví dụ: chuẩn hóa một thời lượng liên quan đến tháng và ngày yêu cầu một ngày tham chiếu:

const duration = Temporal.Duration.from("P1M32D");
const referenceDate = Temporal.PlainDate.from("2024-01-01");
const normalizedDuration = duration.normalized({ relativeTo: referenceDate });
console.log(normalizedDuration.toString()); // Output: P2M1D

Trong ví dụ này, thời lượng P1M32D được chuẩn hóa liên quan đến ngày 1 tháng 1 năm 2024, dẫn đến P2M1D vì tháng 1 có 31 ngày.

Nếu bạn chỉ xử lý các thành phần thời gian (giờ, phút, giây, v.v.), bạn có thể chuẩn hóa mà không cần ngày tham chiếu:

const duration = Temporal.Duration.from("PT25H61M");
const normalizedDuration = duration.normalized({ relativeTo: null }); //hoặc bỏ qua đối số relativeTo
console.log(normalizedDuration.toString()); // Output: P1DT2H1M

So sánh thời lượng

Bạn có thể so sánh thời lượng bằng phương thức compare(). Phương thức này trả về:

const duration1 = Temporal.Duration.from("P1Y");
const duration2 = Temporal.Duration.from("P6M");

const comparisonResult = Temporal.Duration.compare(duration1, duration2);
console.log(comparisonResult); // Output: 1

Ví dụ thực tế

1. Tính thời gian cho đến một sự kiện

Giả sử bạn muốn tính thời gian còn lại cho đến một sự kiện cụ thể. Sử dụng Temporal.Now.zonedDateTimeISO() để lấy thời gian hiện tại và trừ ngày của sự kiện. Nếu ngày của sự kiện đã qua, đầu ra sẽ là số âm.

const eventDate = Temporal.ZonedDateTime.from({ timeZone: 'America/Los_Angeles', year: 2024, month: 12, day: 25, hour: 9, minute: 0, second: 0 });
const now = Temporal.Now.zonedDateTimeISO('America/Los_Angeles');

const durationUntilEvent = eventDate.since(now);

console.log(durationUntilEvent.toString()); // Output: ví dụ: P262DT14H30M (tùy thuộc vào ngày và giờ hiện tại)

2. Theo dõi thời lượng công việc dự án

Trong quản lý dự án, bạn có thể sử dụng Temporal.Duration để theo dõi thời lượng ước tính hoặc thực tế của các công việc.

const task1EstimatedDuration = Temporal.Duration.from("PT8H"); // 8 giờ
const task2EstimatedDuration = Temporal.Duration.from("PT16H"); // 16 giờ

const totalEstimatedDuration = task1EstimatedDuration.add(task2EstimatedDuration);
console.log(`Tổng thời gian ước tính: ${totalEstimatedDuration.toString()}`); // Output: Tổng thời gian ước tính: P1DT

3. Tính tuổi

Mặc dù tính tuổi một cách chính xác đòi hỏi phải xem xét năm nhuận và múi giờ, Temporal.Duration có thể cung cấp một ước tính hợp lý:

const birthDate = Temporal.PlainDate.from("1990-05-15");
const currentDate = Temporal.PlainDate.from("2024-01-20");

const ageDuration = currentDate.since(birthDate, { smallestUnit: 'years' });
console.log(`Tuổi ước tính: ${ageDuration.years} năm`); // Output: Tuổi ước tính: 33 năm

4. Hiển thị thời lượng dễ đọc

Thông thường, bạn cần hiển thị thời lượng ở định dạng dễ đọc. Mặc dù Temporal.Duration không có các hàm định dạng tích hợp, bạn có thể tạo logic định dạng tùy chỉnh:

function formatDuration(duration) {
  const parts = [];
  if (duration.years) parts.push(`${duration.years} năm${duration.years > 1 ? 's' : ''}`);
  if (duration.months) parts.push(`${duration.months} tháng${duration.months > 1 ? 's' : ''}`);
  if (duration.days) parts.push(`${duration.days} ngày${duration.days > 1 ? 's' : ''}`);
  if (duration.hours) parts.push(`${duration.hours} giờ${duration.hours > 1 ? 's' : ''}`);
  if (duration.minutes) parts.push(`${duration.minutes} phút${duration.minutes > 1 ? 's' : ''}`);
  if (duration.seconds) parts.push(`${duration.seconds} giây${duration.seconds > 1 ? 's' : ''}`);

  return parts.join(', ');
}

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const formattedDuration = formatDuration(duration);
console.log(formattedDuration); // Output: 1 năm, 2 tháng, 10 ngày, 2 giờ, 30 phút

Sử dụng nâng cao và cân nhắc

1. Xử lý múi giờ

Khi xử lý các khoảng thời gian vượt qua ranh giới múi giờ hoặc chuyển đổi giờ tiết kiệm ánh sáng ban ngày, điều quan trọng là phải sử dụng Temporal.ZonedDateTime để đảm bảo tính toán chính xác. Sử dụng Temporal.PlainDateTemporal.PlainTime sẽ tránh mọi chuyển đổi múi giờ.

2. Đơn vị nhỏ nhất và làm tròn

Các phương thức `since()` và `until()` thường chấp nhận các tùy chọn để xác định đơn vị nhỏ nhất cho thời lượng kết quả. Ví dụ: tính thời gian *cho đến* một sự kiện và giới hạn kết quả xuống đến ngày.

const eventDate = Temporal.PlainDate.from("2024-12-25");
const now = Temporal.PlainDate.from("2024-01-20");

const durationUntilEvent = now.until(eventDate, { smallestUnit: 'days' });

console.log(durationUntilEvent.toString()); //ví dụ đầu ra PT340D

3. Giây nhuận

Temporal không tính đến giây nhuận một cách tự nhiên. Nếu bạn yêu cầu độ chính xác cực cao, bạn sẽ cần phải xử lý giây nhuận riêng.

4. Múi giờ IANA

Temporal API dựa trên cơ sở dữ liệu múi giờ IANA (Internet Assigned Numbers Authority). Đảm bảo rằng môi trường của bạn có phiên bản cập nhật của cơ sở dữ liệu IANA để xử lý chính xác các chuyển đổi múi giờ.

Thực tiễn tốt nhất

Cạm bẫy thường gặp

Các trường hợp sử dụng thực tế trên các nền văn hóa khác nhau

Temporal API có thể đặc biệt có lợi trong các ứng dụng toàn cầu, nơi sự khác biệt về múi giờ và sắc thái văn hóa là rất quan trọng. Dưới đây là một vài ví dụ:

Kết luận

Temporal.Duration cung cấp một cách mạnh mẽ và trực quan để làm việc với các khoảng thời gian trong JavaScript. Bằng cách hiểu các tính năng và thực tiễn tốt nhất của nó, bạn có thể tự tin thực hiện các phép tính thời lượng chính xác và đáng tin cậy trong các ứng dụng của mình. Việc áp dụng Temporal API dẫn đến mã sạch hơn, dễ bảo trì hơn và giảm nguy cơ mắc lỗi liên quan đến việc xử lý ngày giờ cũ.

Khi bạn đi sâu hơn vào Temporal API, hãy nhớ tham khảo tài liệu chính thức và thử nghiệm với các tình huống khác nhau để nắm bắt đầy đủ các khả năng của nó. Với thiết kế hiện đại và các tính năng toàn diện, Temporal được thiết lập để cách mạng hóa cách chúng ta xử lý ngày, giờ và thời lượng trong JavaScript.