TimeZone.getTimeZone(String) returns the GMT zone when it cannot understand the identifier, and it does not throw an exception for an unknown identifier. A misspelled value such as America/Los_Angele therefore uses GMT instead of the intended zone.

Why is this an issue?

The String overload of TimeZone.getTimeZone does not reject unknown identifiers. If the argument is not a named ID and does not match custom GMT syntax, the method returns the GMT zone. The call succeeds, so the application can keep running with GMT instead of the intended zone.

Use a named identifier that Java recognizes, such as America/Los_Angeles, or a custom GMT offset that matches the documented syntax.

This rule accepts a named identifier when Java recognizes it, such as America/Los_Angeles. The recognized set can differ slightly across JDK and time-zone database versions. Legacy three-letter IDs such as PST remain recognized for compatibility and are not reported.

When no named ID matches, a custom GMT identifier can still be valid. The syntax is GMT followed by a sign and an offset: GMT+10 and GMT-8:00 are accepted. Hours must be between 0 and 23, and minutes must be between 00 and 59. From Java 19, seconds between 00 and 59 are also accepted, for example GMT+01:00:30. On earlier Java versions that form is not recognized and falls back to GMT. When sonar.java.source is not set, this rule accepts identifiers that include seconds.

Exceptions

This rule does not raise an issue when the argument is not a compile-time constant String, including values computed at runtime.

Code examples

Noncompliant code example

void namedIds(String userZone) {
  TimeZone.getTimeZone("America/Los_Angele"); // Noncompliant
}

Compliant solution

void namedIds(String userZone) {
  TimeZone.getTimeZone("America/Los_Angeles");
  TimeZone.getTimeZone("PST"); // legacy three-letter ID
  TimeZone.getTimeZone(userZone); // not a compile-time constant
}

Noncompliant code example

void spacedId() {
  TimeZone.getTimeZone("America/Los Angeles"); // Noncompliant
}

Compliant solution

void spacedId() {
  TimeZone.getTimeZone("America/Los_Angeles");
}

Noncompliant code example

void customOffsets() {
  TimeZone.getTimeZone("GMT+24"); // Noncompliant
  TimeZone.getTimeZone("GMT1"); // Noncompliant
  TimeZone.getTimeZone("UTC+0"); // Noncompliant: custom offsets must start with "GMT"
}

Compliant solution

void customOffsets() {
  TimeZone.getTimeZone("GMT+10");
  TimeZone.getTimeZone("GMT-8");
  TimeZone.getTimeZone("GMT+00:00");
}

Noncompliant code example

For projects targeting Java 18 and earlier:

void customOffsetWithSeconds() {
  TimeZone.getTimeZone("GMT+01:00:30"); // Noncompliant: seconds are not recognized before Java 19
}

Compliant solution

void customOffsetWithSeconds() {
  TimeZone.getTimeZone("GMT+01:00");
}

Noncompliant code example

For projects targeting Java 19 and later:

void customOffsetWithSeconds() {
  TimeZone.getTimeZone("GMT+01:00:60"); // Noncompliant: seconds must be between 00 and 59
}

Compliant solution

void customOffsetWithSeconds() {
  TimeZone.getTimeZone("GMT+01:00:30");
}

Resources

Documentation

Related rules