2

In my program I am getting intermittently a ParseException while doing SimpleDateFormat.parse.

I have written one apache storm bolt, in that I am parsing the input date "2018-02-26 18:13:32 UTC".

This exception is not thrown for every input date. Also, I have printed the input date in error log. Visually there are no issues with input date format.

But I've got the ParseException for intermittent inputs.

I doubt is that because it is concurrent environment.

Following is the code snippet:

utcDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss 'UTC'");
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Milind Pardeshi
  • 143
  • 2
  • 3
  • 10
  • 1
    Can you add examples of both input that fails and input that doesn't? – ernest_k Feb 28 '18 at 12:30
  • 1
    I recommend you avoid the `SimpleDateFormat` class. It is not only long outdated, it is also notoriously troublesome. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Feb 28 '18 at 12:46
  • As long as you instantiate `SimpleDateFormat` every time you want to parse an input then it should be thread-safe (although not ideal in terms of performance). And your pattern using escaping "UTC" will only work if you also set the timezone of the newly created formatter to "UTC". But strictly avoid reusing the formatter in multi-thread-environment. – Meno Hochschild Mar 07 '18 at 15:29

2 Answers2

4

I doubt is that because it is concurrent environment.

Actually, that's the most probable cause, because SimpleDateFormat is not thread safe. Check here an analysis of the problem and how to fix it: https://www.javaspecialists.eu/archive/Issue172.html

Apart from that, "UTC" is an important information (it indicates that, well, the date is in UTC), so you can't treat it as a literal (inside quotes). The formatter you created is ignoring that the date is in UTC (because inside quotes it's treated as "some text", not as "it's in UTC"), so it's actually using the JVM default timezone (which can't necessarily be UTC).

To correctly parse UTC, you must use the z pattern:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
Date date = sdf.parse("2018-02-26 18:13:32 UTC");

But if you're using Java 8 or higher, just use the new date API:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // parse date and time
    .appendPattern("yyyy-MM-dd HH:mm:ss ")
    // parse UTC
    .appendOffset("+HH:MM", "UTC")
    // create the formatter
    .toFormatter();
OffsetDateTime odt = OffsetDateTime.parse("2018-02-26 18:13:32 UTC", fmt);

It seems more complicated at first, but this new API provides lots of different date-time types and much more options to parse and format them.

And more important: it's thread safe.

UPDATE:

As suggested in the comments, you can also do:

DateTimeFormatter fmt  = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz");
ZonedDateTime zdt = ZonedDateTime.parse("2018-02-26 18:13:32 UTC", fmt);

If you still need to work with java.util.Date, it's easy to convert:

Date javaUtilDate = Date.from(zdt.toInstant());

The OffsetDateTime class also has a toInstant() method, so both can be converted to Date.

zuts
  • 47
  • 3
  • Depending on the requirements `DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss zzz")` may be all the OP needs (will work for parsing into a `ZonedDateTime`, you can always convert to `OffsetDateTime` if you want). – Ole V.V. Feb 28 '18 at 13:01
  • @OleV.V. That's interesting, why **zzz** doesn't work with `OffsetDateTime`? I think I need to study more... – zuts Feb 28 '18 at 13:07
  • I discovered once I had to parse a string that contained both an offset and a time zone name. Parsing into an `OffsetDateTime` would ignore the time zone, and parsing into a `ZonedDateTime` would ignore the offset. And `zzz` counts as zone name. – Ole V.V. Feb 28 '18 at 13:16
  • @OleV.V. I thought that "UTC" was a synonym for "Z" (zero offset), not a zone name. But it seems I was wrong... – zuts Feb 28 '18 at 13:38
  • All I say is that it *can* be parsed as a zone name. Whether that makes sense or not? Your judgement is as good as mine. – Ole V.V. Feb 28 '18 at 14:02
  • Thanks I have updated my code to DateTimeFormatter utcDateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz"); DateTimeFormatter Instantformatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); try { String instantDate = Instantformatter.format(utcDateFormatter.parse(inputDate)); – Milind Pardeshi Apr 03 '18 at 10:07
1

SimpleDateFormat is not threadsafe and you really can get a ParseException in the concurrent environment.

See here for details.

For Java 8 you can use DateTimeFormatter which is threadsafe.

zuts
  • 47
  • 3
Mike Adamenko
  • 2,944
  • 1
  • 15
  • 28
  • 1
    For Java 6 & 7, use the *ThreeTen-Backport* library, a back-port Of much of the *java.time* functionality. The legacy date-time classes are a wretched mess and should be avoided. – Basil Bourque Feb 28 '18 at 15:46
  • @MikeAdamenko I believe Joda-Time is a step forward, but it’s a finished project with no major development these days. So if you’re fine with an external dependency, why not go for the future-proof replacement for Joda-Time, java.time through the mentioned backport? The home page you are linking to says “Users are now asked to migrate to `java.time` (JSR-310).” – Ole V.V. Mar 01 '18 at 12:01
  • 1
    @MikeAdamenko The reason to use *ThreeTen-Backport* rather than *Joda-Time* is the close parallel in API between *java.time* and *ThreeTen-Backport*. When you do eventually move to Java 8 or later, you’ll need do little more to your codebase than change your `import` statements when you decide to phase out use of the back-port. – Basil Bourque Mar 01 '18 at 17:40
  • @BasilBourque yes, it make sense, agree with you – Mike Adamenko Mar 02 '18 at 13:04