Search code examples
javasonarqube

sonarqube error on static Calendar variable


I have the following variable declared :

private static Calendar calendar = Calendar.getInstance();

I am using that variable 'calendar' in a static method like below :

myStaticMethod(String reqDate){
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    Date some_date;
    long seconds = 0;
    int value = 10;
    try {
        some_date = df.parse(reqDate);
        calendar.setTime(some_date);
        calendar.add(Calendar.DATE, value);
        Date effValueDate = calendar.getTime();
        seconds = (effValueDate .getTime() - System.currentTimeMillis()) / 1000;
    } catch (ParseException e) {
        //---Do---something----
    }
}

I am getting the following sonar error :

Make "calendar" an instance variable.
Not all classes in the standard Java library were written to be thread-safe. 
Using them in a multi-threaded manner is highly likely to cause data problems or exceptions at runtime.    
This rule raises an issue when an instance of Calendar, DateFormat, 
javax.xml.xpath.XPath, or javax.xml.validation.SchemaFactory is marked static.

This is my solution, but I am not sure if the solution quality is good enough.

myStaticMethod(String reqDate) {
    Calendar calendar = Calendar.getInstance();
    // the do the next processing
}

So how can I fix the error... can I use a local Calendar variable in this case instead of class variable or is there any other smart way to address this.


Solution

  • The sonar warning is caused because the calendar object could be updated from multiple threads. Either, as you said you could declare the calendar as local to the method.

    OR 1) Make a synchronized block in your code. (using local calendar is better)

    Date effValueDate ;
    synchronized(calendar) {
        calendar.setTime(some_date);
        calendar.add(Calendar.DATE, value);
        effValueDate = calendar.getTime();
    }
    
    1. RECOMMENDED: Use the new java API classes for date and time (java.time).
    // or use DateTimeFormatter.ISO_LOCAL_DATE
    DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
    LocalDateTime date = LocalDate.parse(reqDate, pattern).plusDays(value).atStartOfDay();
    Duration duration= Duration.between(date, LocalDateTime.now());
    long seconds = duration.getSeconds();           
    

    Read more about the new API