1

Checkmarx complains that "the file utilizes "format" that is accessed by other concurrent functionality in a way that is not thread-safe, which may result in a Race Condition over this resource. It highlights the format method. How do we resolve this?

 String endDate =
                configProperties.getDateFormatter().format(Date.from(date.plusMonths(-1L * auditTimeMonthLimit).atStartOfDay()
                        .atZone(ZoneId.systemDefault())
                        .toInstant()));

Other part of code

 private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

 public SimpleDateFormat getDateFormatter() {
        return dateFormatter;
    }
Deepak
  • 47
  • 8
  • 2
    Please don't use SimpleDateFormat it is legacy, instead, look at modern `java.time` library – Youcef LAIDANI Nov 16 '22 at 14:54
  • could you add more details please, which class and how – Deepak Nov 16 '22 at 16:23
  • ['Z' is not the same as Z](https://stackoverflow.com/a/67953075/10819573). – Arvind Kumar Avinash Nov 17 '22 at 20:24
  • The way I read your code you have got a modern `LocalDate`, and then you are converting it to `LocalDateTime` to `ZonedDateTime` to `Instant` to an old-fashioned `Date` in order to format it? Sorry, that’s stupid. Convert the `ZonedDateTime` to an `OffsetDateTime` in UTC and either use its `toString` method or format it using a `DateTimeFormatter`. And obtain thread safety for free at the same time. – Ole V.V. Nov 18 '22 at 20:53
  • Does this answer your question? [Returning NumberFormatException form SimpleDateFormat in Java code](https://stackoverflow.com/questions/63500822/returning-numberformatexception-form-simpledateformat-in-java-code). Does [this](https://stackoverflow.com/questions/6840803/why-is-javas-simpledateformat-not-thread-safe)? – Ole V.V. Nov 18 '22 at 20:56

2 Answers2

1

SimpleDateFormat is not thread safe. This is a good explanation.

There is not a lot of code in the example, but having a final instance of SimpleDateFormat implies it may be used by multiple threads.

Maybe configProperties is a global singleton? It is hard to tell, but if that code is accessed by multiple threads (including as part of a web controller or other type of web endpoint handler) and that is a single instance for every thread then you have a problem.

One possible solution (maybe not ideal, but you can translate it to something that works for you):

public SimpleDateFormat getDateFormatter() {
        return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    }

There are likely better options for formatting than this, so maybe doing it entirely different would be better.

NathanL
  • 357
  • 2
  • 8
  • Thanks for the suggestion. Could you add more details for "doing it entirely different". which class or code we can use here – Deepak Nov 17 '22 at 07:59
  • @Deepak it would be near impossible to suggest an entirely different way without the ability to assess the full code. You'd need to explore available APIs that fit with how your code works or will need to refactor your code to fit how alternative APIs work. – NathanL Nov 17 '22 at 12:13
  • ['Z' is not the same as Z](https://stackoverflow.com/a/67953075/10819573). – Arvind Kumar Avinash Nov 17 '22 at 20:21
  • The entirely different way is in the other answer. It’s not using `SImpleDateFormat` and does not suffer from race conditions. – Ole V.V. Nov 19 '22 at 07:44
1

Legacy date-time API is error-prone e.g. java.util.Date and SimpleDateFormatter aren’t thread-safe, leading to potential concurrency issues for users. It is recommended to stop using them completely and switch to the modern date-time API.

The output you are trying to achieve is already the default format of Instant and therefore, you do not need to use a formatter.

However, probably you are not familiar with the modern date-time API and therefore, for the sake of your learning, I have also demonstrated the use of DateTimeFormatter.

Demo using java.time API:

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {

    public static void main(String[] args) {
        LocalDate date = LocalDate.now(); // Some date
        int auditTimeMonthLimit = 5; // Some value
        String endDate = date.minusMonths(auditTimeMonthLimit)
                .atStartOfDay(ZoneId.systemDefault())
                .toString();
        System.out.println(endDate);

        // In case you wanted the UTC date-time out of the local date
        endDate = date.minusMonths(auditTimeMonthLimit)
                .atStartOfDay(ZoneOffset.UTC)
                .toString();
        System.out.println(endDate);

        // In case you wanted the start date of the default time-zone to be converted
        // into the UTC date-time
        endDate = date.minusMonths(auditTimeMonthLimit)
                .atStartOfDay(ZoneId.systemDefault())
                .toInstant()
                .toString();
        System.out.println(endDate);

        // A custom format
        ZonedDateTime zdt = date.minusMonths(auditTimeMonthLimit)
                .atStartOfDay(ZoneId.systemDefault())
                .withZoneSameInstant(ZoneOffset.UTC);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
                "uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
        endDate = formatter.format(zdt);
        System.out.println(endDate);
    }
}

Output in my time-zone:

2022-06-17T00:00+01:00[Europe/London]
2022-06-17T00:00Z
2022-06-16T23:00:00Z
2022-06-16T23:00:00.000Z

Important points:

  1. I recommend you use LocalDate#minusMonths i.e. instead of using date.plusMonths(-1L * auditTimeMonthLimit), you should use date.minusMonths(auditTimeMonthLimit).
  2. 'Z' is not the same as Z.
  3. For your use case, I recommend you use LocalDate#atStartOfDay(ZoneId zone) instead of the non-parametrized LocalDate#atStartOfDay. This will make you

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

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110