Android convert time parsing time error (even tried time in joda)

I am parsing multiple news feeds and each pubDate follows the same format:

Sun, 11 Jun 2017 18:18:23 +0000

Unfortunately, one channel doesn't work:

Sat, 10 Jun 2017 12:49:45 PM EST

I was trying to parse a date with no luck using androids java date and SimpleDateFormat

:

try {
    Calendar cal = Calendar.getInstance();
    TimeZone tz = cal.getTimeZone();
    SimpleDateFormat readDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
    readDate.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = readDate.parse(rssDateTime);
    SimpleDateFormat writeDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
    writeDate.setTimeZone(tz);
    parsedDate = writeDate.format(date);
} catch (ParseException e) {
    e.printStackTrace();
}

      

What throws and mistakes:

java.text.ParseException: Unmatched date: "Sat, Jun 3, 2017 19:53:09 EST" (at offset 26)

I also tried to do this using joda time:

DateTime dtUTC = null;
DateTimeZone timezone = DateTimeZone.getDefault();
DateTimeFormatter formatDT = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss Z");
DateTime dtRssDateTime = formatDT.parseDateTime(rssDateTime);
DateTime now = new DateTime();
DateTime nowUTC = new LocalDateTime(now).toDateTime(DateTimeZone.UTC);

long instant = now.getMillis();
long instantUTC = nowUTC.getMillis();
long offset = instantUTC - instant;
dtUTC = dtRssDateTime.withZoneRetainFields(timezone);
dtUTC = dtUTC.minusMillis((int) offset);
String returnTimeDate = "";
returnTimeDate = dtUTC.toString(formatDT);

      

What is causing the error:

Caused by: java.lang.IllegalArgumentException: Invalid format: "Sat, 10 Jun 2017 12:49:45 EST" malformed on "EST"

Has anyone encountered this before?

+3


source to share


1 answer


First of all, if you are starting a new project, I suggest you use the new API with date of use instead of joda-time (more on that below). Anyway, here's a solution for both.


Time in Yoda

The problem is that the pattern Z

is an offset (in formats such as +0000

or -0100

), but the string EST

is the short name of the timezone that is parsed by the pattern Z

(see the jodatime javadoc for more details).

So, you need a template with optional sections that can receive one or the other at the same time. You can do this with a class org.joda.time.format.DateTimeFormatterBuilder

.

First you need to create 2 instances org.joda.time.format.DateTimeParser

(one for Z

and one for Z

) and add them as additional parsers. Then you build org.joda.time.format.DateTimeFormatter

using the following code. Note that I also used java.util.Locale

