The legacy date-time API (java.util
date-time types and their formatting type, SimpleDateFormat
etc.) is outdated and error-prone. Let's first understand how SimpleDateFormat
erroneously processes a string like 00/00/0000
and later we will cover how the modern date-time API prevents an attempt to processes an invalid string like this.
The following demo will help us understand easily both, the concept and the problem:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdfInput = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
SimpleDateFormat sdfOutput = new SimpleDateFormat("dd MMMM yG", Locale.ENGLISH);
System.out.println(sdfOutput.format(sdfInput.parse("00/00/2021")));
}
}
Output:
30 November 2020AD
Some facts about SimpleDateFormat
:
SimpleDateFormat
processes the month, 1
as January
.
- It processes the year, month and day-of-month in a circular fashion which is very similar to how
int
, and long
are processed.

For the sake of completeness, look at the output of the following statements:
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
System.out.println(Integer.MIN_VALUE - 1); // 2147483647
Based on this concept, this is how SimpleDateFormat
has parsed 00/00/2021
:
- Month,
00
: Go back to the last month i.e. December which also means the year will become 2020.
- Day-of-month,
00
: Go back to the last day of the last month which also means December will shift back one place to become November and the last day of November is 30.
Thus, the output will be 30 November 2020AD
.
Quiz: What will be the output for the input string, -01/-01/2021
?
If your answer is: 30 October 2020AD
, you have understood it correctly.
Given below are some more examples:
SimpleDateFormat sdfInput = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
SimpleDateFormat sdfOutput = new SimpleDateFormat("dd MMMM yG", Locale.ENGLISH);
System.out.println(sdfOutput.format(sdfInput.parse("00/00/0000"))); // 30 November 2BC
System.out.println(sdfOutput.format(sdfInput.parse("00/-01/2021"))); // 31 October 2020AD
System.out.println(sdfOutput.format(sdfInput.parse("00/13/2021"))); // 31 December 2021AD
System.out.println(sdfOutput.format(sdfInput.parse("00/14/2021"))); // 31 January 2022AD
Why 2BC
?
Luckily1. there is no year-0. Before 1AD, we have 1BC. So, we know year-0 as 1BC. If you go one year back, it will be 2BC. You would like to check this answer for some more explanation.
You will encounter many such surprises while using the legacy date-time API. For these reasons, it is recommended to stop using it completely and switch to java.time
, the modern date-time API*.
java.time
Let's see how java.time
, the modern API prevents an attempt to processes an invalid string like this:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDate.parse("00/00/0000", DateTimeFormatter.ofPattern("dd/MM/uuuu", Locale.ENGLISH));
}
}
Output:
Exception in thread "main" java.time.format.DateTimeParseException:
Text '00/00/0000' could not be parsed: Invalid value for MonthOfYear
(valid values 1 - 12): 0
Learn more about java.time
, the modern date-time API2. from Trail: Date Time.
1. Otherwise, it would have opened a can of worms regarding whether year-0 should be called 0AD or 0BC.
2. For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.