Lớp java.time.ZonedDateTime
Lớp ZonedDateTime trong gói java.time của Java được sử dụng để biểu diễn cả ngày tháng, thời gian và múi giờ theo định dạng ISO-8601, bao gồm cả thông tin về quy tắc chuyển đổi giờ mùa hè (DST). Ví dụ: "2007-12-03T10:15:30+01:00 Europe/Paris" (ngày 03 tháng 12 năm 2007, 10:15:30 giờ cộng thêm 1 giờ so với UTC theo múi giờ Châu Âu/Paris). ZonedDateTime là một đối tượng ngày giờ bất biến (immutable) lưu trữ thông tin ngày, tháng, năm, giờ, phút, giây, thậm chí có thể chính xác đến nano giây, cùng với múi giờ và độ lệch chuẩn UTC.
Ⅰ. Đặc điểm của lớp ZonedDateTime
↳ Lưu trữ cả ngày tháng (giống LocalDate) và thời gian (giống LocalTime) kèm theo múi giờ (ZoneId) và độ lệch chuẩn UTC.
↳ Xử lý việc chuyển đổi giữa thời gian địa phương (LocalDateTime) và thời điểm trên trục thời gian (Instant).
↳ Hỗ trợ các trường hợp phức tạp như thay đổi giờ mùa hè (DST) dẫn đến việc có nhiều độ lệch chuẩn hợp lệ cho cùng một ngày giờ địa phương (khoảng trống - Gap và chồng chéo - Overlap).
↳ Là lớp bất biến (immutable), các phép toán dựa trên sự so sánh bằng (==) hoặc sử dụng hash code không nên được áp dụng cho ZonedDateTime. Bạn nên sử dụng phương thức equals để so sánh hai ngày giờ.
↳ An toàn cho nhiều luồng (thread-safe).
So sánh lớp ZonedDateTime với lớp LocalDateTime và lớp OffsetDateTime:
↳ Lớp ZonedDateTime chi tiết hơn lớp LocalDateTime vì nó lưu trữ cả múi giờ.
↳ Lớp ZonedDateTime chi tiết hơn lớp OffsetDateTime vì nó lưu trữ đầy đủ thông tin quy tắc chuyển đổi giờ mùa hè (DST) của múi giờ.
Tóm lại, lớp ZonedDateTime là lớp hữu ích khi bạn cần lưu trữ ngày giờ theo múi giờ cụ thể và xử lý các trường hợp phức tạp liên quan đến thay đổi giờ mùa hè. Nó cung cấp thông tin chi tiết hơn về múi giờ so với lớp OffsetDateTime.
Ⅱ. Khai báo lớp ZonedDateTime trong Java
Để sử dụng lớp ZonedDateTime và các lớp khác trong gói java.time, bạn cần thêm câu lệnh import vào đầu file Java của mình.
Cú pháp câu lệnh import:
Cú pháp
import java.time.ZonedDateTime;
Cú pháp khai báo lớp ZonedDateTime:
Cú pháp
public final class ZonedDateTime
extends Object
implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable
Dưới đây là giải thích chi tiết về cú pháp khai báo này:
↳ public: Lớp này có thể được truy cập từ bất kỳ đâu trong chương trình, không bị giới hạn phạm vi.
↳ final: Lớp này không thể bị kế thừa. Điều này có nghĩa là bạn không thể tạo một lớp con từ ZonedDateTime.
↳ class ZonedDateTime: Khai báo một lớp có tên là ZonedDateTime.
extends Object: Tất cả các lớp trong Java đều kế thừa từ lớp Object (lớp gốc của tất cả các lớp trong Java). Việc này cho phép lớp ZonedDateTime kế thừa các phương thức cơ bản từ Object, chẳng hạn như toString(), equals(), và hashCode().
implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable
Lớp ZonedDateTime thực hiện (implements) các giao diện implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable. Điều này có nghĩa là lớp ZonedDateTime phải cung cấp các phương thức được khai báo trong những giao diện này.
↳ implements Temporal: Lớp ZonedDateTime thực hiện giao diện Temporal. Giao diện này cung cấp các phương thức để thao tác với các đối tượng thời gian và ngày tháng, chẳng hạn như truy vấn và điều chỉnh các thành phần ngày và giờ.
↳ implements ChronoZonedDateTime<LocalDate>: Lớp ZonedDateTime thực hiện giao diện ChronoZonedDateTime<LocalDate>. Đây là một giao diện mở rộng của ZonedDateTime, định nghĩa các phương thức liên quan đến việc xử lý ngày và giờ trong một hệ thống lịch cụ thể (như Gregorian). ChronoZonedDateTime cho phép các lớp như ZonedDateTime tương tác với LocalDate và hỗ trợ các thao tác thời gian liên quan đến múi giờ.
↳ implements Serializable: ZonedDateTime thực hiện giao diện Serializable, cho phép các đối tượng của lớp này được chuyển đổi thành một dạng byte để lưu trữ hoặc truyền qua mạng (serialization) và phục hồi từ dạng byte này (deserialization).
Ⅲ. Các phương thức của lớp ZonedDateTime
Lớp ZonedDateTime cung cấp nhiều phương thức để thao tác với ngày giờ theo múi giờ. Việc phân nhóm các phương thức theo chức năng sẽ giúp bạn hiểu rõ hơn về cách sử dụng lớp ZonedDateTime. Dưới đây là cách phân nhóm các phương thức phổ biến của lớp ZonedDateTime:
Tạo đối tượng ZonedDateTime
↳ now(): Lấy ngày giờ hiện tại từ hệ thống theo múi giờ mặc định.
↳ of(LocalDate date, LocalTime time, ZoneId zone) (static): Tạo một đối tượng ZonedDateTime từ ngày (LocalDate), thời gian (LocalTime) và múi giờ (ZoneId).
Dưới đây là một ví dụ về cách sử dụng hai phương thức now() và of(LocalDate date, LocalTime time, ZoneId zone) của lớp ZonedDateTime trong cùng một lớp Java:
Ví dụ: Example.java
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Example {
public static void main(String[] args) {
// Sử dụng phương thức now() để lấy ngày giờ hiện tại theo múi giờ mặc định
ZonedDateTime currentZonedDateTime = ZonedDateTime.now();
System.out.println("Ngày giờ hiện tại: " + currentZonedDateTime);
// Tạo một LocalDate và LocalTime cụ thể
LocalDate date = LocalDate.of(2023, 7, 22);
LocalTime time = LocalTime.of(15, 30, 45);
ZoneId zone = ZoneId.of("America/New_York");
// Sử dụng phương thức of(LocalDate date, LocalTime time, ZoneId zone) để tạo một đối tượng ZonedDateTime
ZonedDateTime specificZonedDateTime = ZonedDateTime.of(date, time, zone);
System.out.println("Ngày giờ cụ thể với múi giờ New York: " + specificZonedDateTime);
}
}
Kết quả của chương trình là:
Ngày giờ cụ thể với múi giờ New York: 2023-07-22T15:30:45-04:00[America/New_York]
Kết quả của chương trình sẽ là ngày giờ hiện tại theo múi giờ hệ thống mặc định và một ngày giờ cụ thể với múi giờ New York.
Lấy các thành phần
↳ get(TemporalField field): Lấy giá trị của một trường ngày tháng hoặc thời gian cụ thể (ví dụ như ngày, tháng, năm, giờ, phút, giây) dưới dạng số nguyên.
↳ getZone(): Lấy thông tin múi giờ (ZoneId), ví dụ như "Asia/Kolkata".
Dưới đây là một ví dụ về cách sử dụng các phương thức get(TemporalField field) và getZone() của lớp ZonedDateTime trong cùng một lớp Java:
Ví dụ: Example.java
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
public class Example {
public static void main(String[] args) {
// Tạo một đối tượng ZonedDateTime cụ thể
LocalDate date = LocalDate.of(2023, 7, 22);
LocalTime time = LocalTime.of(15, 30, 45);
ZoneId zone = ZoneId.of("Asia/Kolkata");
ZonedDateTime zonedDateTime = ZonedDateTime.of(date, time, zone);
// Lấy giá trị của các trường ngày tháng và thời gian cụ thể
int year = zonedDateTime.get(ChronoField.YEAR);
int month = zonedDateTime.get(ChronoField.MONTH_OF_YEAR);
int dayOfMonth = zonedDateTime.get(ChronoField.DAY_OF_MONTH);
int hour = zonedDateTime.get(ChronoField.HOUR_OF_DAY);
int minute = zonedDateTime.get(ChronoField.MINUTE_OF_HOUR);
int second = zonedDateTime.get(ChronoField.SECOND_OF_MINUTE);
// Lấy thông tin múi giờ
ZoneId zoneId = zonedDateTime.getZone();
// In ra kết quả
System.out.println("Năm: " + year);
System.out.println("Tháng: " + month);
System.out.println("Ngày trong tháng: " + dayOfMonth);
System.out.println("Giờ: " + hour);
System.out.println("Phút: " + minute);
System.out.println("Giây: " + second);
System.out.println("Múi giờ: " + zoneId);
}
}
Kết quả của chương trình là:
Tháng: 7
Ngày trong tháng: 22
Giờ: 15
Phút: 30
Giây: 45
Múi giờ: Asia/Kolkata
Kết quả của chương trình sẽ là các giá trị của năm, tháng, ngày trong tháng, giờ, phút, giây và múi giờ từ đối tượng ZonedDateTime đã tạo.
Thay đổi múi giờ
↳ withZoneSameInstant(ZoneId zone): Trả về một bản sao của ZonedDateTime với múi giờ khác (zone) nhưng vẫn giữ nguyên thời điểm (instant).
Dưới đây là một ví dụ về cách sử dụng phương thức withZoneSameInstant(ZoneId zone) của lớp ZonedDateTime để thay đổi múi giờ nhưng giữ nguyên thời điểm (instant) trong cùng một lớp Java:
Ví dụ: Example.java
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Example {
public static void main(String[] args) {
// Tạo một đối tượng ZonedDateTime cụ thể
LocalDate date = LocalDate.of(2023, 7, 22);
LocalTime time = LocalTime.of(15, 30, 45);
ZoneId originalZone = ZoneId.of("Asia/Kolkata");
ZonedDateTime zonedDateTime = ZonedDateTime.of(date, time, originalZone);
// Thay đổi múi giờ nhưng giữ nguyên thời điểm
ZoneId newZone = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTimeInNewZone = zonedDateTime.withZoneSameInstant(newZone);
// In ra kết quả
System.out.println("ZonedDateTime gốc: " + zonedDateTime);
System.out.println("Múi giờ gốc: " + zonedDateTime.getZone());
System.out.println("ZonedDateTime sau khi thay đổi múi giờ: " + zonedDateTimeInNewZone);
System.out.println("Múi giờ mới: " + zonedDateTimeInNewZone.getZone());
}
}
Kết quả của chương trình là:
ZonedDateTime gốc: 2023-07-22T15:30:45+05:30[Asia/Kolkata]
Múi giờ gốc: Asia/Kolkata
ZonedDateTime sau khi thay đổi múi giờ: 2023-07-22T06:00:45-04:00[America/New_York]
Múi giờ mới: America/New_York
Hy vọng ví dụ này sẽ giúp bạn hiểu cách chuyển đổi giữa các múi giờ khác nhautrong Java.
Cộng/Trừ thời gian
↳ minus(long amountToSubtract, TemporalUnit unit): Trả về một bản sao của ZonedDateTime hiện tại nhưng đã trừ đi một giá trị cụ thể theo đơn vị thời gian nhất định (ví dụ như trừ 30 phút).
↳ plus(long amountToAdd, TemporalUnit unit): Trả về một bản sao của ZonedDateTime hiện tại nhưng đã cộng thêm một giá trị cụ thể theo đơn vị thời gian nhất định (ví dụ như cộng 3 giờ).
Dưới đây là ví dụ về cách sử dụng các phương thức cộng và trừ thời gian của lớp ZonedDateTime trong Java.
Ví dụ: Example.java
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class Example {
public static void main(String[] args) {
// Tạo một ZonedDateTime đại diện cho thời điểm hiện tại ở Việt Nam
ZonedDateTime nowInVietnam = ZonedDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh"));
// Trừ đi 2 ngày
ZonedDateTime twoDaysAgo = nowInVietnam.minus(2, ChronoUnit.DAYS);
System.out.println("Hai ngày trước: " + twoDaysAgo);
// Cộng thêm 3 giờ
ZonedDateTime threeHoursLater = twoDaysAgo.plus(3, ChronoUnit.HOURS);
System.out.println("Ba giờ sau khi trừ 2 ngày: " + threeHoursLater);
// Cộng thêm 1 tuần và 2 ngày
ZonedDateTime nextWeek = nowInVietnam.plus(1, ChronoUnit.WEEKS).plus(2, ChronoUnit.DAYS);
System.out.println("Một tuần hai ngày tới: " + nextWeek);
}
}
Kết quả của chương trình là:
Ba giờ sau khi trừ 2 ngày: 2024-07-21T00:34:11.516371600+07:00[Asia/Ho_Chi_Minh]
Một tuần hai ngày tới: 2024-07-31T21:34:11.516371600+07:00[Asia/Ho_Chi_Minh]
Ví dụ này minh họa cách bạn có thể thay đổi thời gian bằng cách cộng hoặc trừ các khoảng thời gian khác nhau.
Định dạng
↳ format(DateTimeFormatter formatter): Định dạng ZonedDateTime hiện tại theo định dạng được cung cấp bởi DateTimeFormatter.
Dưới đây là ví dụ về cách sử dụng phương thức format để định dạng một đối tượng ZonedDateTime trong Java:
Ví dụ: Example.java
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Example {
public static void main(String[] args) {
// Tạo một ZonedDateTime đại diện cho thời điểm hiện tại ở Việt Nam
ZonedDateTime nowInVietnam = ZonedDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh"));
// Định dạng theo định dạng BASIC_ISO_DATE
DateTimeFormatter formatter1 = DateTimeFormatter.BASIC_ISO_DATE;
String formatted1 = nowInVietnam.format(formatter1);
System.out.println("Định dạng BASIC_ISO_DATE: " + formatted1);
// Định dạng theo mẫu tùy chỉnh
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
String formatted2 = nowInVietnam.format(formatter2);
System.out.println("Định dạng tùy chỉnh: " + formatted2);
}
}
Kết quả của chương trình là:
Định dạng tùy chỉnh: 2024-07-22 21:44:50 ICT
Với ví dụ trên, bạn đã có thể sử dụng phương thức format để định dạng các đối tượng ZonedDateTime theo nhiều cách khác nhau, đáp ứng nhu cầu hiển thị thời gian phù hợp cho từng trường hợp.
Các ngoại lệ thường gặp khi sử dụng lớp ZonedDateTime trong Java
Dưới đây là các ngoại lệ thường gặp và nguyên nhân của chúng:
Ngoại lệ DateTimeException
↳ Nguyên nhân: Xảy ra khi có lỗi liên quan đến định dạng ngày tháng hoặc thời gian không hợp lệ, chẳng hạn như ngày không tồn tại trong tháng hoặc thời gian không hợp lệ.
↳ Ví dụ: Khi cố gắng tạo một đối tượng ZonedDateTime với một ngày không hợp lệ, như 30 tháng 2.
Ngoại lệ DateTimeParseException
↳ Nguyên nhân: Xảy ra khi chuỗi đầu vào không khớp với định dạng ngày tháng hoặc thời gian mong đợi.
↳ Ví dụ: Khi sử dụng phương thức parse(CharSequence text) và chuỗi đầu vào không phải là định dạng hợp lệ cho ZonedDateTime.
Ngoại lệ DateTimeException (do múi giờ không hợp lệ)
↳ Nguyên nhân: Xảy ra khi sử dụng múi giờ không hợp lệ hoặc không tồn tại.
↳ Ví dụ: Khi tạo một đối tượng ZonedDateTime với ZoneId không hợp lệ hoặc không được hỗ trợ, chẳng hạn như "Invalid/Zone".
Ngoại lệ UnsupportedTemporalTypeException
↳ Nguyên nhân: Xảy ra khi thao tác với một trường thời gian không được hỗ trợ bởi ZonedDateTime.
↳ Ví dụ: Khi cố gắng cộng một khoảng thời gian lớn như ngày vào ZonedDateTime nhưng loại đơn vị không được hỗ trợ.
Ngoại lệ ArithmeticException
↳ Nguyên nhân: Xảy ra khi có lỗi số học trong các phép toán liên quan đến thời gian, chẳng hạn như vượt quá giới hạn số nguyên.
↳ Ví dụ: Khi cộng hoặc trừ một khoảng thời gian quá lớn mà dẫn đến tràn số.
Lưu ý:
Khi làm việc với ZonedDateTime, việc kiểm tra dữ liệu đầu vào và các thao tác ngày tháng là rất quan trọng để tránh các ngoại lệ. Sử dụng các khối try-catch để xử lý ngoại lệ và cung cấp thông báo lỗi hoặc xử lý các tình huống bất thường để làm cho ứng dụng của bạn ổn định và dễ duy trì hơn.