Common Java Date Handling Pitfalls: Leap Year, Formatting, Timezone, and Thread Safety
This article explains several Java date‑time pitfalls—including leap‑year calculations, differences between YYYY/yyyy and HH/hh patterns, SimpleDateFormat initialization, Calendar hour fields, integer overflow in date arithmetic, thread‑unsafe usage of SimpleDateFormat, and daylight‑saving‑time handling—providing code examples and best‑practice recommendations.
In 2024, a leap‑year related production bug exposed inconsistencies between front‑end and back‑end date validation, prompting a detailed discussion of common Java date handling pitfalls.
Leap‑year bug: When a user selects a range from 2023‑02‑28 to 2024‑02‑29 , the front‑end subtracts 12 months from the end date and deems the range valid, while the back‑end adds 12 months to the start date and obtains 2024‑02‑28 , marking the range illegal. The discrepancy stems from how LocalDate.plusMonths treats February 29 in a leap year.
public class Test {
public static void main(String[] args) {
String startDateString = "20230228";
String endDateString = "20240229";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate startDate = LocalDate.parse(startDateString, formatter);
LocalDate endDate = LocalDate.parse(endDateString, formatter);
LocalDate startDatePlus12Months = startDate.plusMonths(12);
if (startDatePlus12Months.isBefore(endDate)) {
System.out.println("时间区间超过一年");
throw new RuntimeException();
} else {
System.out.println("时间区间小于一年");
}
}
}The program prints 时间区间超过一年 , illustrating the off‑by‑one‑day issue.
Date‑format patterns: YYYY represents the week‑based year, which can differ from the calendar year yyyy . Likewise, HH denotes a 24‑hour clock while hh uses a 12‑hour clock, causing hour‑shift errors when parsing.
SimpleDateFormat initialization: Without specifying a locale, parsing English month names fails in a default Chinese locale. Adding Locale.US resolves the DateTimeParseException .
String dateStr = "Wed Mar 18 10:00:00 2020";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy", Locale.US);
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);Calendar hour field: Using Calendar.HOUR (12‑hour field) sets the hour to 10 AM but the resulting time may appear as 22 h because the AM/PM flag is unchanged. The correct field is Calendar.HOUR_OF_DAY for a 24‑hour value.
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 10);
System.out.println(c.getTime());Date arithmetic overflow: Adding 30 days with an int can overflow, producing a date earlier than expected. Using a long literal ( 30L ) or the Java 8 plusDays API avoids this problem.
SimpleDateFormat thread safety: Sharing a single SimpleDateFormat instance across multiple threads leads to NumberFormatException and incorrect parsing. Replace it with the thread‑safe DateTimeFormatter or use separate formatter instances per thread.
public class SimpleDateFormatTest {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// ... multithreaded usage causing exceptions ...
}Daylight‑saving‑time (DST) issue: Parsing 1986‑05‑04 00:30:00 with the default Shanghai timezone adds one hour because China observed DST in 1986. Setting the timezone to GMT+8 or disabling DST yields the correct time.
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.parse("1986-05-04 00:30:00"));Developers are advised to use the modern Java 8+ date‑time API, be aware of leap‑year edge cases, choose the correct format symbols, avoid mutable shared formatters, and handle time‑zone/DST settings explicitly.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.