Giới thiệu
SimpleDateFormat định dạng và phân tích giá trị java.util.Date sử dụng chuỗi mẫu tùy chỉnh, và DateFormat cung cấp API cơ sở trừu tượng cho định dạng ngày giờ nhận biết locale. Hãy dùng các API này khi bạn duy trì mã Java kế thừa vẫn phụ thuộc vào java.util.Date; cho mã mới, hãy ưu tiên DateTimeFormatter từ API ngày/giờ Java 8+ vì SimpleDateFormat không an toàn cho luồng (thread-safe).
Hướng dẫn này chỉ cách dùng DateFormat và SimpleDateFormat cho định dạng, phân tích, chuyển đổi múi giờ và đầu ra locale-specific. Nó cũng bao gồm xác thực nghiêm ngặt với setLenient(false), các giải pháp thay thế an toàn cho luồng, và đường dẫn thực tế di chuyển sang DateTimeFormatter.
Điểm chính cần nhớ
DateFormatlà API định dạng trừu tượng, vàSimpleDateFormatlà triển khai cụ thể cho định dạng và phân tích dựa trên mẫuSimpleDateFormathỗ trợ các token mẫu phong phú cho năm, tháng, ngày, trường thời gian và đầu ra múi giờ- Một instance
SimpleDateFormatdùng chung không an toàn trong mã đa luồng vì trạng thái nội bộ có thể thay đổi ThreadLocal<SimpleDateFormat>cô lập instance formatter cho mỗi luồng khi mã kế thừa không thể di chuyển ngay lập tứcDateTimeFormatterbất biến và an toàn cho luồng, nên là thay thế ưu tiên cho mã Java mới.parse()có thể némParseException, nên mã phân tích nên dùng try-catch và thông báo lỗi hữu íchsetLenient(false)ngăn chặn việc điều chỉnh lặng lẽ các ngày không hợp lệ, như2024-13-45- Luôn truyền
Localerõ ràng để tránh đầu ra phụ thuộc môi trường trong triển khai server
SimpleDateFormat trong Java là gì?
SimpleDateFormat là lớp con của DateFormat cho phép bạn định nghĩa mẫu ngày/giờ rõ ràng cho định dạng và phân tích trong Java.
Dùng DateFormat khi bạn muốn đầu ra nhận biết locale dựa trên phong cách qua phương thức factory, và dùng SimpleDateFormat khi bạn cần kiểm soát mẫu chính xác như "dd-MM-yyyy" hoặc "yyyy-MM-dd'T'HH:mm:ssZ". Các hằng số phong cách của DateFormat (SHORT, MEDIUM, LONG, FULL) tạo ra đầu ra phù hợp locale mà không yêu cầu bạn biết thứ tự ngày khu vực chính xác, hữu ích cho chuỗi hiển thị hướng người dùng nơi định dạng nên tuân theo quy ước locale của người dùng. Dùng SimpleDateFormat khi định dạng đầu ra được cố định bởi yêu cầu bên ngoài như hợp đồng API, định dạng log hoặc quy ước đặt tên tệp, vì những trường hợp đó yêu cầu mẫu chính xác bất kể locale.
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
Locale locale = Locale.US;
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String styleBased = dateFormat.format(new Date());
SimpleDateFormat patternBased = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", locale);
String patternOutput = patternBased.format(new Date());
System.out.println(styleBased);
System.out.println(patternOutput);
Jan 5, 2026
05-01-2026 14:30:22
new Locale(String, String) đã bị deprecated kể từ Java 19. Hãy dùng Locale.Builder thay thế.
Locale locale = new Locale.Builder()
.setLanguage("en")
.setRegion("US")
.build();
Để định dạng thời gian thay vì ngày, dùng DateFormat.getTimeInstance() với cùng tham số locale và phong cách.
Locale locale = Locale.US;
DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
System.out.println(timeFormat.format(new Date()));
Đầu ra phụ thuộc vào thời gian và múi giờ hiện tại, ví dụ: 2:30:45 PM
Để có cái nhìn rộng hơn về API ngày/giờ Java hiện đại, xem Java 8 Date, LocalDate, LocalDateTime, Instant và Java 8 Features with Examples.
Tài liệu tham khảo cú pháp mẫu SimpleDateFormat
Các ký tự mẫu của SimpleDateFormat ánh xạ trực tiếp đến thành phần ngày/giờ, và việc chọn mẫu đúng kiểm soát cả định dạng đầu ra và hành vi phân tích.
| Ký hiệu | Ý nghĩa | Mẫu ví dụ | Đầu ra ví dụ |
|---|---|---|---|
G | Th紀元 chỉ định | G | AD |
y | Năm | yyyy | 2026 |
M | Tháng trong năm | MMMM | January |
w | Tuần trong năm | w | 3 |
W | Tuần trong tháng | W | 1 |
D | Ngày trong năm | D | 15 |
d | Ngày trong tháng | dd | 05 |
F | Ngày trong tuần của tháng | F | 1 |
E | Tên ngày trong tuần | EEE | Mon |
u | Số ngày trong tuần (1 = Thứ Hai) | u | 1 |
a | Dấu hiệu AM/PM | a | PM |
H | Giờ trong ngày (0-23) | HH | 14 |
k | Giờ trong ngày (1-24) | k | 14 |
K | Giờ trong am/pm (0-11) | K | 2 |
h | Giờ trong am/pm (1-12) | hh | 02 |
m | Phút trong giờ | mm | 30 |
s | Giây trong phút | ss | 45 |
S | Mili giây | SSS | 123 |
z | Múi giờ chung | z | IST |
Z | Độ lệch múi giờ RFC 822 | Z | +0530 |
X | Độ lệch múi giờ ISO 8601 | XXX | +05:30 |
Lặp lại mẫu và độ rộng đầu ra
Các ký tự mẫu lặp lại thay đổi độ rộng, đệm số hoặc phong cách văn bản.
| Trường | Mẫu | Đầu ra ví dụ |
|---|---|---|
| Tháng | M | 1 |
| Tháng | MM | 01 |
| Tháng | MMM | Jan |
| Tháng | MMMM | January |
| Ngày trong tháng | d | 5 |
| Ngày trong tháng | dd | 05 |
| Giờ (0-23) | H | 7 |
| Giờ (0-23) | HH | 07 |
| Năm | yy | 26 |
| Năm | yyyy | 2026 |
Các ví dụ mẫu phổ biến với đầu ra mong đợi
Các mẫu phổ biến này hữu ích cho log, API và ngày hướng người dùng.
| Mẫu | Đầu ra ví dụ |
|---|---|
MM/dd/yyyy | 01/05/2026 |
dd-M-yyyy hh:mm:ss | 05-1-2026 02:30:45 |
dd MMMM yyyy | 05 January 2026 |
dd MMMM yyyy zzzz | 05 January 2026 India Standard Time |
E, dd MMM yyyy HH:mm:ss z | Mon, 05 Jan 2026 14:30:45 IST |
Cách định dạng ngày dùng SimpleDateFormat
Để định dạng một Date, tạo SimpleDateFormat với mẫu và gọi .format(date).
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
Date now = new Date();
SimpleDateFormat basic = new SimpleDateFormat("MM-dd-yyyy", Locale.US);
SimpleDateFormat withTime = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.US);
SimpleDateFormat isoLike = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
System.out.println(basic.format(now));
System.out.println(withTime.format(now));
System.out.println(isoLike.format(now));
01-05-2026
05-01-2026 14:30:45
2026-01-05T14:30:45+0000
SimpleDateFormat áp dụng quy tắc múi giờ và locale từ instance formatter, nên hãy đặt những giá trị đó rõ ràng khi đầu ra phải nhất quán.
Cách phân tích chuỗi thành ngày dùng SimpleDateFormat
Để phân tích chuỗi thành Date, tạo formatter với mẫu đầu vào chính xác và gọi .parse(input).
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
try {
SimpleDateFormat dateParser = new SimpleDateFormat("dd-MM-yyyy", Locale.US);
Date parsedDate = dateParser.parse("05-01-2026");
System.out.println(parsedDate);
SimpleDateFormat timeParser = new SimpleDateFormat("HH:mm:ss", Locale.US);
Date parsedTime = timeParser.parse("22:15:09");
System.out.println(parsedTime);
} catch (ParseException e) {
e.printStackTrace();
}
Mon Jan 05 00:00:00 UTC 2026
Thu Jan 01 22:15:09 UTC 1970
Nếu chỉ cung cấp thời gian, Java dùng ngày epoch làm phần ngày.
Xử lý ParseException đúng cách
parse() ném ParseException khi đầu vào không khớp với mẫu cấu hình, nên mã production nên bắt nó và log cả đầu vào thô và mẫu mong đợi.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
String input = "31/01/2026";
String pattern = "dd-MM-yyyy";
SimpleDateFormat parser = new SimpleDateFormat(pattern, Locale.US);
try {
parser.parse(input);
System.out.println("Parsed successfully");
} catch (ParseException e) {
// Bao gồm cả giá trị đầu vào và mẫu để debug dễ dàng hơn.
System.err.println("Failed to parse date string: " + input);
System.err.println("Expected pattern: " + pattern);
System.err.println("Parser error: " + e.getMessage());
}
Failed to parse date string: 31/01/2026
Expected pattern: dd-MM-yyyy
Parser error: Unparseable date: "31/01/2026"
Để có mẫu xử lý ngoại lệ tái sử dụng cho các dịch vụ, xem Exception Handling in Java và Java 8 Date, LocalDate, LocalDateTime, Instant.
Xác thực chuỗi ngày trước khi phân tích
SimpleDateFormat mặc định lenient (rộng rãi), nên giá trị không hợp lệ có thể roll over thay vì thất bại; gọi setLenient(false) để thực thi xác thực nghiêm ngặt.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
String invalid = "2024-13-45";
SimpleDateFormat lenientParser = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
SimpleDateFormat strictParser = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
strictParser.setLenient(false);
System.out.println("Lenient parse result:");
try {
// Chế độ lenient lặng lẽ roll over giá trị không hợp lệ thay vì ném ngoại lệ.
System.out.println(lenientParser.parse(invalid));
} catch (ParseException e) {
System.out.println("ParseException: " + e.getMessage());
}
System.out.println("Strict parse result:");
try {
System.out.println(strictParser.parse(invalid));
} catch (ParseException e) {
System.out.println("ParseException: " + e.getMessage());
}
Lenient parse result: Fri Feb 14 00:00:00 UTC 2025
Strict parse result: ParseException: Unparseable date: "2024-13-45"
Xử lý múi giờ trong SimpleDateFormat
SimpleDateFormat có thể hiển thị cùng một Date trong các múi giờ khác nhau bằng cách đặt múi giờ trên formatter trước khi xuất. Đối tượng Date chỉ lưu trữ số mili giây UTC và không mang múi giờ riêng. Múi giờ là mối quan tâm định dạng, không phải lưu trữ; cùng giá trị Date tạo ra đầu ra đọc được khác nhau tùy theo múi giờ formatter áp dụng. Điều này có nghĩa setTimeZone() phải được gọi trên formatter trước khi gọi format(); đặt sau đó không ảnh hưởng đến đầu ra đã tạo.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
Date timestamp = new Date(1700000000000L); // Instant cố định cho đầu ra lặp lại
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("UTC: " + formatter.format(timestamp));
formatter.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("New York: " + formatter.format(timestamp));
formatter.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
System.out.println("Kolkata: " + formatter.format(timestamp));
UTC: 2023-11-14 22:13:20 +0000
New York: 2023-11-14 17:13:20 -0500
Kolkata: 2023-11-15 03:43:20 +0530
Xem How to Convert Java Date into Specific TimeZone format để biết mẫu chuyển đổi múi giờ sâu hơn.
Lỗi thường gặp về múi giờ và cách tránh
Lỗi múi giờ thường xuất phát từ việc nhầm lẫn giữa instant lưu trữ và thời gian địa phương hiển thị.
Datelưu mili giây UTC, và múi giờ chỉ áp dụng khi định dạng- Gọi
setTimeZone()trên instance formatter dùng chung trong môi trường đa luồng có thể thay đổi múi giờ giữa các thao tác của luồng khác, tạo ra đầu ra sai múi giờ mà không ném ngoại lệ - Tái sử dụng một instance
SimpleDateFormatduy nhất qua các HTTP request trong servlet hoặc Spring controller có thể gây ra việc gọisetTimeZone()của request này ảnh hưởng đến đầu ra của request khác; tạo instance mới mỗi request hoặc dùngThreadLocal<SimpleDateFormat>để cô lập trạng thái TimeZone.getTimeZone("Invalid/Zone")lặng lẽ fallback về GMT vì hợp đồng phương thức chỉ định GMT làm mặc định cho ID không nhận diện thay vì ném ngoại lệ- Xác thực zone ID với
ZoneId.of()từjava.timetrước khi truyền choTimeZone.getTimeZone();ZoneId.of()némZoneRulesExceptiontrên ID không hợp lệ, làm rõ vấn đề
Định dạng ngày nhận biết Locale
Để tạo đầu ra ngày locale-specific như tên tháng tiếng Pháp hoặc tên ngày tiếng Đức, truyền Locale cho constructor SimpleDateFormat. Dùng các hằng số tích hợp Locale.US, Locale.UK, Locale.FRENCH, Locale.GERMAN cho các khu vực phổ biến, hoặc xây dựng locale tùy chỉnh với Locale.Builder khi cần khu vực không có trong hằng số. Locale kiểm soát ngôn ngữ dùng cho các trường văn bản như tên tháng, tên ngày và dấu hiệu AM/PM.
Chỉ định Locale trong constructor SimpleDateFormat
Truyền Locale làm đối số constructor thứ hai. Nếu bỏ qua, SimpleDateFormat gọi Locale.getDefault() nội bộ, gắn formatter với locale runtime của JVM, giá trị khác nhau giữa máy dev, môi trường CI và server production chạy dưới cấu hình locale OS khác nhau.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
Date sample = new Date(1700000000000L);
String pattern = "EEEE, dd MMMM yyyy";
SimpleDateFormat usFormatter = new SimpleDateFormat(pattern, Locale.US);
SimpleDateFormat frFormatter = new SimpleDateFormat(pattern, Locale.FRENCH);
String usDate = usFormatter.format(sample);
String frDate = frFormatter.format(sample);
System.out.println("US: " + usDate);
System.out.println("French: " + frDate);
US: Tuesday, 14 November 2023
French: mardi, 14 novembre 2023
Định dạng tên tháng và ngày theo khu vực
Cùng mẫu tạo ra đầu ra văn bản khác nhau theo locale.
| Locale | Mẫu | Đầu ra |
|---|---|---|
Locale.US | EEEE, dd MMMM yyyy | Tuesday, 14 November 2023 |
Locale.FRENCH | EEEE, dd MMMM yyyy | mardi, 14 novembre 2023 |
Locale.GERMAN | EEEE, dd MMMM yyyy | Dienstag, 14 November 2023 |
Phụ thuộc vào Locale.getDefault() trong JVM chạy trong môi trường server có thể tạo đầu ra không nhất quán vì cài đặt locale OS khác nhau giữa các host. Luôn truyền Locale rõ ràng.
Vấn đề an toàn luồng với SimpleDateFormat
Tại sao SimpleDateFormat không an toàn cho luồng
SimpleDateFormat thay đổi trạng thái Calendar và NumberFormat nội bộ trong thao tác phân tích/định dạng. Nếu nhiều luồng dùng một instance, các trường có thể thay đổi này có thể bị ghi đè giữa chừng, gây race condition và đầu ra không thể đoán trước. Thực tế, điều này biểu hiện là ngày từ luồng này xuất hiện trong kết quả của luồng kia, .parse() trả về ngày hoàn toàn sai mà không có ngoại lệ, hoặc ArrayIndexOutOfBoundsException được ném từ bên trong formatter. Các lỗi này ngẫu nhiên và phụ thuộc tải, khiến chúng khó tái tạo trong môi trường test đơn luồng.
Khi bạn cần định dạng an toàn cho đồng thời trong mã kế thừa, hãy cô lập instance theo luồng hoặc phạm vi request. Về hành vi luồng Java, xem Java Thread Example.
Dùng ThreadLocal để cô lập instance SimpleDateFormat
ThreadLocal<SimpleDateFormat> cho mỗi luồng instance formatter riêng và tránh trạng thái có thể thay đổi dùng chung. Trong môi trường thread-pool như servlet container và ứng dụng Spring, luồng được tái sử dụng qua các request và không bao giờ bị kết thúc, nghĩa là giá trị ThreadLocal không bao giờ tự động bị xóa. Luôn gọi FORMATTER.remove() sau khi thao tác hoàn thành, hoặc instance formatter sẽ tồn tại suốt vòng đời luồng và tích tụ thành rò rỉ bộ nhớ dưới tải bền bỉ.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class ThreadSafeLegacyFormatter {
// Mỗi luồng có một instance formatter chuyên dụng.
private static final ThreadLocal<SimpleDateFormat> FORMATTER = ThreadLocal.withInitial(() ->
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US));
public static String format(Date date) {
try {
// Không có đột biến xuyên luồng vì mỗi luồng đọc formatter của mình.
return FORMATTER.get().format(date);
} finally {
// Xóa instance sau khi dùng để ngăn rò rỉ bộ nhớ trong thread pool.
FORMATTER.remove();
}
}
}
Chuyển sang DateTimeFormatter như giải pháp an toàn cho luồng
DateTimeFormatter bất biến và an toàn cho luồng, nên một instance static dùng chung là an toàn.
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
// Không an toàn cho luồng khi chia sẻ qua các luồng
SimpleDateFormat unsafe = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
System.out.println(unsafe.format(new Date()));
// An toàn cho luồng: DateTimeFormatter bất biến và có thể lưu làm trường static
DateTimeFormatter safe = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.US);
System.out.println(safe.format(LocalDateTime.now()));
2026-01-05 14:30:22
2026-01-05 14:30:22
Di chuyển từ SimpleDateFormat sang DateTimeFormatter
Di chuyển sang DateTimeFormatter loại bỏ rủi ro an toàn luồng, loại bỏ các giải pháp ThreadLocal, và tích hợp gọn gàng với các kiểu java.time. Đối với hầu hết codebase, việc di chuyển là cơ học: cú pháp mẫu gần như giống nhau, hai API có thể cùng tồn tại trong quá trình triển khai tăng dần, và bạn không cần chuyển đổi mọi thứ cùng lúc. Rủi ro chính là ký hiệu u, nghĩa là ngày trong tuần trong SimpleDateFormat và năm trong DateTimeFormatter; mẫu sao chép trực tiếp giữa các API mà không kiểm tra điều này sẽ lặng lẽ tạo ra đầu ra sai.
So sánh mã song song
Đoạn mã song song này minh họa hành vi định dạng và phân tích tương đương trong API kế thừa và hiện đại.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
String pattern = "yyyy-MM-dd";
String input = "2026-01-05";
// SimpleDateFormat (API java.util.Date kế thừa)
SimpleDateFormat legacyFormatter = new SimpleDateFormat(pattern, Locale.US);
Date legacyParsed = legacyFormatter.parse(input);
String legacyFormatted = legacyFormatter.format(new Date());
// DateTimeFormatter (API java.time hiện đại)
DateTimeFormatter modernFormatter = DateTimeFormatter.ofPattern(pattern, Locale.US);
LocalDate modernParsed = LocalDate.parse(input, modernFormatter);
String modernFormatted = modernFormatter.format(LocalDate.now());
System.out.println(legacyParsed);
System.out.println(legacyFormatted);
System.out.println(modernParsed);
System.out.println(modernFormatted);
Mon Jan 05 00:00:00 UTC 2026
2026-01-05
2026-01-05
2026-01-05
Khác biệt cú pháp mẫu giữa hai API
Hầu hết chữ cái mẫu tương tự, nhưng một số ký hiệu khác nhau và có thể phá vỡ di chuyển nếu sao chép trực tiếp.
| Nhiệm vụ | Mẫu SimpleDateFormat | Mẫu DateTimeFormatter | Ghi chú |
|---|---|---|---|
| Năm | yyyy | uuuu hoặc yyyy | Ưu tiên uuuu trong java.time cho tính nhất quán năm proleptic |
| Độ lệch/tên múi giờ | z, Z, X | z, Z, x, X | java.time phân biệt nhiều biến thể offset hơn (x và X) |
| Văn bản literal | 'T' | 'T' | Cả hai API dùng dấu nháy đơn cho literal |
Ký hiệu u | Số ngày trong tuần | Năm | Cạm bẫy nổi tiếng: u thay đổi ý nghĩa giữa API |
Checklist di chuyển từng bước
Dùng checklist này để chuyển đổi từng formatter một trong codebase hiện có.
- Thay
import java.text.SimpleDateFormat;bằngimport java.time.format.DateTimeFormatter; - Thay
new SimpleDateFormat("yyyy-MM-dd")bằngDateTimeFormatter.ofPattern("yyyy-MM-dd") - Thay
.parse()bằngLocalDate.parse()hoặcLocalDateTime.parse()tùy độ chính xác đầu vào - Thay
.format(date)bằngformatter.format(localDate)hoặcformatter.format(localDateTime) - Loại bỏ
new Date()nơi có thể và dùngLocalDate.now()hoặcInstant.now()
DateTimeFormatter hoạt động độc quyền với các kiểu java.time (LocalDate, LocalDateTime, ZonedDateTime, Instant). Nó không chấp nhận java.util.Date trực tiếp. Nếu bạn có mã vẫn tạo giá trị java.util.Date, ví dụ từ ORM kế thừa hoặc JDBC driver, hãy chuyển đổi trước với date.toInstant() trước khi truyền cho DateTimeFormatter. Hai API có thể cùng tồn tại trong cùng codebase trong quá trình di chuyển tăng dần; bạn không cần chuyển đổi mọi thứ cùng lúc. DateTimeFormatter cũng thay thế ParseException bằng java.time.format.DateTimeParseException, là ngoại lệ unchecked, nên khối try-catch trở nên tùy chọn nhưng vẫn khuyến khích trong mã phân tích production.
Đối với ngữ cảnh di chuyển và lựa chọn kiểu, tham khảo Java 8 Date, LocalDate, LocalDateTime, Instant.
Câu hỏi Thường gặp
Khác biệt giữa SimpleDateFormat và DateFormat trong Java là gì?
DateFormat là lớp trừu tượng định nghĩa hợp đồng cho định dạng ngày/giờ. SimpleDateFormat là lớp con cụ thể cho phép định dạng dựa trên mẫu tùy chỉnh. Bạn dùng SimpleDateFormat trực tiếp trong mã; bạn dùng DateFormat như kiểu tham chiếu khi muốn chấp nhận bất kỳ triển khai nào.
SimpleDateFormat có an toàn cho luồng không?
Không. Instance SimpleDateFormat không an toàn cho luồng. Chia sẻ một instance qua các luồng mà không đồng bộ hóa gây ra kết quả phân tích và định dạng không thể đoán trước. Dùng ThreadLocal<SimpleDateFormat> hoặc thay bằng java.time.format.DateTimeFormatter, bất biến và an toàn cho luồng.
Cách phân tích chuỗi ngày định dạng dd-MM-yyyy dùng SimpleDateFormat?
Khởi tạo SimpleDateFormat với mẫu "dd-MM-yyyy", sau đó gọi .parse(yourString). Bọc lời gọi trong khối try-catch cho ParseException. Đảm bảo chuỗi đầu vào khớp chính xác mẫu, bao gồm ký tự phân tách.
Cách chuyển Date sang múi giờ khác dùng SimpleDateFormat?
Gọi .setTimeZone(TimeZone.getTimeZone("America/New_York")) trên instance SimpleDateFormat trước khi gọi .format(date). Đối tượng Date bên dưới chứa mili giây UTC; formatter áp dụng độ lệch múi giờ khi xuất.
Mẫu "yyyy-MM-dd'T'HH:mm:ssZ" tạo ra gì?
Nó tạo timestamp tương thích ISO 8601, ví dụ 2024-06-15T14:30:00+0530. T là ký tự literal bọc trong dấu nháy đơn. Z đại diện cho độ lệch múi giờ RFC 822.
Cách di chuyển từ SimpleDateFormat sang DateTimeFormatter?
Thay new SimpleDateFormat("yyyy-MM-dd") bằng DateTimeFormatter.ofPattern("yyyy-MM-dd"). Thay lời gọi .parse() bằng LocalDate.parse() hoặc LocalDateTime.parse(). Thay lời gọi .format(date) bằng formatter.format(localDate). Lưu ý DateTimeFormatter dùng u cho năm trong một số ngữ cảnh thay vì y.
Tại sao SimpleDateFormat phân tích ngày không hợp lệ mà không ném ngoại lệ?
Mặc định, SimpleDateFormat dùng chế độ phân tích lenient, điều chỉnh giá trị ngoài phạm vi thay vì từ chối. Gọi .setLenient(false) trước khi phân tích để thực thi xác thực nghiêm ngặt và ném ParseException trên đầu vào không hợp lệ.
Cách định dạng ngày với locale cụ thể dùng SimpleDateFormat?
Truyền Locale làm đối số thứ hai cho constructor: new SimpleDateFormat("dd MMMM yyyy", Locale.FRENCH). Điều này hiển thị tên tháng và ngày bằng ngôn ngữ chỉ định, quan trọng cho ứng dụng phục vụ nhiều khu vực.
Kết luận
Hướng dẫn này bao gồm cơ bản DateFormat và SimpleDateFormat, cú pháp mẫu, quy trình định dạng và phân tích, hành vi múi giờ, hiển thị locale-aware, vấn đề an toàn luồng và di chuyển sang DateTimeFormatter.
Bây giờ bạn có thể chọn formatter phù hợp cho mã kế thừa và hiện đại, phân tích đầu vào phòng thủ với xác thực nghiêm ngặt, tránh trôi dạt locale và múi giờ trong môi trường server, và loại bỏ rủi ro đồng thời từ formatter ngày dùng chung.
Để bước tiếp theo, xem lại Java 8 Date, LocalDate, LocalDateTime, Instant, Java 8 Features with Examples, Exception Handling in Java, và How to Convert Java Date into Specific TimeZone format để tiếp tục hiện đại hóa xử lý ngày/giờ trong ứng dụng Java.
Tham khảo:
- SimpleDateFormat API Doc
- DateFormat API Doc
- DateTimeFormatter API Doc
Bài viết này được cấp phép dưới Giấy phép Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.