Java Ngày và Giờ

⚡ Tóm tắt thông minh

Java Việc xử lý ngày và giờ kết hợp các lớp java.util.Date và SimpleDateFormat cũ với API java.time hiện đại được giới thiệu trong Java 8, cung cấp cho các nhà phát triển các kiểu dữ liệu bất biến, an toàn cho đa luồng để thực hiện công việc lập lịch chính xác.

  • 🕒 Ngày cũ: Lớp java.util.Date lưu trữ một thời điểm tính bằng mili giây và in ra một chuỗi mặc định thông qua phương thức toString.
  • 🔤 Các chữ cái theo mẫu: SimpleDateFormat sử dụng các ký tự như yyyy, MM, dd, HH, mm, ss và zzz để định nghĩa các định dạng đầu ra tùy chỉnh.
  • ⚖️ so sánh: Phương thức compareTo trả về một số nguyên âm, bằng không hoặc dương để sắp xếp hai đối tượng Date theo thứ tự thời gian.
  • 🆕 API hiện đại: Các lớp LocalDate, LocalDateTime, ZonedDateTime và Instant trong java.time thay thế Date và Calendar bằng các đối tượng bất biến.
  • 🧵 An toàn chủ đề: DateTimeFormatter là bất biến và an toàn khi chia sẻ, trong khi SimpleDateFormat cần đồng bộ hóa hoặc một lớp bao bọc ThreadLocal.
  • 🔁 Khả năng tương tác: Chuyển đổi giữa API cũ và API mới bằng cách sử dụng các hàm hỗ trợ Date.toInstant, Date.from và Calendar.toInstant.

Java Ngày và Giờ

Xử lý ngày và giờ trong Java Đây là một phần thường xuyên trong việc xây dựng logic lập lịch, ghi nhật ký thời gian, báo cáo và nhật ký kiểm toán. Trước khi viết bất kỳ đoạn mã nào, việc nắm rõ các tham số tạo nên một ngày tháng là rất hữu ích:

  • năm (hai hoặc bốn chữ số).
  • tháng (dưới dạng hai chữ số, ba chữ cái đầu tiên của tháng, hoặc toàn bộ tên tháng).
  • ngày (số ngày cụ thể trong tháng).
  • ngày (Tên ngày trong tuần, ví dụ như Chủ Nhật, Thứ Hai, Thứ Ba).

Java Nó hiển thị nhiều tham số khác mô tả một thời điểm trên dòng thời gian, bao gồm kỷ nguyên, tuần trong năm, múi giờ và độ lệch. Các phần bên dưới sẽ trình bày chi tiết từng tham số với các ví dụ minh họa.

Cách sử dụng Ngày trong Java

Ngày hiển thị trong Java

Java cung cấp một Date lớp học trong java.util Gói này lưu trữ một thời điểm cụ thể với độ chính xác đến từng mili giây và cung cấp các phương thức hỗ trợ để in, so sánh và định dạng thời điểm đó. Để lấy ngày giờ hiện tại, hãy khởi tạo lớp bằng hàm tạo mặc định của nó:

import java.util.Date;

class Date_Ex1 {
    public static void main(String args[]) {
        // Instantiate a Date object by invoking its constructor
        Date objDate = new Date();
        // Display the Date and Time using toString()
        System.out.println(objDate.toString());
    }
}

Đầu ra:

Wed Nov 29 06:36:22 UTC 2017

Ví dụ này in ngày tháng theo định dạng toString mặc định. Để hiển thị cùng một thời điểm theo định dạng khác, trước tiên bạn cần hiểu các ký tự định dạng. Java sử dụng.

SimpleDateFormat: Phân tích và định dạng ngày tháng

SimpleDateFormat lớp trong java.text chuyển đổi một Date Chuyển đổi đối tượng thành chuỗi được định dạng và phân tích chuỗi được định dạng đó trở lại thành một DateNó sử dụng các chữ cái mẫu tương ứng với thành phần ngày hoặc giờ. Bảng dưới đây liệt kê tất cả các chữ cái được hỗ trợ:

