John Farrelly
John Farrelly

Reputation: 7459

SimpleDateFormat problems with 2 year date

I'm trying to understand two things:

  1. Why doesn't the following code throw an exception (since the SimpleDateFormat is not lenient)
  2. It doesn't throw an exception, but why is it parsing the year as 0013 (instead of using the rules here the +80:-20 years from today rule)

Here's the code

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

public class TestDate {
    public static void main(String[] args) throws Exception {
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
        format.setLenient(false);
        Date date = format.parse("01/01/13"); // Since this only has a 2 digit year, I would expect an exception to be thrown

        System.out.println(date); // Prints Sun Jan 01 00:00:00 GMT 13

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        System.out.println(cal.get(Calendar.YEAR)); // Prints 13
    }
}

If it makes a difference, I'm using java 1.6.0_38-b05 on Ubuntu

Upvotes: 5

Views: 15957

Answers (4)

Anonymous
Anonymous

Reputation: 86148

java.time

Use java.time, the modern Java date and time API, and the exception you expected comes sure thing:

    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    String strToParse = "01/01/13";
    LocalDate date = LocalDate.parse(strToParse, dateFormatter);

Result:

Exception in thread "main" java.time.format.DateTimeParseException: Text '01/01/13' could not be parsed at index 6
  at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
  at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
  at java.base/java.time.LocalDate.parse(LocalDate.java:428)

If you scroll right, you will see that the exception message mentions “could not be parsed at index 6”. Index 6 in the string is where the two digit year stands where four digits were expected.

When this question was asked nearly 7 years ago, using SimpleDateFormat and Date was acceptable. They were always poorly designed, and their replacement came out 7 months after the question was asked, so they are now considered long outdated. I warmly recommend using java.time instead.

Link

Oracle tutorial: Date Time explaining how to use java.time.

Upvotes: 1

Evgeniy Dorofeev
Evgeniy Dorofeev

Reputation: 135992

SimpleDateFormat API:

For parsing, if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.

As for lenient, when it's set to false parse throws exception for invalid dates, eg 01/32/12, while in lenient mode this date is treated as 02/01/12. SimpleDateFormat uses Calendar internally, details about leniency can be found in Calendar API.

Upvotes: 3

Paul
Paul

Reputation: 20061

According to the SimpleDateFormat javadoc for JDK 1.6,

For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields.

A look at the source code for the method that does the work, SimpleDateFormat.parse(String, ParsePosition), confirms this. There is a variable obeyCount that is true if the pattern has no delimiters, like "yyyyMMdd", and false otherwise. With your pattern the parser looks for 3 numbers separated by 2 delimiters and does not care about the number of digits in each position.

The answers to your questions:

  1. Lenient is not a factor when delimiters are used.
  2. You did not call SimpleDateFormat.set2DigitYearStart so why should the code do what you didn't tell it to do?

Upvotes: 3

Kukanani
Kukanani

Reputation: 748

Question 1. This is a partial duplicate of Why Does Java's SimpleDateFormat parse this. The second answer on the question answers this nicely. The crux of it is this:

Number: For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields. Year: During parsing, only strings consisting of exactly two digits […] will be parsed into the default century. Any other numeric string, such as a one digit string, a three or more digit string, or a two digit string that isn't all digits (for example, "-1"), is interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the same pattern

Question 2. Look carefully: your input format is not the same as the format that you are passing to the function.

new SimpleDateFormat("dd/MM/yyyy");

vs.

format.parse("01/01/13");

Either parse to 01/01/2013 or use the date forat 'dd/MM/yy'.

Upvotes: 3

Related Questions