Getting wrong data when using SimpleDateFormat.parse()
Asked Answered
D

3

12

I am getting the strangest error, when trying to parse a string as a calendar. It seems that it messes up the Date object which I use to set the result calendar's time. The error is pretty inconsistent (or I see no logic in it). Can anyone point out what I might be doing wrong ?

public class caltest{
public static final SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");

public static void main(String[] args) {
    String date1 = "1992-03-11 12:00:12.123";
    String date2 = "1993-03-11 12:00:12.123";
    String date3 = "1994-03-11 12:00:12.123";
    String date4 = "1995-03-11 12:00:12.123";
    parseStringAsCalendar(date1);
    parseStringAsCalendar(date2);
    parseStringAsCalendar(date3);
    parseStringAsCalendar(date4);
}
public static String calendarToString(Calendar cal) {
    return sdf.format(cal.getTime());
}

public static Calendar parseStringAsCalendar(String s) {
    Date time = null;
    try {
        time = sdf.parse(s);
    } catch (ParseException e) {
        System.out.println("Exception");
        e.printStackTrace();
    }
    System.out.println(time.toString());
    GregorianCalendar ret = new GregorianCalendar();
    ret.setTime(time);

    return ret;
}

}

The output is :

Sun Dec 29 12:00:12 CET 1991
Sun Dec 27 12:00:12 CET 1992
Sun Dec 26 12:00:12 CET 1993
Sun Jan 01 12:00:12 CET 1995
Dorri answered 22/3, 2012 at 23:16 Comment(0)
S
19

You're using YYYY in your format specifier, which is week year (as of Java 7, I believe). You want yyyy, which is just "year". (See the SimpleDateFormat documentation.)

I suspect the rest of the date was out because you tried to also specify the month and day, which aren't really "features" in the week year... if you'd specified the "week of week year" and day of week, it might have given some more sensible results, but only if you really meant to use week years, which I doubt :)

Starstarboard answered 22/3, 2012 at 23:18 Comment(4)
Both answers were very useful, this one shines more of a light on the underlying problem. I was checking the wrong documentation.Dorri
you just saved me from several hours of fruitless search on the internet! thank youBook
What exactly is the difference between year and week year? The documentation is a little sparse on this distinction (as is the documentation for Calendar).Doubleganger
@tytk: Year is the conventional year, where a year is broken into year/month/day. Week-year is a more "week-of-year" focused way of breaking things down, where each date is represented as "week-year, week of week-year, day-of-week".Starstarboard
G
3

Use this:

public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

It's lower case y for year, not upper case Y. With that, the result is:

Wed Mar 11 12:00:12 EST 1992
Thu Mar 11 12:00:12 EST 1993
Fri Mar 11 12:00:12 EST 1994
Sat Mar 11 12:00:12 EST 1995

See here:

Grandpa answered 22/3, 2012 at 23:21 Comment(0)
G
2

java.time

The question and existing answers use SimpleDateFormat which was the correct thing to do in 2012. In Mar 2014, the java.util date-time API and their formatting API, SimpleDateFormat were supplanted by the modern Date-Time API. Since then, it is strongly recommended to stop using the legacy date-time API.

Solution using the modern date-time API: As you can learn from the documentation, the symbol Y is used for week-based-year whereas we need y (year-of-era) in this case. However, I prefer u to y.

Note that your date-time string has just date and time units (no time-zone or time-zone offset etc.). The java.time API provides you with LocalDateTime to represent such an object.

In case, you need to obtain a date-time object with time-zone or one representing just a moment in time, java.time provides you with specific types. You can check overview of java.time types here.

With Java 8, java.util date-time API was also upgraded to make it easy to switch to java.time API e.g. if you need java.util.Date instance from a an Instant, you can use Date#from.

Demo:

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        String strDateTime = "1992-03-11 12:00:12.123";

        DateTimeFormatter parser = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);

        // An alternative parser
        DateTimeFormatter ldtParser = new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral(' ').append(DateTimeFormatter.ISO_LOCAL_TIME).toFormatter(Locale.ENGLISH);

        LocalDateTime ldt = LocalDateTime.parse(strDateTime, parser);
        System.out.println(ldt);

        // Parsing using the alternative parser
        ldt = LocalDateTime.parse(strDateTime, ldtParser);
        System.out.println(ldt);

        // Converting LocalDateTime to a ZonedDateTime
        // Replace ZoneId.systemDefault() with applicable ZoneId e.g.
        // ZoneId.of("America/New_York")
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = ldt.atZone(zoneId);
        System.out.println(zdt);
        // Alternatively,
        zdt = ZonedDateTime.of(ldt, zoneId);
        System.out.println(zdt);

        // Obtaining an Instant
        Instant instant = zdt.toInstant();
        System.out.println(instant);

        // In case you need an instance of java.util.Date
        Date date = Date.from(instant);
    }
}

Output in my time-zone, Europe/London:

1992-03-11T12:00:12.123
1992-03-11T12:00:12.123
1992-03-11T12:00:12.123Z[Europe/London]
1992-03-11T12:00:12.123Z[Europe/London]
1992-03-11T12:00:12.123Z

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.


Note: Check this answer and this answer to learn how to use java.time API with JDBC.

Golden answered 29/12, 2022 at 9:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.