Bức thưThành phần ngày hoặc giờVí dụ
GNgười chỉ định thời đạiAD
yNăm2026
MTháng trong nămTháng 07 hoặc tháng XNUMX hoặc XNUMX
wTuần trong năm27
WTuần trong tháng2
DNgày trong năm189
dNgày trong tháng10
FNgày trong tuần trong tháng2
ETên ngày trong tuầnThứ ba hoặc thứ ba
uSố ngày trong tuần (1 = Monday, 7 = Chủ nhật)1
aDấu hiệu AM hoặc PMPM
HGiờ trong ngày (0-23)0
kGiờ trong ngày (1-24)24
KGiờ sáng hoặc chiều (0-11)0
hGiờ sáng hoặc chiều (1-12)12
mPhút trong giờ30
sThứ hai trong phút55
SMili giây978
zMúi giờ (tên)Múi giờ Thái Bình Dương; PST; GMT-08:00
ZMúi giờ (độ lệch RFC 822)-0800
XMúi giờ (độ lệch ISO 8601)-08 hoặc -0800 hoặc -08:00

Bạn không cần phải nhớ toàn bộ bảng. Hãy tham khảo nó mỗi khi bạn tạo một mẫu mới.

Cách sử dụng SimpleDateFormat

SimpleDateFormat là một lớp con của DateFormat Công cụ này cho phép bạn định dạng và phân tích cú pháp ngày tháng bằng cách sử dụng các chữ cái mẫu ở trên. Kết hợp các chữ cái để mô tả chính xác chuỗi bạn muốn tạo ra hoặc đọc.

Ví dụ:

  1. Định dạng ngày tháng bắt buộc: 2012.10.23/20/20 45:XNUMX:XNUMX giờ chuẩn Thái Bình DươngMẫu định dạng phù hợp là... yyyy.MM.dd HH:mm:ss zzz.
  2. Định dạng ngày tháng bắt buộc: 09:30:00 AM 23-May-2012Mẫu định dạng phù hợp là... hh:mm:ss a dd-MMM-yyyy.

Mẹo: Xem chữ viết hoa. M là tháng, chữ thường m rất nhỏ. Chữ in hoa H là đồng hồ 24 giờ, chữ thường h Đây là đồng hồ 12 giờ. Việc nhầm lẫn giữa chúng sẽ dẫn đến kết quả sai mà không gây ra tác dụng phụ nào.

Chương trình dưới đây in ngày tháng hiện tại hai lần, một lần theo định dạng mặc định và một lần theo mẫu SimpleDateFormat:

import java.text.SimpleDateFormat;
import java.util.Date;

class TestDates_Format {
    public static void main(String args[]) {
        // Current system date and time is assigned to objDate
        Date objDate = new Date();
        System.out.println(objDate);

        // Build the target pattern and format the date
        String strDateFormat = "hh:mm:ss a dd-MMM-yyyy";
        SimpleDateFormat objSDF = new SimpleDateFormat(strDateFormat);
        System.out.println(objSDF.format(objDate));
    }
}

Đầu ra:

Wed Nov 29 06:31:41 UTC 2017
06:31:41 AM 29-Nov-2017

So sánh ngày Ví dụ

So sánh ngày Ví dụ

Cách đáng tin cậy nhất để đặt hai cái Date các đối tượng là compareTo() Phương thức này trả về một số nguyên âm khi người gọi ở thời điểm sớm hơn, số 0 khi cả hai thời điểm bằng nhau và một số nguyên dương khi người gọi ở thời điểm muộn hơn. Đoạn mã bên dưới phân tích hai chuỗi ngày tháng và in ra thứ tự tương đối của chúng:

import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;

class TestDates_Compare {
    public static void main(String args[]) throws ParseException {

        SimpleDateFormat objSDF = new SimpleDateFormat("dd-MM-yyyy");
        Date dt_1 = objSDF.parse("20-08-1981");
        Date dt_2 = objSDF.parse("12-10-2012");

        System.out.println("Date1 : " + objSDF.format(dt_1));
        System.out.println("Date2 : " + objSDF.format(dt_2));

        // compareTo returns > 0 when dt_1 is after dt_2
        if (dt_1.compareTo(dt_2) > 0) {
            System.out.println("Date 1 occurs after Date 2");
        }
        // compareTo returns < 0 when dt_1 is before dt_2
        else if (dt_1.compareTo(dt_2) < 0) {
            System.out.println("Date 1 occurs before Date 2");
        }
        // compareTo returns 0 when both dates are equal
        else {
            System.out.println("Both are the same date");
        }
    }
}

Đầu ra:

