Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SimpleDateFormat fails to reject input missing century on the year of the input, despite "yyyy" in the formatting pattern

I have a SimpleDateFormat with the pattern yyyy-M-d", and the following scenario:

String str = "02-03-04";        
SimpleDateFormat f = new SimpleDateFormat("yyyy-M-d");
f.setLenient(false);
System.out.println(f.parse(str));

The output is Sat Mar 04 00:00:00 EST 2

My goal was to only catch dates in the format like 2004-02-03 and to ignore 02-03-04. I thought the yyyy in the pattern would require a 4 digit year, but clearly this is not the case. Can anyone explain why this is not throwing a parse exception? I would like it to...

like image 811
Jeff Storey Avatar asked Dec 10 '10 15:12

Jeff Storey


2 Answers

Well, I can explain it from the docs:

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.

It's possible that Joda Time would be stricter - and it's a better API in general, IMO...

You could always throw an exception if the year is less than 1000 after parsing...

like image 170
Jon Skeet Avatar answered Oct 01 '22 10:10

Jon Skeet


tl;dr

Using java.time, that input with that formatting pattern fails, just as you expected.

LocalDate
.parse(
    "02-03-04" ,
    DateTimeFormatter.ofPattern ( "uuuu-M-d" )
)

…throws java.time.format.DateTimeParseException

java.time

The classes you were using are now supplanted by the modern java.time classes defined in JSR 310 and built into Java 8 and later.

The LocalDate class represents a date-only value without time-of-day and without time zone or offset-from-UTC.

Define a custom formatting pattern as you asked. Use the DateTimeFormatter class.

DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuu-M-d" );

Try to parse your input.

String input = "02-03-04";
LocalDate localDate = LocalDate.parse ( input , f );

We encounter a DateTimeParseException, failing on the missing century of the year of the input. Just as you expected.

Exception in thread "main" java.time.format.DateTimeParseException: Text '02-03-04' could not be parsed at index 0


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

like image 41
Basil Bourque Avatar answered Oct 01 '22 12:10

Basil Bourque