Bug in Java Date Difference Calculation Caused Massive Reward Emails and Its Fix Using Java 8 API
A game server developer copied a flawed Java function to calculate offline days, which mis‑computed the difference across year boundaries, leading to thousands of erroneous reward emails on 2020‑01‑01, and the issue was resolved by switching to the Java 8 date‑time API.
The author, a game server developer, needed to send item rewards to players based on the number of days they were offline. The implementation was straightforward: compute the day interval between the last offline time and the current login, then award items accordingly. To avoid writing the calculation from scratch, the author copied a Java function from an online source.
public static int differentDays(Date date1, Date date2) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date2);
int day1 = cal1.get(Calendar.DAY_OF_YEAR);
int day2 = cal2.get(Calendar.DAY_OF_YEAR);
int year1 = cal1.get(Calendar.YEAR);
int year2 = cal2.get(Calendar.YEAR);
if (year1 != year2) {
int timeDistance = 0;
for (int i = year1; i < year2; i++) {
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
timeDistance += 366;
} else {
timeDistance += 365;
}
}
return timeDistance + (day2 - day1);
} else {
System.out.println("判断day2 - day1 : " + (day2 - day1));
return day2 - day1;
}
}For several months the function appeared to work, but on 2020‑01‑01 it returned 358 days for the call differentDays("2020-1-1", "2019-12-25") instead of the correct -7. This error caused the system to send hundreds of reward emails, severely breaking the game’s item balance. Investigation revealed that the bug occurs when the two dates are in different years and the first date is later than the second, because the algorithm incorrectly adds whole‑year day counts without handling negative intervals.
The problem was fixed by abandoning the custom calendar logic and using the modern Java 8 date‑time API, which correctly handles all edge cases:
public static int differentDays(Date date1, Date date2) {
if (date1 == null || date2 == null) {
throw new RuntimeException("日期不能为空");
}
LocalDate localDate1 = date2LocalDate(date1);
LocalDate localDate2 = date2LocalDate(date2);
return (int) localDate1.until(localDate2, ChronoUnit.DAYS);
}
public static LocalDate date2LocalDate(Date date) {
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
return instant.atZone(zoneId).toLocalDate();
}The author concludes with a warning: never blindly copy unknown code into production; always test thoroughly, and prefer the robust, well‑designed APIs provided by the language, such as Java 8’s date‑time classes, to avoid hidden bugs that can cause serious operational issues.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.