Date1 : 20-08-1981
Date2 : 12-10-2012
Date 1 occurs before Date 2

Lưu ý: Mã gốc sử dụng mẫu dd-mm-yyyy. Chữ thường mm Chữ cái biểu thị phút, không phải tháng, vì vậy trình phân tích cú pháp đã âm thầm tạo ra giá trị tháng sai. Cách khắc phục ở trên sử dụng chữ hoa. MM Đối với tháng, đó là chữ cái theo mẫu chính xác.

Java 8. API ngày giờ hiện đại (java.time)

Java 8 đã giới thiệu java.time Gói này là một API được thiết kế lại, lấy cảm hứng từ thư viện Joda-Time. Các lớp mới là bất biến, an toàn cho đa luồng và tách biệt lịch của con người khỏi dòng thời gian của máy móc. Các kiểu dữ liệu phổ biến nhất là:

  • LocalDate Dùng cho ngày tháng không có giờ hoặc múi giờ (ví dụ: ngày sinh nhật).
  • LocalTime Dùng để hiển thị giờ trên đồng hồ treo tường mà không có ngày tháng hoặc múi giờ.
  • LocalDateTime Đối với ngày và giờ không có múi giờ.
  • ZonedDateTime Cho một ngày và giờ cụ thể với múi giờ nhất định.
  • OffsetDateTime Đối với ngày và giờ có độ lệch UTC nhưng không có múi giờ cụ thể.
  • Instant đối với dấu thời gian của máy được đo từ thời điểm bắt đầu kỷ nguyên UNIX.
  • DurationPeriod Đối với các khoản tiền tính theo thời gian và theo ngày.

Ví dụ dưới đây in ra ngày, giờ, dấu thời gian và múi giờ hiện tại bằng cách sử dụng API hiện đại:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.Instant;
import java.time.ZoneId;

class ModernDateDemo {
    public static void main(String args[]) {
        LocalDate today = LocalDate.now();
        LocalDateTime now = LocalDateTime.now();
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
        Instant epoch = Instant.now();

        System.out.println("LocalDate     : " + today);
        System.out.println("LocalDateTime : " + now);
        System.out.println("ZonedDateTime : " + zoned);
        System.out.println("Instant       : " + epoch);
    }
}

Mỗi loại đều có các phương thức tạo (now, of, parse) và các thao tác bất biến (plusDays, minusHours, withYear) trả về một giá trị mới thay vì làm thay đổi đối tượng gốc.

DateTimeFormatter: Định dạng ngày tháng theo cách hiện đại Java

DateTimeFormatter in java.time.format thay thế SimpleDateFormatNó sử dụng cùng một kiểu chữ cái mẫu, không thể thay đổi và an toàn khi chia sẻ giữa các luồng mà không cần đồng bộ hóa bổ sung. Lớp này cũng cung cấp các hằng số có sẵn như... ISO_LOCAL_DATEISO_ZONED_DATE_TIME cho các định dạng tiêu chuẩn.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class FormatterDemo {
    public static void main(String args[]) {
        LocalDateTime now = LocalDateTime.now();

        DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        DateTimeFormatter human = DateTimeFormatter.ofPattern("hh:mm:ss a dd-MMM-yyyy");

        System.out.println(now.format(iso));
        System.out.println(now.format(human));

        LocalDateTime parsed = LocalDateTime.parse("2026-07-04T09:30:00", iso);
        System.out.println("Parsed back : " + parsed);
    }
}

Để so sánh hai ngày trong API hiện đại, hãy gọi isBefore, isAfter, hoặc là isEqualCác phương pháp này dễ hiểu hơn compareTo và làm việc với mọi người java.time lớp học.

An toàn khi sử dụng ren và các biện pháp tốt nhất Java Ngày và Giờ

