0

I am trying to parse the date from the string and then save to DB, but sometimes when checking on DB the year value is wrong.

private SimpleDateFormat simpleDateFormatTimestampJPOS = new SimpleDateFormat("yyyyMMddHHmmssSSS");

private Date findTxnDate() throws ParseException
{
    Date date;
    String timeStamp = "20181115040613555";
    date = simpleDateFormatTimestampJPOS.parse(timeStamp);

    return date;
}

I don't know what happens, this is a problem from SimpleDateFormat, the server, or etc. can somebody help me?

1018 and what i expected is 2018

fyi java version on server

java version "1.8.0_111"

Java(TM) SE Runtime Environment (build 1.8.0_111-b14)

Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

Community
  • 1
  • 1
  • 1
    Without providing an actual example that doesn't do what you expect, it's hard to tell you what you are doing wrong... – assylias Nov 15 '18 at 09:54
  • 1
    This works for me. I cannot reproduce the problem. – Henry Nov 15 '18 at 09:56
  • Please elaborate more on 'sometimes'. Did anything change between runs? – Timothy T. Nov 15 '18 at 09:56
  • I think this has nothing to do with this date. It correctly parses the date srting. Something wrong with the code which pushes the date to database – Rakesh Nov 15 '18 at 09:57
  • 1
    I [cannot reproduce your error](https://rextester.com/SCAEB23157). – Tim Biegeleisen Nov 15 '18 at 09:58
  • assert date.getYear()==2018; works fine with your example. Can you send actual data? – lopushen Nov 15 '18 at 09:58
  • 1
    Sorry but this can't be solved without checking the whole system. To find where the error happen you should either step through the code with a debugger or add a print/log statement wherever something happens to this date value. You could also check the string value of timestamp before you pass it to parse and throw an exception if it starts with 1018 or isn't the right length. This could help you pin down where this problem comes from. – Wulf Nov 15 '18 at 09:59
  • thanks for the answer, 'sometimes' i mean is this method will be called when user click the button from the web. and then save to database `postgres` using jpa from 100 transactions parse the year wrong just 3-5 transaction random. what i see the month, day, minute, second, and millisecond is correct just the year save to db '1018' not '2018' – user3635651 Nov 15 '18 at 10:07
  • 2
    This is a perfect time to switch to the newer Java Time API provided in the `java.time` package. `Date` and `Calendar` classes are considered obsolete. – MC Emperor Nov 15 '18 at 10:18
  • 1
    i think the answer from @Alermikon make sense, because SimpleDateFormat is thread unsafe. SimpleDateFormat should be put in the method – user3635651 Nov 15 '18 at 10:20
  • 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. Nov 15 '18 at 10:21
  • See also [Why is Java's SimpleDateFormat not thread-safe?](https://stackoverflow.com/questions/6840803/why-is-javas-simpledateformat-not-thread-safe) – Ole V.V. Nov 15 '18 at 10:25
  • In particular when you are using Java 1.8 anyway, I see no reason why you should want to struggle with the outdated date-time classes. – Ole V.V. Nov 15 '18 at 14:51
  • 1
    FYI, the terribly troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Nov 17 '18 at 05:06

3 Answers3

2

SimpleDateFormat is thread unsafe, which can cause such error if the same instance is used in several threads.

Use new instance each time or some thread-safe alternative (FastDateFormat for example)

  • Even better, use [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). Not so much because its `DateTimeFormatter` *is* thread-safe. The whole API is just so much nicer to work with. – Ole V.V. Nov 16 '18 at 09:34
1

I don't know if it's the reason for your failure, but if it's for storing in the database, the datatype to store the time is TimeStamp and not Date.

The code fragment you presented is the way it used to be used in earlier Java versions and as is already mentioned by others, SimpleDateFormat is not thread safe.

May I also present you another way of achieving the same thing, using new and more programmer friendly classes from the java.time package which is thread safe:

LocalDateTime newDate = null;
String dateTime = "20181115040613555";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS", Locale.ENGLISH);
try {
    newDate = LocalDateTime.parse(dateTime, dtf);
} catch (DateTimeParseException e) {
    throw new InvalidInputException("Invalid date input.");
}

More info can be found here: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

Mathias G.
  • 4,093
  • 3
  • 34
  • 56
0

If you use your code in a multi-threaded scenario, you may get the wrong result because SimpleDateFormat is not a thread-safe class. If you use Java8+ use DateTimeFormatter instead. Here is a test code to verify SimpleDateFormat is not thread-safe class, hope helpful.

public class MultiThreadSimpleDateFormatClient {    
    public static void main(String[] args) {
        HandleDate handleDate = new HandleDate();
        Random random = new Random();
        Set<String> randomStrs = new HashSet<>();
        Thread thread1 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread2 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread3 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread4 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread5 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread6 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread7 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread8 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread9 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });
        Thread thread10 = new Thread(() -> {
            while (true) {
                int partOfYear = random.nextInt(10);
                handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
        thread6.start();
        thread7.start();
        thread8.start();
        thread9.start();
        thread10.start();
    }
}

public class HandleDate {
    // this is not thread safe
    private SimpleDateFormat simpleDateFormatTimestampJPOS = new SimpleDateFormat("yyyyMMddHHmmssSSS");

    public void verifyNotThreadSafe(String timeStamp) {
        try {
            // this is thread safe
            //SimpleDateFormat simpleDateFormatTimestampJPOS = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            Date date = simpleDateFormatTimestampJPOS.parse(timeStamp);
            LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
            String expectedYear = timeStamp.substring(0, 4);
            String actualYear = String.valueOf(localDateTime.getYear());
            if (!expectedYear.equals(actualYear)) {
                System.out.println("expected:" + expectedYear + ", but real:" + actualYear);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
xxy
  • 810
  • 7
  • 13