to make sure it parses the day of the week and month names correctly (so you don't depend on the default locale, which may be different for each system / machine):

// offset parser (for "+0000")
DateTimeParser offsetParser = new DateTimeFormatterBuilder().appendPattern("Z").toParser();
// timezone name parser (for "EST")
DateTimeParser zoneNameParser = new DateTimeFormatterBuilder().appendPattern("z").toParser();
// formatter for both patterns
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // append common pattern
    .appendPattern("EEE, d MMM yyyy HH:mm:ss ")
    // optional offset
    .appendOptional(offsetParser)
    // optional timezone name
    .appendOptional(zoneNameParser)
    // create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config)
    .toFormatter().withLocale(Locale.ENGLISH)
    // make sure the offset "+0000" is parsed
    .withOffsetParsed();

// parse the strings
DateTime est = fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST");
DateTime utc = fmt.parseDateTime("Sun, 11 Jun 2017 18:18:23 +0000");
System.out.println(est);
System.out.println(utc);

      

The output will be:

2017-06-10T12: 49: 45.000-04: 00
2017-06-11T18: 18: 23.000Z

If they are not what you expected (or you are still getting errors), see the notes below.


Notes :

  • Note that it EST

    was printed as an offset date / time -0400

    . This is because it EST

    became the internal timezone America/New_York

    , which is now in daylight saving time, and its offset is -0400

    (I could figure it out by doing DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST"))

    . The problem is this: these three letter names are ambiguous, not standard , and joda-time just takes for these the "default". So, if you weren't expecting this timezone, and you don't want to rely on the default values, you can use a map with custom values, for example:

    // mapping EST to some other timezone (I know it wrong and Chicago is not EST, it just an example)
    Map<String, DateTimeZone> map = new LinkedHashMap<>();
    map.put("EST", DateTimeZone.forID("America/Chicago"));
    // parser for my custom map
    DateTimeParser customTimeZoneParser = new DateTimeFormatterBuilder().appendTimeZoneShortName(map).toParser();
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // append common pattern
        .appendPattern("EEE, d MMM yyyy HH:mm:ss ")
        // optional offset
        .appendOptional(offsetParser)
        // optional custom timezone name
        .appendOptional(customTimeZoneParser)
        // optional timezone name (accepts all others that are not in the map)
        .appendOptional(zoneNameParser)
        // create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config)
        .toFormatter().withLocale(Locale.ENGLISH)
        // make sure the offset "+0000" is parsed
        .withOffsetParsed();
    System.out.println(fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST"));
    
          

This will parse EST

as America/Chicago

(I know this is wrong and Chicago is not EST

, this is just an example of how you can change the default settings with a map) and the output will be:

2017-06-10T12: 49: 45.000-05: 00

If you get an error with the first code above, you can also use this by mapping EST

to the required timezone (depending on the jodatime and Java version you are using, the EST

default may not be displayed and throws an exception, so using a custom map allows avoid it).


New API with date time



As @Ole VV's comment said (and I didn't have time to write yesterday), joda-time is being replaced with the new Java Date and Time API , which is far superior compared to the old classes Date

andSimpleDateFormat

.

If you are using Java> = 8, the package is java.time

already part of the JDK. For Java <= 7, there is ThreeTen Backport . And for Android there is ThreeTenABP (more on how to use it here ).

If you are starting a new project, consider a new API instead of joda-time, because the joda website says: Note that Joda-Time is considered to be pretty much a "ready-made" project. No significant improvements are planned. If you are using Java SE 8 go to java.time (JSR-310).

Below is the code below. The only difference is the package names (in Java 8 - java.time

, and in ThreeTen Backport (or Android ThreeTenABP) - org.threeten.bp

), but the class and method names are the same.

The idea is very similar to jodatime with minor differences:

  • you can use optional section separators []

  • a set with custom timezone names is required (to map EST

    to some valid asymmetric timezone ) (since it EST

    does not map to any default)
  • a new class is used: ZonedDateTime

    which represents a date and time with a timezone (so it covers both of your cases).

As a reminder, these classes are in a package java.time

(or org.threeten.bp

whichever version of Java you are using, as described above):

// set with custom timezone names
Set<ZoneId> set = new HashSet<>();
// when parsing, ambiguous EST uses to New York
set.add(ZoneId.of("America/New_York"));

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // append pattern, with optional offset (delimited by [])
    .appendPattern("EEE, d MMM yyyy HH:mm:ss[ Z]")
    // append optional timezone name with custom set for EST
    .optionalStart().appendLiteral(" ").appendZoneText(TextStyle.SHORT, set).optionalEnd()
    // create formatter using English locale to make sure it parses weekdays and month names correctly
    .toFormatter(Locale.ENGLISH);

ZonedDateTime est = ZonedDateTime.parse("Sat, 10 Jun 2017 12:49:45 EST", fmt);
ZonedDateTime utc = ZonedDateTime.parse("Sun, 11 Jun 2017 18:18:23 +0000", fmt);
System.out.println(est); // 2017-06-10T12:49:45-04:00[America/New_York]
System.out.println(utc); // 2017-06-11T18:18:23Z

      

The output will be:

2017-06-10T12: 49: 45-04: 00 [America / New_York]
2017-06-11T18: 18: 23Z

Note that in the first case EST

it was set to America/New_York

(according to the custom set setting). appendZoneText

performs the trick by using values ​​in a custom set to resolve ambiguous cases.

And the second case was set to UTC since the offset +0000

.

If you want to convert the first object to UTC, it will be removed:

System.out.println(est.withZoneSameInstant(ZoneOffset.UTC)); // 2017-06-10T16:49:45Z

      

The output will be the date / time in New York converted to UTC:

2017-06-10T16: 49: 45Z

Instead ZoneOffset.UTC

, of course, you can use whatever timezone or offset you want (using the classes ZoneId

and ZoneOffset

), check the javadoc for more details).

+2


source







All Articles