Nathan Schwermann
Nathan Schwermann

Reputation: 31493

SimpleDateFormat timezone parsing

I'm having a tough time parsing this date its the +0 at the end that is causing a problem, does anyone know whats wrong with my format string?? If I remove the +0 from the date string and the Z from the format string it works fine, unfortunately for my application that isn't an option.

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        SimpleDateFormat dateFormater = new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss zZ");
        try {
            Date d = dateFormater.parse("Sun, 04 Dec 2011 18:40:22 GMT+0");
            System.out.println(d.toLocaleString());
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Upvotes: 3

Views: 7640

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 338171

tl;dr

Use modern java.time classes.

OffsetDateTime
        .parse (
                "Sun, 04 Dec 2011 18:40:22 GMT+0" ,
                DateTimeFormatter
                        .ofPattern ( "EEE, dd MMM uuuu HH:mm:ss O" )
                        .withLocale ( Locale.of ( "en" , "US" ) )
        )
        .toString ( )

java.time in modern Java

Avoid the terribly-flawed legacy classes such as Calendar, Date, and SimpleDateFormat. Use only the java.time classes in Java 8+.

RFC 822 & 1123

Your input string "Sun, 04 Dec 2011 18:40:22 GMT+0" seems to be a screwy version of the outmoded standard format defined in RFC 822 & RFC 1123. Java comes with a predefined formatter RFC_1123_DATE_TIME for those standards, but your input varies a bit too much for that to work.

Tip: For storing and exchanging date-time values textually, use only ISO 8601 formats.

DateTimeFormatter

So we need to define our own pattern to match your input. Use DateTimeFormatter class.

DateTimeFormatter.ofPattern ( "EEE, dd MMM uuuu HH:mm:ss O" )

Locale

We need a Locale to specify the human language and cultural norms to be used in translating the name of the month and name of the day-of-week.

Locale locale = Locale.of ( "en" , "US" );
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "EEE, dd MMM uuuu HH:mm:ss O" ).withLocale ( locale );

OffsetDateTime

Parse as a OffsetDateTime, to represent a moment as seen with an offset from the temporal meridian of UTC.

In this case the offset is zero hours-minutes-seconds from UTC. Shown below as Z, pronounced “Zulu”.

String input = "Sun, 04 Dec 2011 18:40:22 GMT+0";
OffsetDateTime odt = OffsetDateTime.parse ( input , f );

Result:

odt.toString() = 2011-12-04T18:40:22Z

Upvotes: 1

ruakh
ruakh

Reputation: 183201

One approach is to use normal string-manipulation techniques to translate your string from a form that you're expecting to a form that SimpleDateFormat will understand. You haven't said exactly what range of time-zone formats are acceptable, but one possibility is something like this:

private static Date parse(String dateString) throws ParseException
{
    final SimpleDateFormat dateFormat =
        new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss Z");
    dateString = dateString.replaceAll("(GMT[+-])(\\d)$", "$1\\0$2");
    dateString = dateString.replaceAll("(GMT[+-]\\d\\d)$", "$1:00");
    return dateFormat.parse(dateString);
}

That would support GMT plus-or-minus a one-or-two-digit hour offset, in addition to still supporting anything already supported by SimpleDateFormat, such as EST or GMT+1030.

Alternatively, if you know it will always be GMT, then you can just set the time-zone on the formatter, and ignore the time-zone in the string:

private static Date parse(String dateString) throws ParseException
{
    final SimpleDateFormat dateFormat =
        new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss");
    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    return dateFormat.parse(dateString);
}

You can also split the difference. I notice that the time-zone format in your string matches what's expected by TimeZone.getTimeZone(). Is that intentional? If so, you can grab that time-zone format out of the string, pass it to dateFormat.setTimeZone beforehand, and then ignore it during actual parsing:

private static Date parse(final String dateString) throws ParseException
{
    final SimpleDateFormat dateFormat =
        new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss");
    if(dateString.indexOf("GMT") > 0)
        dateFormat.setTimeZone
        (
            TimeZone.getTimeZone
                (dateString.substring(dateString.indexOf("GMT")))
        );
    return dateFormat.parse(dateString);
}

Upvotes: 3

BalusC
BalusC

Reputation: 1108537

If the format is that consistent, you could append 0:00 to the date string.

String dateString = "Sun, 04 Dec 2011 18:40:22 GMT+0";
SimpleDateFormat sdf = new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss z", Locale.ENGLISH);
Date date = sdf.parse(dateString + "0:00");
System.out.println(date);

(note that I fixed the SimpleDateFormat construction to explicitly specify the locale which would be used to parse the day of week and month names, otherwise it may fail on platforms which does not use English as default locale; I also wonder if you don't actually need HH instead of kk, but that aside)

Upvotes: 2

Related Questions