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()
và 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()
và 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ề:
- -1 nếu thời lượng đầu tiên ngắn hơn thời lượng thứ hai.
- 0 nếu thời lượng bằng nhau.
- 1 nếu thời lượng đầu tiên dài hơn thời lượng thứ hai.
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.PlainDate
và Temporal.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
- Sử dụng định dạng ISO 8601 cho chuỗi thời lượng: Điều này đảm bảo tính nhất quán và khả năng tương tác.
- Chọn loại Temporal thích hợp: Sử dụng
Temporal.PlainDate
,Temporal.PlainTime
,Temporal.ZonedDateTime
hoặcTemporal.Instant
dựa trên việc bạn có cần hỗ trợ múi giờ hay không. - Chuẩn hóa thời lượng khi cần thiết: Chuẩn hóa đơn giản hóa thời lượng và giúp chúng dễ so sánh hơn.
- Xử lý múi giờ cẩn thận: Chuyển đổi múi giờ có thể phức tạp, vì vậy hãy sử dụng
Temporal.ZonedDateTime
và lưu ý đến các chuyển đổi giờ tiết kiệm ánh sáng ban ngày. - Xem xét đơn vị nhỏ nhất: Khi tính toán thời lượng, hãy chỉ định đơn vị nhỏ nhất để có được mức độ chính xác mong muốn.
- Viết kiểm thử đơn vị: Kiểm tra kỹ lưỡng mã của bạn để đảm bảo rằng các phép tính thời lượng là chính xác.
Cạm bẫy thường gặp
- Bỏ qua múi giờ: Không tính đến múi giờ có thể dẫn đến tính toán thời lượng không chính xác, đặc biệt khi xử lý các sự kiện ở các địa điểm khác nhau.
- Sử dụng đối tượng Date cũ: Đối tượng
Date
cũ được biết đến với những điểm kỳ quặc và không nhất quán của nó. Ưu tiên Temporal API để xử lý ngày giờ đáng tin cậy hơn. - Không chuẩn hóa thời lượng: Không chuẩn hóa thời lượng có thể làm cho các so sánh và tính toán trở nên phức tạp hơn.
- Định dạng ISO 8601 không chính xác: Sử dụng chuỗi thời lượng ISO 8601 không hợp lệ có thể gây ra lỗi.
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ụ:
- Lập lịch sự kiện toàn cầu: Lập lịch chính xác các sự kiện trên nhiều múi giờ, có tính đến các chuyển đổi giờ tiết kiệm ánh sáng ban ngày. Ví dụ: lên lịch một hội thảo trên web bắt đầu lúc 9:00 sáng PST và hiển thị thời gian bắt đầu tương ứng ở các múi giờ khác nhau như CET, JST và AEDT.
- Lập kế hoạch du lịch quốc tế: Tính toán thời gian di chuyển, bao gồm cả thời gian dừng chân và thay đổi múi giờ. Điều này hữu ích cho việc tạo hành trình và quản lý lịch trình chuyến bay. Ví dụ: tính tổng thời gian di chuyển từ New York đến Tokyo, bao gồm thời gian dừng chân ở London và điều chỉnh theo sự khác biệt về múi giờ.
- Thương mại điện tử toàn cầu: Hiển thị thời gian giao hàng ước tính theo múi giờ địa phương của người dùng. Điều này đòi hỏi phải xem xét múi giờ gốc, thời gian vận chuyển và múi giờ đích. Ví dụ: một mặt hàng được vận chuyển từ một nhà kho ở Đức đến một khách hàng ở Úc, với thời gian giao hàng ước tính là 7 ngày, được hiển thị theo giờ địa phương của khách hàng.
- Giao dịch tài chính xuyên biên giới: Tính toán chính xác việc tích lũy lãi suất hoặc thời hạn thanh toán trên các khu vực khác nhau. Điều này thường liên quan đến việc xem xét các ngày làm việc và ngày lễ khác nhau ở mỗi quốc gia. Ví dụ: tính lãi suất tích lũy trên một khoản vay ở Singapore, có tính đến các ngày lễ ở Singapore.
- Các ứng dụng lịch đa văn hóa: Hỗ trợ các hệ thống lịch khác nhau, chẳng hạn như lịch Hồi giáo hoặc Do Thái, và tính toán chính xác thời lượng sự kiện và lời nhắc dựa trên các lịch này.
- Quản lý dự án toàn cầu: Theo dõi thời lượng và thời hạn của công việc dự án trên các nhóm phân tán, có tính đến lịch làm việc và múi giờ khác nhau.
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.