1. Overview
Sometimes, we want to be able to specify the TimeZone used by an application. For a service running globally, this could mean that all servers are posting events using the same TimeZone, no matter their location.
We can achieve this in a few different ways. One approach involves the use of JVM arguments when we execute the application. The other approach is to make the change programmatically in our code at various points of the bootup lifecycle.
In this short tutorial, we’ll examine a few ways to set the default TimeZone of a Spring Boot Application. First, we’ll see how to achieve this via the command line, and then, we’ll dive into some options for doing this programmatically at startup in our Spring Boot code. Finally, we’ll examine the differences between these approaches.
2. Main Concepts
The default value for TimeZone is based on the operating system of the machine where the JVM is running. We can change this:
- By passing JVM arguments, using the user.timezone argument, in different ways depending on weather we run a task or a JAR
- Programmatically, using the bean lifecycle configuration options (during/before creation of beans) or even inside a class, during execution
Setting the default TimeZone in a Spring Boot Application affects different components, such as logs’ timestamps, schedulers, JPA/Hibernate timestamps, and more. This means our choice of where to do this depends on when we need it to take effect. For example, do we want it during some bean’s creation or after WebApplicationContext is initialized?
It’s important to be precise about when to set this value because it could lead to unwanted application behavior. For example, an alarm service might set an alarm before the time zone change takes effect, which might then cause the alarm to activate at the wrong time.
Another factor to think of, before we decide on which option to go with, is testability. Using JVM arguments is the easier option, but testing it might be trickier and more prone to bugs. There’s no guarantee that the unit tests will run with the same JVM arguments as the production deployment.
3. Setting Default TimeZone on bootRun Task
If we use the bootRun task to run the application, we can pass the default TimeZone using JVM arguments in the command line. In this case, the value we set is available from the very beginning of the execution:
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Duser.timezone=Europe/Athens"
4. Setting Default TimeZone on JAR Execution
Similar to running the bootRun task, we can pass the default TimeZone value in the command line when executing the JAR file. And again, the value we set is available from the very beginning of the execution:
java -Duser.timezone=Europe/Athens -jar spring-core-4-0.0.1-SNAPSHOT.jar
5. Setting Default TimeZone on Spring Boot Startup
Let’s see how to insert our time zone change in different parts of Spring’s startup process.
5.1. Main Method
First, suppose we set the value inside the main method. In this case, we have it available from the very early stages of the execution, even before detecting the Spring Profile:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00"));
SpringApplication.run(MainApplication.class, args);
}
}
Though this is the first step of the lifecycle, it doesn’t take advantage of the possibilities we have with our Spring configuration. We either have to hardcode the timezone or programmatically read it from something like an environment variable.
5.2. BeanFactoryPostProcessor
Second, BeanFactoryPostProcessor is a factory hook that we can use to modify the application context’s bean definitions. This way, we set the value just before any bean instantiation happens:
@Component
public class GlobalTimezoneBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00"));
}
}
5.3. PostConstruct
Last, we can use PostConstruct of our MainApplication class to set the default TimeZone value just after the initialization of WebApplicationContext is completed. At this point, we can inject the TimeZone value from our configuration properties:
@SpringBootApplication
public class MainApplication {
@Value("${application.timezone:UTC}")
private String applicationTimeZone;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
@PostConstruct
public void executeAfterMain() {
TimeZone.setDefault(TimeZone.getTimeZone(applicationTimeZone));
}
}
6. Conclusion
In this brief tutorial, we learned several ways of setting the default TimeZone in a Spring Boot Application. We discussed the impact that changing the default value might have, and based on these factors, we should be able to decide on the right approach for our use cases.
As always, all the source code is available over on GitHub.