Lion
Lion

Reputation: 19027

Wonky dating in Java

The following Java code just parses a date (with time portion) 2009-01-28-09:11:12 using SimpleDateFormat. Let's have look at it.

final public class Main
{
    public static void main(String[] args)
    {            
        try
        {
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            Date d = df.parse("2009-01-28-09:11:12");
            System.out.println(d);
        }
        catch (ParseException ex)
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

The date (with time) displayed (after parsing) by the above code is as follows,

Sun Nov 30 22:07:51 IST 2008

even though we are attempting to parse the date 2009-01-28-09:11:12. It looks somewhat wonky. Why does it parse so?

Upvotes: 3

Views: 401

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 338171

tl;dr

If you must use a custom format for your data rather than a standard format, define a formatting pattern to match with java.time.format.DateTimeFormatter to parse into a java.time.LocalDateTime object.

LocalDateTime
.parse ( 
    "2009-01-28-09:11:12" , 
    DateTimeFormatter.ofPattern( "uuuu-MM-dd-HH:mm:ss" ) 
)
.toString()  // Generate text in standard ISO 8601 format. 

2009-01-28T09:11:12

Avoid legacy classes

You are using terribly-flawed legacy classes that were supplanted by the modern java.time classes defined in JSR 310 for Java 8+.

Never use Date, Calendar, Timestamp, SimpleDateFormat, etc.

java.time

DateTimeFormatter

Define a formatting pattern to match your non-standard format. Instantiate a DateTimeFormatter object.

String input = "2009-01-28-09:11:12" ;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "uuuu-MM-dd-HH:mm:ss" ) ;

LocalDateTime

To represent a date with time-of-day but lacking the context of a time zone or offset-from-UTC, use java.time.LocalDateTime.

LocalDateTime ldt = LocalDateTime.parse ( input , formatter );

ISO 8601

ISO 8601 is an international standard of date-time formats in text.

date (with time portion) 2009-01-28-09:11:12

Rather than invent your own format, use ISO 8601. The standard format for your date uses a T between the date portion and the time portion.

2009-01-28T09:11:12

The java.time classes use ISO 8601 formats by default when parsing/generating text.

Parsing:

LocalDateTime ldt = LocalDateTime.parse( "2009-01-28T09:11:12" ) ;

Generating:

String output = ldt.toString() ;  // Produces "2009-01-28T09:11:12".

Upvotes: 1

icyrock.com
icyrock.com

Reputation: 28578

Shouldn't your date format be something like this:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");

to match this format:

Date d = df.parse("2009-01-28-09:11:12");

?

As for why, per this:

the parser actually looks at these as numbers, and the trick is that - is a part of a number, representing negative numbers. So if you do:

df.parse("2009-01-02-00:00:00")

it gives:

Mon Dec 01 00:02:00 EST 2008

That parses 2009 as yyyy, then -0 as MM (which is previous month as months start from 1), then 1 as dd, etc.

As per parse in DateFormat:

By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).

I guess, if you have an option, it would be better to use slashes instead of dashes, if you like formats like 2009/01/02 12:34:56. This:

df.parse("2009/01/02-00:00:00")

will throw an exception:

ERROR java.text.ParseException:
Unparseable date: "2009/01/02-00:00:00"

I can only conclude it's a very good thing that / is not considered a number division by DateFormat...

Upvotes: 5

Joni
Joni

Reputation: 111219

The dates are parsed in lenient mode by default. This means that it's forgiving to errors like parsing feb 29 as mar 1 if it's not a leap year. What you are asking it to parse is the -28th day of the -1st month of 2009, and it dutifully tries to give you exactly what you ask for.

To turn off this behaviour call format.setLenient(false). Then the dateformat will throw an exception if you try to parse something that's not a real date.

Upvotes: 1

Hauke Ingmar Schmidt
Hauke Ingmar Schmidt

Reputation: 11607

Well, you could define the format that you actually are parsing...

Upvotes: 2

Related Questions