Cả hai java.util.DateSimpleDateFormat có trước thời kỳ đồng thời hiện đại JavaMỗi phương pháp đều có những nhược điểm riêng. Hãy tuân thủ các quy tắc dưới đây để đảm bảo việc xử lý ngày tháng chính xác trong mã nguồn sản xuất:

  • Nên ưu tiên sử dụng java.time hơn Date và Calendar. Các kiểu dữ liệu mới này là bất biến và an toàn cho đa luồng, đồng thời mô hình hóa miền dữ liệu chính xác hơn (ngày tháng không có giờ, giờ không có múi giờ, v.v.).
  • Không nên dùng chung một SimpleDateFormat giữa các luồng. Lớp này lưu trữ trạng thái phân tích cú pháp nội bộ và không an toàn cho đa luồng. Hãy bọc nó trong một lớp khác. ThreadLocal, đồng bộ hóa trên phiên bản hoặc chuyển sang DateTimeFormatter.
  • Luôn luôn chỉ định Ngôn ngữ/Vùng miền (Locale). Tên tháng và ngày thay đổi theo ngôn ngữ mặc định của JVM, điều này tạo ra các mẫu như sau: MMMEEE Không tương thích đa nền tảng. Hãy truyền rõ ràng ngôn ngữ địa phương (ví dụ: Locale.ENGLISH).
  • Hãy nêu rõ múi giờ. LocalDateTime không có vùng, vì vậy hãy chuyển đổi thành ZonedDateTime or Instant Khi thời điểm chính xác là rất quan trọng, đặc biệt là đối với nhật ký, cột cơ sở dữ liệu và tải trọng mạng.
  • Hãy chuyển đổi giữa hai API một cách cẩn thận. Sử dụng date.toInstant(), Date.from(instant)calendar.toInstant().atZone(zone) tại ranh giới giữa mã nguồn cũ và mã nguồn hiện đại.
  • Lưu trữ dấu thời gian theo múi giờ UTC. Kiên trì và transmit ngày tháng dưới dạng UTC (một Instant hoặc một chuỗi ISO 8601) và chỉ chuyển đổi sang vùng người dùng khi hiển thị.

Câu Hỏi Thường Gặp

Gọi LocalDateTime.now() từ gói java.time để lấy ngày và giờ theo múi giờ hệ thống, hoặc LocalDate.now() để chỉ lấy ngày. Trên các phiên bản cũ hơn JavaSử dụng `new java.util.Date()` để lấy thời điểm hiện tại.

java.util.Date biểu thị một thời điểm tính bằng mili giây kể từ kỷ nguyên và có thể thay đổi. LocalDate mô hình hóa một ngày tháng trên lịch mà không có thời gian hoặc múi giờ, là bất biến và an toàn cho đa luồng, đồng thời cung cấp các phương thức rõ ràng hơn như plusDays và isBefore.

Không. SimpleDateFormat giữ trạng thái phân tích cú pháp có thể thay đổi và tạo ra kết quả sai khi được chia sẻ giữa các luồng. Hãy bọc nó trong ThreadLocal, đồng bộ hóa trên thể hiện hoặc chuyển sang DateTimeFormatter, vốn bất biến và an toàn hơn.

Hãy nhập chính xác chuỗi ký tự “yyyy-MM-dd”. Chữ MM viết hoa là tháng, chữ mm viết thường là phút. Để có ngày giờ đầy đủ, hãy sử dụng định dạng “yyyy-MM-dd HH:mm:ss”. Luôn chỉ định ngôn ngữ (Locale) để đảm bảo tên tháng được nhất quán.

Với kiểu dữ liệu Date cũ, hãy sử dụng compareTo, phương thức này trả về một số nguyên âm, bằng không hoặc dương. Với java.time, hãy gọi isBefore, isAfter hoặc isEqual trên một đối tượng LocalDate hoặc Instant. Cả hai cách tiếp cận đều tuân theo thứ tự thời gian.

Gọi date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(). Để quay lại, sử dụng Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()). Chọn múi giờ cụ thể khi thời điểm chính xác là quan trọng.

Các mô hình AI được huấn luyện trên các tập dữ liệu nhật ký và tài liệu sẽ suy luận ra mẫu có khả năng xảy ra từ các chuỗi mẫu và trả về mẫu DateTimeFormatter phù hợp. Các thuật toán phân loại máy học cũng đánh dấu các trường không rõ ràng, chẳng hạn như thứ tự tháng-ngày so với thứ tự ngày-tháng.

Đúng vậy. GitHub Copilot đề xuất các mẫu DateTimeFormatter, chuyển đổi ZonedDateTime và... JUnit kiểm tra đầu ra nhạy cảm với ngôn ngữ địa phương. RevHãy xem xét kỹ từng gợi ý về mẫu chữ cái, khu vực và múi giờ chính xác trước khi thực hiện.

Tóm tắt bài viết này với: